From ede865cd1eac631214d2d8dd1b315debdd9917f6 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:18:41 +0100 Subject: [PATCH 001/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/00-bootstrap/README.md | 314 +++++++++++++++++++++++ fast/stages/00-bootstrap/automation.tf | 107 ++++++++ fast/stages/00-bootstrap/billing.tf | 112 ++++++++ fast/stages/00-bootstrap/diagram.png | Bin 0 -> 42583 bytes fast/stages/00-bootstrap/log-export.tf | 73 ++++++ fast/stages/00-bootstrap/main.tf | 32 +++ fast/stages/00-bootstrap/organization.tf | 205 +++++++++++++++ fast/stages/00-bootstrap/outputs.tf | 113 ++++++++ fast/stages/00-bootstrap/variables.tf | 100 ++++++++ 9 files changed, 1056 insertions(+) create mode 100644 fast/stages/00-bootstrap/README.md create mode 100644 fast/stages/00-bootstrap/automation.tf create mode 100644 fast/stages/00-bootstrap/billing.tf create mode 100644 fast/stages/00-bootstrap/diagram.png create mode 100644 fast/stages/00-bootstrap/log-export.tf create mode 100644 fast/stages/00-bootstrap/main.tf create mode 100644 fast/stages/00-bootstrap/organization.tf create mode 100644 fast/stages/00-bootstrap/outputs.tf create mode 100644 fast/stages/00-bootstrap/variables.tf diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md new file mode 100644 index 00000000..15d0d500 --- /dev/null +++ b/fast/stages/00-bootstrap/README.md @@ -0,0 +1,314 @@ +# Organization bootstrap + +The main purpose of this stage is to enable critical organization-level functionality that depends on broad administrative permissions, and to prepare the prerequisites needed to enable automation in this and future stages. + +It is intentionally simple, to minimize usage of administrative-level permissions and enable simple auditing and troubleshooting, and only deals with three sets of resources: + +- project, service accounts, and GCS buckets for automation +- projects, BQ datasets, and sinks for audit log and billing exports +- IAM bindings on the organization + +The following diagram can be used as a simple high level reference for the following sections, which describe the stage and its possible customizations in detail. + +![Organization-level diagram](diagram.png) + +## Design overview and choices + +As mentioned above, this stage only does the bare minimum required to bootstrap automation, and to ensure that base audit and billing exports are in place from the start to provide some measure of accountability, even before the security configurations are applied in a later stage. + +It also sets up organization-level IAM bindings so the Organization Administrator role is only used here, trading off some design freedom for ease of auditing and troubleshooting, and reducing the risk of costly security mistakes down the line. The only exception to this rule is for the [Resource Management stage](../01-resman) service account, and is described below. + +### User groups + +User groups are particularly important, not only here but throughout the whole automation process, as they provide a stable frame of reference that allows decoupling the final set of permissions for each group, from the stage where entities and resources are created and their IAM bindings defined. As an example, the final set of roles for the networking group is contributed by this stage at the organization level (XPN Admin, Cloud Asset Viewer, etc.), and by the Resource Management stage at the folder level. + +To simplify adoption, we have standardized the initial set of groups on those outlined in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist), as they provide a comprehensive and flexible starting point that can suit most users. Adding new groups, or deviating from the initial setup is of course possible and reasonably simple, and it's briefly outlined in the customization section below. + +### Organization-level IAM + +The service account used in the [Resource Management stage](../01-resman) needs to be able to grant specific roles at the organizational level (`roles/billing.user`, `roles/compute.xpnAdmin`, etc.), to enable specific functionality for subsequent stages that deal with network or security resources, or billing-related activities. + +In order to be able to assign those roles without having the full authority of the Organization Admin role, this stage defines a custom role that only allows setting IAM policies on the organization, and grants it via a [delegated role grant](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) that only allows it to be used to grant a limited subset of roles. + +In this way, the Resource Management service account can effectively act as an Organization Admin, but only to grant the roles it effectively needs to control. + +One consequence of the above setup, is the need to configure IAM bindings as non-authoritative for the roles included in the IAM condition, since those same roles are effectively under control of two stages: this one, and Resource Management. Using authoritative bindings for these roles instead of non-authoritative ones, would generate potential conflicts where each stage tries to overwrite and negate the bindings applied by the other at each `apply` cycle. + +### Automation project and resources + +One other design choice worth mentioning here, is the use of a single automation project for all foundational stages, trading off some complexity on the API side (single source for usage quota, multiple service activation) for increased flexibility and simpler operations, while still effectively providing the same degree of separation via resource-level IAM. + +### Billing account + +We support three use cases in regards to billing: + +- the billing account is part of this same organization, IAM bindings will be set at the organization level +- the billing account is part of a different organization, billing IAM bindings will be set at the organization level in the billing account owning organization +- the billing account is not considered part of an organization (even though it might be), billing IAM bindings are set on the billing account itself + +For same-organization billing, we configure a custom organization role that can set IAM bindings, via a delegated role grant to limit its scope to the relevant roles. + +For details on how to configure the different billing account modes, refer to the [How to run this stage](#how-to-run-this-stage) section below. + +### Naming + +We are intentionally not supporting random prefix/suffixes for names, as that is an antipattern that is typically only used in development, and does not map to the actual production usage we see at customers, who always adopt a fixed naming convention. + +What is implemented here is a fairly common convention, composed of tokens ordered by relative importance: + +- a static prefix (e.g. `myco` or `myco-gcp`) +- an environment identifier (e.g. `prod`) +- a team/owner identifier (e.g. `sec` for Security) +- a context identifier (e.g. `core` or `kms`) +- an arbitrary identifier used to distinguish similar resources (e.g. `0`, `1`) + +Tokens are joined by a `-` character, which makes it easy to visually separate the individual tokens, and allows to programmatically split them in billing exports to derive initial high-level groupings for cost attribution. + +The convention is used in its full form only for specific resources which have globally unique names (projects, GCS buckets), other resources adopt a shorter version for legibility, as the full context can always be derived from their project. + +The [Customizations](#names-and-naming-convention) section on names below explains how to configure tokens, or how to implement a different naming convention. + +## How to run this stage + +This stage has very simple initial requirements, as it is designed to work on newly created GCP organizations. Four steps are needed to bring up this stage: + +- an Organization Admin self-assigns the required roles listed below +- the same administrator runs the first `init/apply` sequence passing a special variable to `apply` +- the providers configuration file is derived from the Terraform output or linked from the generated file +- a second `init` is run to migrate state, and from then on the stage is run via impersonation + +### Prerequisites + +The roles that the Organization Admin used in the first `apply` needs to self-grant are: + +- Billing Account Administrator (`roles/billing.admin`) + either on the org (if the billing account has been moved to the org) or on the billing account +- Logging Admin (`roles/logging.admin`) +- Organization Role Administrator (`roles/iam.organizationRoleAdmin`) +- Organization Administrator (`roles/resourcemanager.organizationAdmin`) +- Project Creator (`roles/resourcemanager.projectCreator`) + +To quickly self-grant the above roles, run the following code snippet as the initial Organization Admin: + +```bash +export BOOTSTRAP_ORG_ID=123456 +export BOOTSTRAP_USER=$(gcloud config list --format 'value(core.account)') +export BOOTSTRAP_ROLES=(roles/billing.admin roles/logging.admin roles/iam.organizationRoleAdmin roles/resourcemanager.projectCreator) +for role in $BOOTSTRAP_ROLES; do + gcloud organizations add-iam-policy-binding $BOOTSTRAP_ORG_ID \ + --member user:$BOOTSTRAP_USER --role $role +done +``` + +#### Billing account in a different organization + +If you are using a billing account belonging to a different organization (e.g. in multiple organization setups), some initial configurations are needed to ensure the identities running this stage can assign billing-related roles. + +If the billing organization is managed by another version of this stage, we leverage the `organizationIamAdmin` created there, to allow restricted granting of billing roles at the organization level. + +If that's not the case, an equivalent role needs to exist, or the predefined `resourcemanager.organizationAdmin` role can be used if it's not managed authoritatively. The role name then needs to be manually changed in the `billing.tf` file, in the `google_organization_iam_binding` resource. + +The identity applying this stage for the first time also needs two roles in billing organization, they can be removed after the first `apply` completes successfully: + +```bash +export BILLING_ORG_ID=789012 +export BILLING_ROLES=(roles/billing.admin roles/resourcemanager.organizationAdmin) +for role in $BILLING_ROLES; do + gcloud organizations add-iam-policy-binding $BILLING_ORG_ID \ + --member user:$BOOTSTRAP_USER --role $role +done +``` + +#### Standalone billing account + +If you are using a standalone billing account, the identity applying this stage for the first time needs to be a billing account administrator: + +```bash +export BILLING_ACCOUNT_ID=ABCD-01234-ABCD +gcloud beta billing accounts add-iam-policy-binding $BILLING_ACCOUNT \ + --member user:$BOOTSTRAP_USER --role roles/billing.admin +``` + +#### Groups + +Before the first run the following IAM groups must exist to allow IAM bindings to be created (actual names are flexible, see the [Customization](#customizations) section): + +- gcp-billing-admins +- gcp-devops +- gcp-network-admins +- gcp-organization-admins +- gcp-security-admins +- gcp-support + +#### Configure variables + +Then make sure you have configured the correct values for the following variables, by editing the defaults in `variables.tf` or providing a `terraform.tfvars` file (preferred): + +- `billing_account` + an object containing the id of your billing account, derived from the Cloud Console UI or by running `gcloud beta billing accounts list`, and the id of the organization owning it, or `null` to use the billing account in isolation +- `groups` + the name mappings for your groups, if you're following the default convention you can leave this to the provided default +- `organization.id`, `organization.domain`, `organization.customer_id` + the id, domain and customer id of your organization, derived from the Cloud Console UI or by running `gcloud organizations list` +- `prefix` + the fixed prefix used in your naming convention + +### Output files and cross-stage variables + +At any time during the life of this stage, you can configure it to automatically generate providers configuration and variable files, to simplify exchanging inputs and outputs between stages and avoid having to edit files manually. + +This is disabled by default, to enable the mechanism just set the `outputs_location` variable to a valid path on a local filesystem, e.g. + +```hcl +outputs_location = "../../configs" +``` + +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 simply link these files in the relevant stages, instead of having to manually transfer outputs from one stage, to Terraform variables in another. + +This is the outline of the output files generated by this stage: + +```bash +[path specified in outputs_location] +├── 00-bootstrap +│   ├── providers.tf +├── 01-resman +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 02-networking +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 02-security +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-gke-multitenant-dev +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-gke-multitenant-prod +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-project-factory-dev +│   └── terraform-bootstrap.auto.tfvars.json +├── 03-project-factory-prod +│   └── terraform-bootstrap.auto.tfvars.json +``` + +### 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: + +```bash +terraform init +terraform apply \ + -var bootstrap_user=$(gcloud config list --format 'value(core.account)') +``` + +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/* ./ +# or from outputs if not using output files +terraform output -json providers | jq -r '.["00-bootstrap"]' \ + | tee providers.tf +# migrate state to GCS bucket configured in providers file +terraform init -migrate-state +``` + +## Customizations + +Most of the variables (e.g. `billing_account` and `organization`) are only used to input actual values and should be self-explanatory. The only meaningful customizations that apply here are groups, and IAM roles. + +### Group names + +As we mentioned above, groups reflect the convention used in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist), with an added level of indirection: the `groups` variable maps logical names to actual names, so that you don't need to delve into the code if your group names do not comply with the checklist convention. + +For example, if your network admins team is called `net-rockstars@example.com`, simply set that name in the variable, minus the domain which is interpolated internally with the organization domain: + +```hcl +variable "groups" { + description = "Group names to grant organization-level permissions." + type = map(string) + default = { + gcp-network-admins = "net-rockstars" + # [...] + } +} +``` + +If your groups layout differs substantially from the checklist, define all relevant groups in the `groups` variable, then rearrange IAM roles in the code to match your setup. + +### IAM + +One other area where we directly support customizations is IAM. The code here, as in all other stages, follows a simple pattern derived from best practices: + +- operational roles for humans are assigned to groups +- any other principal is a service account + +In code, the distinction above reflects on how IAM bindings are specified in the underlying module variables: + +- group roles "for humans" always use `iam_groups` variables +- service account roles always use `iam` variables + +This makes it easy to tweak user roles by adding mappings to the `iam_groups` variables of the relevant resources, without having to understand and deal with the details of service account roles. + +In those cases where roles need to be assigned to end-user service accounts (e.g. an application or pipeline service account), we offer a stage-level `iam` variable that allows pinpointing individual role/members pairs, without having to touch the code internals, to avoid the risk of breaking a critical role for a robot account. The variable can also be used to assign roles to specific users or to groups external to the organization, e.g. to support external suppliers. + +The one exception to this convention is for roles which are part of the delegated grant condition described above, and which can then be assigned from other stages. In this case, use the `iam_additive` variable as they are implemented with non-authoritative resources. Using non-authoritative bindings ensure that re-executing this stage will not override any bindings set in downstream stages. + +### Names and naming convention + +Configuring the individual tokens for the naming convention described above, has varying degrees of complexity: + +- the static prefix can be set via the `prefix` variable once +- the environment identifier is set to `prod` as resources here influence production and are considered as such, and can be changed in `main.tf` locals + +All other tokens are set directly in resource names, as providing abstractions to manage them would have added too much complexity to the code, making it less readable and more fragile. + +If a different convention is needed, identify names via search/grep (e.g. with `^\s+name\s+=\s+"`) and change them in an editor: it should take a couple of minutes at most, as there's just a handful of modules and resources to change. + +Names used in internal references (e.g. `module.foo-prod.id`) are only used by Terraform and do not influence resource naming, so they are best left untouched to avoid having to debug complex errors. + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [automation.tf](./automation.tf) | Automation project and resources. | gcs · iam-service-account · project | | +| [billing.tf](./billing.tf) | Billing export project and dataset. | bigquery-dataset · organization · project | google_billing_account_iam_member · google_organization_iam_binding | +| [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [organization.tf](./organization.tf) | Organization-level IAM and org policies. | organization | google_organization_iam_binding | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | | +| organization | Organization details. | object({…}) | ✓ | | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | | +| bootstrap_user | Email of the nominal user running this stage for the first time. | string | | null | | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | | +| iam | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| iam_additive | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | +| log_sinks | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| billing_dataset | BigQuery dataset prepared for billing export. | | | +| project_ids | Projects created by this stage. | | | +| providers | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| tfvars | Terraform variable files for the following stages. | ✓ | | + + + + + + + diff --git a/fast/stages/00-bootstrap/automation.tf b/fast/stages/00-bootstrap/automation.tf new file mode 100644 index 00000000..8498df00 --- /dev/null +++ b/fast/stages/00-bootstrap/automation.tf @@ -0,0 +1,107 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Automation project and resources. + +module "automation-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + billing_account = var.billing_account.id + name = "iac-core-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + # human (groups) IAM bindings + group_iam = { + (local.groups.gcp-devops) = [ + "roles/iam.serviceAccountAdmin", + "roles/iam.serviceAccountTokenCreator", + ] + (local.groups.gcp-organization-admins) = [ + "roles/iam.serviceAccountTokenCreator", + ] + } + # machine (service accounts) IAM bindings + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + "roles/iam.serviceAccountAdmin" = [ + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + "roles/storage.admin" = [ + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + } + services = [ + "accesscontextmanager.googleapis.com", + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "billingbudgets.googleapis.com", + "cloudbilling.googleapis.com", + "cloudkms.googleapis.com", + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "essentialcontacts.googleapis.com", + "iam.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "serviceusage.googleapis.com", + "stackdriver.googleapis.com", + "storage-component.googleapis.com", + "storage.googleapis.com", + ] +} + +# this stage's bucket and service account + +module "automation-tf-bootstrap-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "iac-core-bootstrap-0" + prefix = local.prefix + versioning = true + depends_on = [module.organization] +} + +module "automation-tf-bootstrap-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "bootstrap-0" + description = "Terraform organization bootstrap service account." + prefix = local.prefix +} + +# resource hierarchy stage's bucket and service account + +module "automation-tf-resman-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "iac-core-resman-0" + prefix = local.prefix + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.automation-tf-resman-sa.iam_email] + } + depends_on = [module.organization] +} + +module "automation-tf-resman-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "resman-0" + description = "Terraform organization bootstrap service account." + prefix = local.prefix +} diff --git a/fast/stages/00-bootstrap/billing.tf b/fast/stages/00-bootstrap/billing.tf new file mode 100644 index 00000000..7d5bcf09 --- /dev/null +++ b/fast/stages/00-bootstrap/billing.tf @@ -0,0 +1,112 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Billing export project and dataset. + +locals { + # used here for convenience, in organization.tf members are explicit + billing_ext_admins = [ + local.groups_iam.gcp-organization-admins, + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] +} + +# billing account in same org (IAM is in the organization.tf file) + +moved { + from = module.billing-export-project + to = module.billing-export-project.0 +} + +module "billing-export-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + count = local.billing_org ? 1 : 0 + billing_account = var.billing_account.id + name = "billing-export-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + } + services = [ + # "cloudresourcemanager.googleapis.com", + # "iam.googleapis.com", + # "serviceusage.googleapis.com", + "bigquery.googleapis.com", + "bigquerydatatransfer.googleapis.com", + "storage.googleapis.com" + ] +} + +moved { + from = module.billing-export-dataset + to = module.billing-export-dataset.0 +} + +module "billing-export-dataset" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + count = local.billing_org ? 1 : 0 + project_id = module.billing-export-project.0.project_id + id = "billing_export" + friendly_name = "Billing export." +} + +# billing account in a different org + +module "billing-organization-ext" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + count = local.billing_org_ext ? 1 : 0 + organization_id = "organizations/${var.billing_account.organization_id}" + iam_additive = { + "roles/billing.admin" = local.billing_ext_admins + } +} + + +resource "google_organization_iam_binding" "billing_org_ext_admin_delegated" { + # refer to organization.tf for the explanation of how this binding works + count = local.billing_org_ext ? 1 : 0 + org_id = var.billing_account.organization_id + # if the billing org does not have our custom role, user the predefined one + # role = "roles/resourcemanager.organizationAdmin" + role = "organizations/${var.billing_account.organization_id}/roles/organizationIamAdmin" + members = [module.automation-tf-resman-sa.iam_email] + condition { + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/billing.costsManager", + "roles/billing.user", + ] + )) + ) + } + depends_on = [module.billing-organization-ext] +} + +# standalone billing account + +resource "google_billing_account_iam_member" "billing_ext_admin" { + for_each = toset( + local.billing_ext ? local.billing_ext_admins : [] + ) + billing_account_id = var.billing_account.id + role = "roles/billing.admin" + member = each.key +} diff --git a/fast/stages/00-bootstrap/diagram.png b/fast/stages/00-bootstrap/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..932dfa8d869f3fb88a155c4d5a54e594bc4a1c4c GIT binary patch literal 42583 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�p0B0Sg}RnZKNt5SPJf~{W5LM8Z)&U; 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 H=h*)Mp1sHv literal 0 HcmV?d00001 diff --git a/fast/stages/00-bootstrap/log-export.tf b/fast/stages/00-bootstrap/log-export.tf new file mode 100644 index 00000000..f6fb51a5 --- /dev/null +++ b/fast/stages/00-bootstrap/log-export.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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Audit log project and sink. + +locals { + log_types = toset([for k, v in var.log_sinks : v.type]) +} + +module "log-export-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "audit-logs-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + billing_account = var.billing_account.id + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + } + services = [ + # "cloudresourcemanager.googleapis.com", + # "iam.googleapis.com", + # "serviceusage.googleapis.com", + "bigquery.googleapis.com", + "storage.googleapis.com", + "stackdriver.googleapis.com" + ] +} + +# one log export per type, with conditionals to skip those not needed + +module "log-export-dataset" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + count = contains(local.log_types, "bigquery") ? 1 : 0 + project_id = module.log-export-project.project_id + id = "audit_export" + friendly_name = "Audit logs export." +} + +module "log-export-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + count = contains(local.log_types, "storage") ? 1 : 0 + project_id = module.log-export-project.project_id + name = "audit-logs-0" + prefix = local.prefix +} + +module "log-export-logbucket" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/logging-bucket?ref=v12.0.0" + count = contains(local.log_types, "logging") ? 1 : 0 + parent_type = "project" + parent = module.log-export-project.project_id + id = "audit-logs-0" +} + +module "log-export-pubsub" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/pubsub?ref=v12.0.0" + for_each = toset([for k, v in var.log_sinks : k if v == "pubsub"]) + project_id = module.log-export-project.project_id + name = "audit-logs-${each.key}" +} diff --git a/fast/stages/00-bootstrap/main.tf b/fast/stages/00-bootstrap/main.tf new file mode 100644 index 00000000..49fa9140 --- /dev/null +++ b/fast/stages/00-bootstrap/main.tf @@ -0,0 +1,32 @@ +/** + * 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 { + groups = { + for k, v in var.groups : + k => "${v}@${var.organization.domain}" + } + groups_iam = { + for k, v in local.groups : + k => "group:${v}" + } + # convenience flags that express where billing account resides + billing_ext = var.billing_account.organization_id == null + billing_org = var.billing_account.organization_id == var.organization.id + billing_org_ext = !local.billing_ext && !local.billing_org + # naming: environment used in most resource names + prefix = join("-", compact([var.prefix, "prod"])) +} diff --git a/fast/stages/00-bootstrap/organization.tf b/fast/stages/00-bootstrap/organization.tf new file mode 100644 index 00000000..3bf04b73 --- /dev/null +++ b/fast/stages/00-bootstrap/organization.tf @@ -0,0 +1,205 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization-level IAM and org policies. + +locals { + # organization authoritative IAM bindings, in an easy to edit format before + # they are combined with var.iam a bit further in locals + _iam = { + "roles/browser" = [ + "domain:${var.organization.domain}" + ] + "roles/logging.admin" = [ + module.automation-tf-bootstrap-sa.iam_email + ] + "roles/owner" = local._iam_bootstrap_user + "roles/resourcemanager.folderAdmin" = [ + module.automation-tf-resman-sa.iam_email + ] + "roles/resourcemanager.organizationAdmin" = concat( + [module.automation-tf-bootstrap-sa.iam_email], + local._iam_bootstrap_user + ) + "roles/resourcemanager.organizationViewer" = [ + "domain:${var.organization.domain}" + ] + } + # organization additive IAM bindings, in an easy to edit format before + # they are combined with var.iam_additive a bit further in locals + _iam_additive = merge( + { + "roles/accesscontextmanager.policyAdmin" = [ + local.groups_iam.gcp-security-admins + ] + "roles/compute.orgFirewallPolicyAdmin" = [ + local.groups_iam.gcp-network-admins + ] + "roles/compute.xpnAdmin" = [ + local.groups_iam.gcp-network-admins + ] + # use additive to support cross-org roles for billing + "roles/iam.organizationRoleAdmin" = [ + local.groups_iam.gcp-security-admins, + module.automation-tf-bootstrap-sa.iam_email + ] + "roles/orgpolicy.policyAdmin" = [ + module.automation-tf-resman-sa.iam_email, + local.groups_iam.gcp-security-admins + ] + }, + local.billing_org ? { + "roles/billing.admin" = [ + local.groups_iam.gcp-organization-admins, + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + } : {} + ) + _iam_bootstrap_user = ( + var.bootstrap_user == null ? [] : ["user:${var.bootstrap_user}"] + ) + _log_sink_destinations = { + bigquery = try(module.log-export-dataset.0.id, null), + logging = try(module.log-export-logbucket.0.id, null), + storage = try(module.log-export-gcs.0.name, null) + } + iam = { + for role in local.iam_roles : role => distinct(concat( + try(sort(local._iam[role]), []), + try(sort(var.iam[role]), []) + )) + } + iam_additive = { + for role in local.iam_roles_additive : role => distinct(concat( + try(sort(local._iam_additive[role]), []), + try(sort(var.iam_additive[role]), []) + )) + } + iam_roles = distinct(concat( + keys(local._iam), keys(var.iam) + )) + iam_roles_additive = distinct(concat( + keys(local._iam_additive), keys(var.iam_additive) + )) + log_sink_destinations = { + for k, v in var.log_sinks : k => ( + v.type == "pubsub" + ? module.log-export-pubsub[k] + : local._log_sink_destinations[v.type] + ) + } +} + +module "organization" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + organization_id = "organizations/${var.organization.id}" + # human (groups) IAM bindings + group_iam = { + (local.groups.gcp-organization-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.admin", + "roles/compute.osAdminLogin", + "roles/compute.osLoginExternalUser", + "roles/owner", + "roles/resourcemanager.folderAdmin", + "roles/resourcemanager.organizationAdmin", + "roles/resourcemanager.projectCreator", + ], + (local.groups.gcp-network-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.techSupportEditor", + ] + (local.groups.gcp-security-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.techSupportEditor", + "roles/iam.securityReviewer", + "roles/logging.admin", + "roles/securitycenter.admin", + ], + (local.groups.gcp-support) = [ + "roles/cloudsupport.techSupportEditor", + "roles/logging.viewer", + "roles/monitoring.viewer", + ] + } + # machine (service accounts) IAM bindings + iam = local.iam + # additive bindings, used for roles co-managed by different stages + iam_additive = local.iam_additive + custom_roles = { + # this is needed for use in additive IAM bindings, to avoid conflicts + "organizationIamAdmin" = [ + "resourcemanager.organizations.get", + "resourcemanager.organizations.getIamPolicy", + "resourcemanager.organizations.setIamPolicy" + ] + "xpnServiceAdmin" = [ + "compute.globalOperations.get", + "compute.organizations.disableXpnResource", + "compute.organizations.enableXpnResource", + "compute.projects.get", + "compute.subnetworks.getIamPolicy", + "compute.subnetworks.setIamPolicy", + "dns.networks.bindPrivateDNSZone", + "resourcemanager.projects.get", + ] + } + logging_sinks = { + for name, attrs in var.log_sinks : name => { + bq_partitioned_table = attrs.type == "bigquery" + destination = local.log_sink_destinations[name] + exclusions = {} + filter = attrs.filter + iam = true + include_children = true + type = attrs.type + } + } +} + +# assign the custom restricted Organization Admin role to the relevant service +# accounts, with a condition that only enables granting specific roles; +# these roles use additive bindings everywhere to avoid conflicts / permadiffs + +resource "google_organization_iam_binding" "org_admin_delegated" { + org_id = var.organization.id + count = local.billing_org ? 1 : 0 + role = module.organization.custom_role_id.organizationIamAdmin + members = [module.automation-tf-resman-sa.iam_email] + condition { + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", concat( + [ + "roles/accesscontextmanager.policyAdmin", + "roles/compute.orgFirewallPolicyAdmin", + "roles/compute.xpnAdmin", + "roles/orgpolicy.policyAdmin", + module.organization.custom_role_id.xpnServiceAdmin + ], + local.billing_org ? [ + "roles/billing.admin", + "roles/billing.costsManager", + "roles/billing.user", + ] : [] + ))) + ) + } + depends_on = [module.organization] +} diff --git a/fast/stages/00-bootstrap/outputs.tf b/fast/stages/00-bootstrap/outputs.tf new file mode 100644 index 00000000..c25b9013 --- /dev/null +++ b/fast/stages/00-bootstrap/outputs.tf @@ -0,0 +1,113 @@ +/** + * 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 { + providers = { + "00-bootstrap" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-sa.email + }) + "01-resman" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-sa.email + }) + } + tfvars = { + "01-resman" = jsonencode({ + automation_project_id = module.automation-project.project_id + billing_account = var.billing_account + custom_roles = module.organization.custom_role_id + groups = var.groups + organization = var.organization + prefix = var.prefix + }) + "02-networking" = jsonencode({ + billing_account_id = var.billing_account.id + organization = var.organization + prefix = var.prefix + }) + "02-security" = jsonencode({ + billing_account_id = var.billing_account.id + organization = var.organization + prefix = var.prefix + }) + "03-gke-multitenant-dev" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-gke-multitenant-prod" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-project-factory-dev" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-project-factory-prod" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + } +} + +# optionally generate providers and tfvars files for subsequent stages + +resource "local_file" "providers" { + for_each = var.outputs_location == null ? {} : local.providers + filename = "${var.outputs_location}/${each.key}/providers.tf" + content = each.value +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-bootstrap.auto.tfvars.json" + content = each.value +} + +# outputs + +output "billing_dataset" { + description = "BigQuery dataset prepared for billing export." + value = try(module.billing-export-dataset.0.id, null) +} + +output "project_ids" { + description = "Projects created by this stage." + value = { + automation = module.automation-project.project_id + billing-export = try(module.billing-export-project.0.project_id, null) + log-export = module.log-export-project.project_id + } +} + +# ready to use provider configurations for subsequent stages when not using files + +output "providers" { + # tfdoc:output:consumers stage-01 + description = "Terraform provider files for this stage and dependent stages." + sensitive = true + value = local.providers +} + +# ready to use variable values for subsequent stages + +output "tfvars" { + description = "Terraform variable files for the following stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/00-bootstrap/variables.tf b/fast/stages/00-bootstrap/variables.tf new file mode 100644 index 00000000..9f102c77 --- /dev/null +++ b/fast/stages/00-bootstrap/variables.tf @@ -0,0 +1,100 @@ +/** + * 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 "billing_account" { + description = "Billing account id and organization id ('nnnnnnnn' or null)." + type = object({ + id = string + organization_id = number + }) +} + +variable "bootstrap_user" { + description = "Email of the nominal user running this stage for the first time." + type = string + default = null +} + +variable "groups" { + # https://cloud.google.com/docs/enterprise/setup-checklist + description = "Group names to grant organization-level permissions." + type = map(string) + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "iam" { + description = "Organization-level custom IAM settings in role => [principal] format." + type = map(list(string)) + default = {} +} + +variable "iam_additive" { + description = "Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings." + type = map(list(string)) + default = {} +} + +variable "log_sinks" { + description = "Org-level log sinks, in name => {type, filter} format." + type = map(object({ + filter = string + type = string + })) + default = { + audit-logs = { + filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" + type = "bigquery" + } + vpc-sc = { + filter = "protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata\"" + type = "bigquery" + } + } + validation { + condition = alltrue([ + for k, v in var.log_sinks : + contains(["bigquery", "logging", "pubsub", "storage"], v.type) + ]) + error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'." + } +} + +variable "organization" { + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} From 1ee07ec9d4dd0b1a88b67e4379d13962a7b70d09 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Mon, 17 Jan 2022 10:22:48 +0100 Subject: [PATCH 002/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/02-networking/README.md | 353 ++++++++++++++++++ fast/stages/02-networking/data/cidrs.yaml | 15 + .../data/dashboards/firewall_insights.json | 68 ++++ .../02-networking/data/dashboards/vpn.json | 248 ++++++++++++ .../data/firewall-rules/landing/rules.yaml | 15 + .../data/hierarchical-policy-rules.yaml | 49 +++ .../data/subnets/dev/dev-default-ew1.yaml | 5 + .../subnets/landing/landing-default-ew1.yaml | 5 + .../data/subnets/prod/prod-default-ew1.yaml | 5 + .../data/subnets/prod/prod-gke-ew1.yaml | 8 + fast/stages/02-networking/diagram.png | Bin 0 -> 141184 bytes fast/stages/02-networking/dns-dev.tf | 53 +++ fast/stages/02-networking/dns-landing.tf | 93 +++++ fast/stages/02-networking/dns-prod.tf | 53 +++ fast/stages/02-networking/main.tf | 72 ++++ fast/stages/02-networking/monitoring.tf | 32 ++ fast/stages/02-networking/outputs.tf | 95 +++++ fast/stages/02-networking/test-resources.tf | 100 +++++ fast/stages/02-networking/variables.tf | 253 +++++++++++++ fast/stages/02-networking/vpc-landing.tf | 93 +++++ fast/stages/02-networking/vpc-spoke-dev.tf | 105 ++++++ fast/stages/02-networking/vpc-spoke-prod.tf | 105 ++++++ fast/stages/02-networking/vpn-onprem.tf | 50 +++ fast/stages/02-networking/vpn-spoke-dev.tf | 73 ++++ fast/stages/02-networking/vpn-spoke-prod.tf | 121 ++++++ 25 files changed, 2069 insertions(+) create mode 100644 fast/stages/02-networking/README.md create mode 100644 fast/stages/02-networking/data/cidrs.yaml create mode 100644 fast/stages/02-networking/data/dashboards/firewall_insights.json create mode 100644 fast/stages/02-networking/data/dashboards/vpn.json create mode 100644 fast/stages/02-networking/data/firewall-rules/landing/rules.yaml create mode 100644 fast/stages/02-networking/data/hierarchical-policy-rules.yaml create mode 100644 fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml create mode 100644 fast/stages/02-networking/diagram.png create mode 100644 fast/stages/02-networking/dns-dev.tf create mode 100644 fast/stages/02-networking/dns-landing.tf create mode 100644 fast/stages/02-networking/dns-prod.tf create mode 100644 fast/stages/02-networking/main.tf create mode 100644 fast/stages/02-networking/monitoring.tf create mode 100644 fast/stages/02-networking/outputs.tf create mode 100644 fast/stages/02-networking/test-resources.tf create mode 100644 fast/stages/02-networking/variables.tf create mode 100644 fast/stages/02-networking/vpc-landing.tf create mode 100644 fast/stages/02-networking/vpc-spoke-dev.tf create mode 100644 fast/stages/02-networking/vpc-spoke-prod.tf create mode 100644 fast/stages/02-networking/vpn-onprem.tf create mode 100644 fast/stages/02-networking/vpn-spoke-dev.tf create mode 100644 fast/stages/02-networking/vpn-spoke-prod.tf diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md new file mode 100644 index 00000000..4df304a8 --- /dev/null +++ b/fast/stages/02-networking/README.md @@ -0,0 +1,353 @@ +# Networking + +This stage sets up the shared network infrastructure for the whole org. It adopts the common “hub and spoke” reference design, which is well suited to multiple scenarios, and offers several advantages versus other designs: + +- the “hub” VPC centralizes external connectivity to on-prem or other cloud environments, and is ready to host cross-environment services like CI/CD, code repositories, and monitoring probes +- the “spoke” VPCs allow partitioning workloads (e.g. by environment like in this setup), while still retaining controlled access to central connectivity and services +- Shared VPC in both hub and spokes splits management of network resources in specific (host) projects, while still allowing them to be consumed from workload (service) projects +- the design also lends itself to easy DNS centralization, both from on-prem to cloud and from cloud to on-prem + +Connectivity between hub and spokes is established here via [VPN HA](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) tunnels, which offer easy interoperability with some key GCP features (GKE, services leveraging Service Networking like Cloud SQL, etc.), allowing clear partitioning of quota and limits between environments, and fine-grained control of routing. Different ways of implementing connectivity, and their respective pros and cons, are discussed below. + +The following diagram illustrates the high-level design, and should be used as a reference for the following sections. The final number of subnets, and their IP addressing design will of course depend on customer-specific requirements, and can be easily changed via variables or external data files without having to edit the actual code. + +![Networking diagram](diagram.png) + +## Design overview and choices + +### VPC design + +The hub/landing VPC hosts external connectivity and shared services for spoke VPCs, which are connected to it via VPN HA tunnels. Spokes are used here to partition environments, which is a fairly common pattern: + +- one spoke VPC for the production environment +- one spoke VPC for the development environment + +Each VPC is created into its own project, and each project is configured as a Shared VPC host, so that network-related resources and access configurations via IAM are kept separate for each VPC. + +The design easily lends itself to implementing additional environments, or adopting a different logical mapping for spokes (e.g. one spoke for each company entity, etc.). Adding spokes is a trivial operation, does not increase the design complexity, and is explained in operational terms in the following sections. + +In multi-organization scenarios, where production and non-production resources use different Cloud Identity and GCP organizations, the hub/landing VPC is usually part of the production organization, and establishes connections with production spokes in its same organization, and non-production spokes in a different organization. + +An additional VPC is also deployed by default with the provided code, disconnected from the other VPCs and hosting a single VM that emulates on-prem for testing purposes, via a Docker network and containers for VPN, DNS, HTTP, etc. + +### External connectivity + +External connectivity to on-prem is implemented here via VPN HA (two tunnels per region), as this is the minimum common denominator often used directly, or as a stop-gap solution to validate routing and transfer data, while waiting for [interconnects](https://cloud.google.com/network-connectivity/docs/interconnect) to be provisioned. + +Connectivity to additional on-prem sites or other cloud providers should be implemented in a similar fashion, via VPN tunnels or interconnects in the landing VPC sharing the same regional router. + +### Internal connectivity + +As mentioned initially, there are of course other ways to implement internal connectivity other than VPN HA. These can be easily retrofitted with minimal code changes, but introduce additional considerations for service interoperability, quotas and management. + +This is a summary of the main options: + +- [VPN HA](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) (implemented here) + - Pros: simple compatibility with GCP services that leverage peering internally, better control on routes, avoids peering groups shared quotas and limits + - Cons: additional cost, marginal increase in latency, requires multiple tunnels for full bandwidth +- [VPC Peering](https://cloud.google.com/vpc/docs/vpc-peering) + - Pros: no additional costs, full bandwidth with no configurations, no extra latency + - Cons: no transitivity (e.g. to GKE masters, Cloud SQL, etc.), no selective exchange of routes, several quotas and limits shared between VPCs in a peering group +- [Multi-NIC appliances](https://cloud.google.com/architecture/best-practices-vpc-design#multi-nic) + - Pros: additional security features (e.g. IPS), potentially better integration with on-prem systems by using the same vendor + - Cons: complex HA/failover setup, limited by VM bandwidth and scale, additional costs for VMs and licenses, out of band management of a critical cloud component + +### IP ranges, subnetting, routing + +Minimizing the number of routes (and subnets) in use on the cloud environment is an important consideration, as it simplifies management and avoids hitting [Cloud Router](https://cloud.google.com/network-connectivity/docs/router/quotas) and [VPC](https://cloud.google.com/vpc/docs/quota) quotas and limits. For this reason, we recommend careful planning of the IP space used in your cloud environment, to be able to use large IP CIDR blocks in routes whenever possible. + +This stage uses a dedicated /16 block (which should of course be sized to your needs) for each region in each VPC, and subnets created in each VPC derive their ranges from the relevant block. + +Spoke VPCs also define and reserve two "special" CIDR ranges dedicated to [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access) and [Internal HTTPs Load Balancers (L7ILB)](https://cloud.google.com/load-balancing/docs/l7-internal). + +Routes in GCP are either automatically created for VPC subnets, manually created via static routes, or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) via BGP sessions, which can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements. + +In this setup, the Cloud Routers are configured so as to exclude the default advertisement of VPC ranges, and they only advertise their respective aggregate ranges via custom advertisements. This greatly simplifies the routing configuration, and more importantly it allows to avoid quota or limit issues by keeping the number of routes small, instead of making it proportional to the subnets and secondary ranges in the VPCs. + +The high-level routing plan implemented in this architecture is as follows: + +| source | target | advertisement | +| ----------- | ----------- | ------------------------------ | +| VPC landing | onprem | GCP aggregate | +| VPC landing | onprem | Cloud DNS forwarders | +| VPC landing | onprem | Google private/restricted APIs | +| VPC landing | spokes | RFC1918 | +| VPC spoke | VPC landing | spoke aggregate | +| onprem | VC landing | onprem aggregates | + +As is evident from the table above, the hub/landing VPC acts as the route concentrator for the whole GCP network, implementing a full line of sight between environments, and between GCP and on-prem. While advertisements can be adjusted to selectively exchange routes (e.g. to isolate the production and the development environment), we recommend using [Firewall](#firewall) policies or rules to achieve the desired isolation. + +### Internet egress + +The path of least resistance for Internet egress is using Cloud NAT, and that is what's implemented in this setup, with a NAT gateway configured for each VPC. + +Several other scenarios are possible of course, with varying degrees of complexity: + +- a forward proxy, with optional URL filters +- a default route to on-prem to leverage existing egress infrastructure +- a full-fledged perimeter firewall to control egress and implement additional security features like IPS + +Future pluggable modules will allow to easily experiment, or deploy the above scenarios. + +### Firewall + +The GCP Firewall is a stateful, distributed feature that allows the creation of L4 policies, either via VPC-level rules or more recently via hierarchical policies applied on the resource hierarchy (organization, folders). + +The current setup adopts both firewall types, and uses hierarchical rules on the Networking folder for common ingress rules (egress is open by default), e.g. from health check or IAP forwarders ranges, and VPC rules for the environment or workload-level ingress. + +Rules and policies are defined in simple YAML files, described below. + +### DNS + +DNS often goes hand in hand with networking, especially on GCP where Cloud DNS zones and policies are associated at the VPC level. This setup implements both DNS flows: + +- on-prem to cloud via private zones for cloud-managed domains, and an [inbound policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) used as forwarding target or via delegation (requires some extra configuration) from on-prem DNS resolvers +- cloud to on-prem via forwarding zones for the on-prem managed domains + +DNS configuration is further centralized by leveraging peering zones, so that + +- the hub/landing Cloud DNS hosts configurations for on-prem forwarding and Google API domains, with the spokes consuming them via DNS peering zones +- the spokes Cloud DNS host configurations for the environment-specific domains, with the hub/landing VPC acting as consumer via DNS peering + +To complete the configuration, the 35.199.192.0/19 range should be routed on the VPN tunnels from on-prem, and the following names configured for DNS forwarding to cloud: + +- `private.googleapis.com` +- `restricted.googleapis.com` +- `gcp.example.com` (used as a placeholder) + +From cloud, the `example.com` domain (used as a placeholder) is forwarded to on-prem. + +This configuration is battle-tested, and flexible enough to lend itself to simple modifications without subverting its design, for example by forwarding and peering root zones to bypass Cloud DNS external resolution. + +## How to run this stage + +This stage is meant to be executed after the [resman](../01-resman) stage has run, as it leverages the automation service account and bucket created there, and additional resources configured in the [bootstrap](../00-boostrap) stage. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the previous stages for the environmental requirements. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during the [resource management](./01-resman) stage, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: + +```bash +cd ../00-bootstrap +terraform output -json providers | jq -r '.["02-networking"]' \ + > ../02-networking/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variables with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +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 + +VPCs are defined in separate files, one for `landing` and one for each of `prod` and `dev`. +Each file contains the same resources, described in the following paragraphs. + +The **project** ([`project`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)". + +The **VPC** ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc#subnet-factory). +Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are in variable `psa_ranges` - such variables are consumed by spoke VPCs. + +**Cloud NAT** ([`net-cloudnat`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress. + +### VPNs + +#### External + +Connectivity to on-prem is implemented with VPN HA ([`net-vpn`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpn)) and defined in [`vpn-onprem.tf`](./vpn-onprem.tf). The file provisionally implements a single logical connection between onprem and landing at `europe-west1`, and the relevant parameters for its configuration are found in variable `vpn_onprem_configs`. + +#### Internal + +VPNs ([`net-vpn`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpn)) used to interconnect landing and spokes are managed by `vpn-spoke-*.tf` files, each implementing both sides of the VPN connection. Per-gateway configurations (e.g. BGP advertisements and session ranges) are controlled by variable `vpn_onprem_configs`. VPN gateways and IKE secrets are automatically generated and configured. + +### Routing and BGP + +Each VPC network ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages a separate routing table, which can define static routes (e.g. to private.googleapis.com) and receives dynamic routes from BGP sessions established with neighbor networks (e.g. landing receives routes from onprem and spokes, and spokes receive RFC1918 from landing). + +Static routes are defined in `vpc-*.tf` files, in the `routes` section of each `net-vpc` module. + +BGP sessions for landing-spoke are configured through variable `vpn_spoke_configs`, while the ones for landing-onprem use variable `vpn_onprem_configs` + +### Firewall + +**VPC firewall rules** ([`net-vpc-firewall`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall)) are defined per-vpc on each `vpc-*.tf` file and leverage a resource factory to massively create rules. +To add a new firewall rule, create a new file or edit an existing one in the `data_folder` directory defined in the module `net-vpc-firewall`, following the examples of the "[Rules factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall#rules-factory)" section of the module documentation. + +**Hierarchical firewall policies** ([`folder`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/folder)) are defined in `main.tf`, and managed through a policy factory implemented by the `folder` module, which applies the defined hierarchical to the `Networking` folder, which contains all the core networking infrastructure. Policies are defined in the `rules_file` file - to define a new one simply use the instructions found on "[Firewall policy factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/organization#firewall-policy-factory)". + + +### DNS + +The DNS ([`dns`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/dns)) infrastructure is defined in [`dns.tf`](dns.tf). + +Cloud DNS manages onprem forwarding, the main GCP zone (in this example `gcp.example.com`) and is peered to environment-specific zones (i.e. `dev.gcp.example.com` and `prod.gcp.example.com`). + +#### Cloud environment + +Per the section above Landing acts as the source of truth for DNS within the Cloud environment. Resources defined in the spoke VPCs consume the Landing DNS infrastructure through DNS peering (e.g. `prod-landing-root-dns-peering`). +Spokes can optionally define private zones (e.g. `prod-dns-private-zone`) - granting visibility to the Landing VPC ensures that the whole cloud environment can query such zones. + +#### Cloud to on-prem + +Leveraging the forwarding zones defined on Landing (e.g. `onprem-example-dns-forwarding` and `reverse-10-dns-forwarding`), the cloud environment can resolve `in-addr.arpa.` and `onprem.example.com.` using the on-premises DNS infrastructure. Onprem resolvers IPs are set in variable `dns.onprem`. + +DNS queries sent to the on-premises infrastructure come from the `35.199.192.0/19` source range, which is only accessible from within a VPC or networks connected to one. + +#### On-prem to cloud + +The [Inbound DNS Policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) defined in module `landing-vpc` ([`landing.tf`](./landing.tf)) automatically reserves the first available IP address on each created subnet (typically the third one in a CIDR) to expose the Cloud DNS service so that it can be consumed from outside of GCP. + +### Private Google Access +[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment. + +For PGA to work: + +* Private Google Access should be enabled on the subnet. \ +Subnets created by the `net-vpc` module are PGA-enabled by default. + +* 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \ +Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed. + +* A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain, as implemented in module `googleapis-private-zone` in [`dns.tf`](./dns.tf) + +### Preliminar activities + +Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` to your needs, to update all reference to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. + +If you're not using FAST, you'll also need to create a `providers.tf` file to configure the GCS backend and the service account to use to run the deployment. + +You're now ready to run `terraform init` and `apply`. + +### Post-deployment activities + +* On-prem routers should be configured to advertise all relevant CIDRs to the GCP environments. To avoid hitting GCP quotas, we recomment aggregating routes as much as possible. +* On-prem routers should accept BGP sessions from their cloud peers. +* On-prem DNS servers should have forward zones for GCP-managed ones. + +## Customizations + +### Adding an environment +To create a new environment (e.g. `staging`), a few changes are required. + +Create a `vpc-spoke-staging.tf` file by copying `vpc-spoke-prod.tf` file, +and adapt the new file by replacing the value "prod" with the value "staging". +Running `diff vpc-spoke-dev.tf vpc-spoke-prod.tf` can help to see how environment files differ. + +The new VPC requires a set of dedicated CIDRs, one per region, added to variable `custom_adv` (for example as `spoke_staging_ew1` and `spoke_staging_ew4`). +>`custom_adv` is a map that "resolves" CIDR names to actual addresses, and will be used later to configure routing. +> +Variables managing L7 Interal Load Balancers (`l7ilb_subnets`) and Private Service Access (`psa_ranges`) should also be adapted, and subnets and firewall rules for the new spoke should be added as described above. + +VPN HA connectivity (see also [VPNs](#vpns)) to `landing` is managed by the `vpn-spoke-*.tf` files. +Copy `vpn-spoke-prod.tf` to `vpn-spoke-staging.tf` - replace "prod" with "staging" where relevant. + +VPN configuration also controls BGP advertisements, which requires the following variable changes: +* `router_configs` to configure the new routers (one per region) created for the `staging` VPC +* `vpn_onprem_configs` to configure the new advertisments to on-premises for the new CIDRs +* `vpn_spoke_configs` to configure the new advertisements to `landing` for the new VPC - new keys (one per region) should be added, such as e.g. `staging-ew1` and `staging-ew4` + +DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `staging.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy all the `prod-*` modules in the `dns.tf` file to `staging-*`, and update their content accordingly. Don't forget to add a peering zone from Landing to the newly created environment private zone. + + + + + +## Files + +| name | description | modules | resources | +| ---------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | +| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | +| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | +| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | +| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | +| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | + +## Variables + +| name | description | type | required | default | producer | +| ------------------ | -------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------: | +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | +| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | +| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | +| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | +| gke | | map(object({…})) | | {} | 01-resman | +| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | +| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | +| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | +| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | +| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | + +## Outputs + +| name | description | sensitive | consumers | +| ------------------------ | ----------------------------------------------- | :-------: | --------- | +| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | +| project_ids | Network project ids. | | | +| project_numbers | Network project numbers. | | | +| shared_vpc_host_projects | Shared VPC host projects. | | | +| shared_vpc_self_links | Shared VPC host projects. | | | +| tfvars | Network-related variables used in other stages. | ✓ | | +| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | + + + + + + + + + + + + + + + + + + + + diff --git a/fast/stages/02-networking/data/cidrs.yaml b/fast/stages/02-networking/data/cidrs.yaml new file mode 100644 index 00000000..5f453d8d --- /dev/null +++ b/fast/stages/02-networking/data/cidrs.yaml @@ -0,0 +1,15 @@ +# skip boilerplate check + +healthchecks: + - 35.191.0.0/16 + - 130.211.0.0/22 + - 209.85.152.0/22 + - 209.85.204.0/22 + +rfc1918: + - 10.0.0.0/8 + - 172.16.0.0/16 + - 192.168.0.0/16 + +onprem_probes: + - 10.255.255.254/32 diff --git a/fast/stages/02-networking/data/dashboards/firewall_insights.json b/fast/stages/02-networking/data/dashboards/firewall_insights.json new file mode 100644 index 00000000..e829091c --- /dev/null +++ b/fast/stages/02-networking/data/dashboards/firewall_insights.json @@ -0,0 +1,68 @@ +{ + "displayName": "Firewall Insights Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Subnet Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/subnet/firewall_hit_count\" resource.type=\"gce_subnetwork\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "VM Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/vm/firewall_hit_count\" resource.type=\"gce_instance\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} \ No newline at end of file diff --git a/fast/stages/02-networking/data/dashboards/vpn.json b/fast/stages/02-networking/data/dashboards/vpn.json new file mode 100644 index 00000000..4396cc00 --- /dev/null +++ b/fast/stages/02-networking/data/dashboards/vpn.json @@ -0,0 +1,248 @@ +{ + "displayName": "VPN Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} \ No newline at end of file diff --git a/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml b/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml new file mode 100644 index 00000000..e72b7c9c --- /dev/null +++ b/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml @@ -0,0 +1,15 @@ +# skip boilerplate check + +allow-onprem-probes-example: + description: "Allow traffic from onprem probes" + direction: INGRESS + action: allow + sources: [] + ranges: + - $onprem_probes + targets: [] + use_service_accounts: false + rules: + - protocol: tcp + ports: + - 12345 diff --git a/fast/stages/02-networking/data/hierarchical-policy-rules.yaml b/fast/stages/02-networking/data/hierarchical-policy-rules.yaml new file mode 100644 index 00000000..0172a309 --- /dev/null +++ b/fast/stages/02-networking/data/hierarchical-policy-rules.yaml @@ -0,0 +1,49 @@ +# skip boilerplate check + +allow-admins: + description: Access from the admin subnet to all subnets + direction: INGRESS + action: allow + priority: 1000 + ranges: + - $rfc1918 + ports: + all: [] + target_resources: null + enable_logging: false + +allow-healthchecks: + description: Enable HTTP and HTTPS healthchecks + direction: INGRESS + action: allow + priority: 1001 + ranges: + - $healthchecks + ports: + tcp: ["80", "443"] + target_resources: null + enable_logging: false + +allow-ssh-from-iap: + description: Enable SSH from IAP + direction: INGRESS + action: allow + priority: 1002 + ranges: + - 35.235.240.0/20 + ports: + tcp: ["22"] + target_resources: null + enable_logging: false + +allow-icmp: + description: Enable ICMP + direction: INGRESS + action: allow + priority: 1003 + ranges: + - 0.0.0.0/0 + ports: + icmp: [] + target_resources: null + enable_logging: false diff --git a/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml new file mode 100644 index 00000000..37c28f03 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.144.0.0/24 +description: Default subnet for dev diff --git a/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml b/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml new file mode 100644 index 00000000..5af68db6 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.0.0/24 +description: Default subnet for landing diff --git a/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml new file mode 100644 index 00000000..7a77f309 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.136.0.0/24 +description: Default subnet for prod diff --git a/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml new file mode 100644 index 00000000..2512947e --- /dev/null +++ b/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +region: europe-west1 +description: GKE subnet for prod +ip_cidr_range: 10.140.0.0/24 +secondary_ip_ranges: + - pods: 10.140.1.0/16 + - services: 10.140.2.0/24 diff --git a/fast/stages/02-networking/diagram.png b/fast/stages/02-networking/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c071ccf1bac3089172b7d67d195ec93fa161e241 GIT binary patch literal 141184 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

NGZ@;#C*C>7?XTUkt-9(V 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ྫ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 Bibenc literal 0 HcmV?d00001 diff --git a/fast/stages/02-networking/dns-dev.tf b/fast/stages/02-networking/dns-dev.tf new file mode 100644 index 00000000..5047f832 --- /dev/null +++ b/fast/stages/02-networking/dns-dev.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Development spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "dev-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "dev-gcp-example-com" + domain = "dev.gcp.example.com." + client_networks = [module.dev-spoke-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "dev-landing-root-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-root-dns-peering" + domain = "." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} + +module "dev-reverse-10-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} diff --git a/fast/stages/02-networking/dns-landing.tf b/fast/stages/02-networking/dns-landing.tf new file mode 100644 index 00000000..4c6bb774 --- /dev/null +++ b/fast/stages/02-networking/dns-landing.tf @@ -0,0 +1,93 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Landing DNS zones and peerings setup. + +# forwarding to on-prem DNS resolvers + +module "onprem-example-dns-forwarding" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "forwarding" + name = "example-com" + domain = "onprem.example.com." + client_networks = [module.landing-vpc.self_link] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "reverse-10-dns-forwarding" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "forwarding" + name = "root-reverse-10" + domain = "10.in-addr.arpa." + client_networks = [module.landing-vpc.self_link] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "gcp-example-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "gcp-example-com" + domain = "gcp.example.com." + client_networks = [module.landing-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# GCP-specific DNS zones peered to the environment spoke that holds the config + +module "prod-gcp-example-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "peering" + name = "prod-root-dns-peering" + domain = "prod.gcp.example.com." + client_networks = [module.landing-vpc.self_link] + peer_network = module.prod-spoke-vpc.self_link +} + +module "dev-gcp-example-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "peering" + name = "dev-root-dns-peering" + domain = "dev.gcp.example.com." + client_networks = [module.landing-vpc.self_link] + peer_network = module.dev-spoke-vpc.self_link +} + +# Google API zone to trigger Private Access + +module "googleapis-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "googleapis-com" + domain = "googleapis.com." + client_networks = [module.landing-vpc.self_link] + recordsets = { + "A private" = { type = "A", ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "A restricted" = { type = "A", ttl = 300, records = [ + "199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7" + ] } + "CNAME *" = { type = "CNAME", ttl = 300, records = ["private.googleapis.com."] } + } +} diff --git a/fast/stages/02-networking/dns-prod.tf b/fast/stages/02-networking/dns-prod.tf new file mode 100644 index 00000000..f9b62ca7 --- /dev/null +++ b/fast/stages/02-networking/dns-prod.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "prod-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "prod-gcp-example-com" + domain = "prod.gcp.example.com." + client_networks = [module.prod-spoke-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "prod-landing-root-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-root-dns-peering" + domain = "." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} + +module "prod-reverse-10-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} diff --git a/fast/stages/02-networking/main.tf b/fast/stages/02-networking/main.tf new file mode 100644 index 00000000..41409d1d --- /dev/null +++ b/fast/stages/02-networking/main.tf @@ -0,0 +1,72 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Networking folder and hierarchical policy. + +locals { + # define the structures used for BGP peers in the VPN resources + bgp_peer_options = { + for k, v in var.vpn_spoke_configs : + k => var.vpn_spoke_configs[k].adv == null ? null : { + advertise_groups = [] + advertise_ip_ranges = { + for adv in(var.vpn_spoke_configs[k].adv == null ? [] : var.vpn_spoke_configs[k].adv.custom) : + var.custom_adv[adv] => adv + } + advertise_mode = try(var.vpn_spoke_configs[k].adv.default, false) ? "DEFAULT" : "CUSTOM" + route_priority = null + } + } + bgp_peer_options_onprem = { + for k, v in var.vpn_onprem_configs : + k => var.vpn_onprem_configs[k].adv == null ? null : { + advertise_groups = [] + advertise_ip_ranges = { + for adv in(var.vpn_onprem_configs[k].adv == null ? [] : var.vpn_onprem_configs[k].adv.custom) : + var.custom_adv[adv] => adv + } + advertise_mode = try(var.vpn_onprem_configs[k].adv.default, false) ? "DEFAULT" : "CUSTOM" + route_priority = null + } + } + l7ilb_subnets = { for env, v in var.l7ilb_subnets : env => [ + for s in v : merge(s, { + active = true + name = "${env}-l7ilb-${s.region}" + })] + } + region_trigram = { + europe-west1 = "ew1" + europe-west3 = "ew3" + } +} + +module "folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Networking" + folder_create = var.folder_id == null + id = var.folder_id + firewall_policy_factory = { + cidr_file = "${var.data_dir}/cidrs.yaml" + policy_name = null + rules_file = "${var.data_dir}/hierarchical-policy-rules.yaml" + } + firewall_policy_association = { + factory-policy = "factory" + } +} + diff --git a/fast/stages/02-networking/monitoring.tf b/fast/stages/02-networking/monitoring.tf new file mode 100644 index 00000000..7b8b70c5 --- /dev/null +++ b/fast/stages/02-networking/monitoring.tf @@ -0,0 +1,32 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Network monitoring dashboards. + +locals { + dashboard_path = "${var.data_dir}/dashboards" + dashboard_files = fileset(local.dashboard_path, "*.json") + dashboards = { + for filename in local.dashboard_files : + filename => "${local.dashboard_path}/${filename}" + } +} + +resource "google_monitoring_dashboard" "dashboard" { + for_each = local.dashboards + project = module.landing-project.project_id + dashboard_json = file(each.value) +} diff --git a/fast/stages/02-networking/outputs.tf b/fast/stages/02-networking/outputs.tf new file mode 100644 index 00000000..4efe9bc6 --- /dev/null +++ b/fast/stages/02-networking/outputs.tf @@ -0,0 +1,95 @@ +/** + * 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. + */ +# optionally generate providers and tfvars files for subsequent stages + +locals { + tfvars = { + "03-project-factory-dev" = jsonencode({ + environment_dns_zone = module.dev-dns-private-zone.domain + shared_vpc_self_link = module.dev-spoke-vpc.self_link + vpc_host_project = module.dev-spoke-project.project_id + }) + "03-project-factory-prod" = jsonencode({ + environment_dns_zone = module.prod-dns-private-zone.domain + shared_vpc_self_link = module.prod-spoke-vpc.self_link + vpc_host_project = module.prod-spoke-project.project_id + }) + } +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-networking.auto.tfvars.json" + content = each.value +} + +# outputs + +output "cloud_dns_inbound_policy" { + description = "IP Addresses for Cloud DNS inbound policy." + value = [for s in module.landing-vpc.subnets : cidrhost(s.ip_cidr_range, 2)] +} + +output "project_ids" { + description = "Network project ids." + value = { + dev = module.dev-spoke-project.project_id + landing = module.landing-project.project_id + prod = module.prod-spoke-project.project_id + } +} + +output "project_numbers" { + description = "Network project numbers." + value = { + dev = "projects/${module.dev-spoke-project.number}" + landing = "projects/${module.landing-project.number}" + prod = "projects/${module.prod-spoke-project.number}" + } +} + +output "shared_vpc_host_projects" { + description = "Shared VPC host projects." + value = { + landing = module.landing-project.project_id + dev = module.dev-spoke-project.project_id + prod = module.prod-spoke-project.project_id + } +} + + +output "shared_vpc_self_links" { + description = "Shared VPC host projects." + value = { + landing = module.landing-vpc.self_link + dev = module.dev-spoke-vpc.self_link + prod = module.prod-spoke-vpc.self_link + } +} + + +output "vpn_gateway_endpoints" { + description = "External IP Addresses for the GCP VPN gateways." + value = { + onprem-ew1 = { for v in module.landing-to-onprem-ew1-vpn.gateway.vpn_interfaces : v.id => v.ip_address } + } +} + +output "tfvars" { + description = "Network-related variables used in other stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/02-networking/test-resources.tf b/fast/stages/02-networking/test-resources.tf new file mode 100644 index 00000000..6f67e30d --- /dev/null +++ b/fast/stages/02-networking/test-resources.tf @@ -0,0 +1,100 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description temporary instances for testing + +module "test-vm-landing-0" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/compute-vm?ref=v12.0.0" + project_id = module.landing-project.project_id + zone = "europe-west1-b" + name = "test-vm-1" + network_interfaces = [{ + network = module.landing-vpc.self_link + subnetwork = module.landing-vpc.subnet_self_links["europe-west1/landing-default-ew1"] + alias_ips = {} + nat = false + addresses = null + }] + tags = ["ssh"] + service_account_create = true + boot_disk = { + image = "projects/debian-cloud/global/images/family/debian-10" + type = "pd-balanced" + size = 10 + } + metadata = { + startup-script = < { + address = cidrhost(v, 0) + network = module.dev-spoke-vpc.self_link + prefix_length = split("/", v)[1] + } + } +} diff --git a/fast/stages/02-networking/vpc-spoke-prod.tf b/fast/stages/02-networking/vpc-spoke-prod.tf new file mode 100644 index 00000000..b4bf6c5b --- /dev/null +++ b/fast/stages/02-networking/vpc-spoke-prod.tf @@ -0,0 +1,105 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke VPC and related resources. + +module "prod-spoke-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + billing_account = var.billing_account_id + name = "prod-net-spoke-0" + parent = var.folder_id + prefix = var.prefix + service_config = { + disable_on_destroy = false + disable_dependent_services = false + } + services = [ + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + ] + shared_vpc_host_config = { + enabled = true + service_projects = [] + } + metric_scopes = [module.landing-project.project_id] + iam = { + "roles/dns.admin" = [var.project_factory_sa.prod] + } +} + +module "prod-spoke-vpc" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpc?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + name = "prod-spoke-0" + mtu = 1500 + data_folder = "${var.data_dir}/subnets/prod" + subnets_l7ilb = local.l7ilb_subnets.prod + # set explicit routes for googleapis in case the default route is deleted + routes = { + private-googleapis = { + dest_range = "199.36.153.8/30" + priority = 1000 + tags = [] + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + restricted-googleapis = { + dest_range = "199.36.153.4/30" + priority = 1000 + tags = [] + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + } +} + +module "prod-spoke-firewall" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpc-firewall?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.name + admin_ranges = [] + http_source_ranges = [] + https_source_ranges = [] + ssh_source_ranges = [] + data_folder = "${var.data_dir}/firewall-rules/prod" + cidr_template_file = "${var.data_dir}/cidrs.yaml" +} + +module "prod-spoke-cloudnat" { + for_each = toset(values(module.prod-spoke-vpc.subnet_regions)) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-cloudnat?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + region = each.value + name = "prod-nat-${local.region_trigram[each.value]}" + router_create = true + router_network = module.prod-spoke-vpc.name + router_asn = 4200001024 + logging_filter = "ERRORS_ONLY" +} + +module "prod-spoke-psa-addresses" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-address?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + psa_addresses = { for r, v in var.psa_ranges.prod : r => { + address = cidrhost(v, 0) + network = module.prod-spoke-vpc.self_link + prefix_length = split("/", v)[1] + } + } +} diff --git a/fast/stages/02-networking/vpn-onprem.tf b/fast/stages/02-networking/vpn-onprem.tf new file mode 100644 index 00000000..87a8dd33 --- /dev/null +++ b/fast/stages/02-networking/vpn-onprem.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and onprem. + +module "landing-to-onprem-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-onprem-ew1" + router_create = true + router_name = "dev-spoke-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_external_gateway = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = [{ + id = 0 + # on-prem router ip address + ip_address = var.vpn_onprem_configs.landing-ew1.peer.address + }] + } + tunnels = { for t in range(2) : "remote-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_onprem_configs.landing-ew1.session_range, 1 + (t * 4)) + asn = var.vpn_onprem_configs.landing-ew1.peer.asn + } + bgp_peer_options = local.bgp_peer_options_onprem["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_onprem_configs.landing-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = 0 + router = null + shared_secret = var.vpn_onprem_configs.landing-ew1.peer.secret_id + vpn_gateway_interface = t + } + } +} diff --git a/fast/stages/02-networking/vpn-spoke-dev.tf b/fast/stages/02-networking/vpn-spoke-dev.tf new file mode 100644 index 00000000..78ce21b9 --- /dev/null +++ b/fast/stages/02-networking/vpn-spoke-dev.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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and development spoke. + +module "landing-to-dev-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-dev-ew1" + # The router used for this VPN is managed in vpn-prod.tf + router_create = false + router_name = "landing-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_gcp_gateway = module.dev-to-landing-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-dev-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } + depends_on = [ + module.landing-to-prod-ew1-vpn.router + ] +} + +module "dev-to-landing-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + network = module.dev-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew1" + router_create = true + router_name = "dev-spoke-vpn-ew1" + router_asn = var.router_configs.spoke-dev-ew1.asn + peer_gcp_gateway = module.landing-to-dev-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["dev-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-dev-ew1-vpn.random_secret + vpn_gateway_interface = t + } + } +} diff --git a/fast/stages/02-networking/vpn-spoke-prod.tf b/fast/stages/02-networking/vpn-spoke-prod.tf new file mode 100644 index 00000000..8936339b --- /dev/null +++ b/fast/stages/02-networking/vpn-spoke-prod.tf @@ -0,0 +1,121 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and production spoke. + +module "landing-to-prod-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-prod-ew1" + router_create = true + router_name = "landing-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_gcp_gateway = module.prod-to-landing-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-prod-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } +} + +module "prod-to-landing-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew1" + router_create = true + router_name = "prod-spoke-vpn-ew1" + router_asn = var.router_configs.spoke-prod-ew1.asn + peer_gcp_gateway = module.landing-to-prod-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["prod-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew1-vpn.random_secret + vpn_gateway_interface = t + } + } +} + +module "landing-to-prod-ew4-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-prod-ew4" + router_create = true + router_name = "landing-vpn-ew4" + router_asn = var.router_configs.landing-ew4.asn + peer_gcp_gateway = module.prod-to-landing-ew4-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-prod-ew4.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew4"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } +} + +module "prod-to-landing-ew4-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew4" + router_create = true + router_name = "prod-spoke-vpn-ew4" + router_asn = var.router_configs.spoke-prod-ew4.asn + peer_gcp_gateway = module.landing-to-prod-ew4-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew4.asn + } + bgp_peer_options = local.bgp_peer_options["prod-ew4"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew4-vpn.random_secret + vpn_gateway_interface = t + } + } +} From 20486edaf66b2d3f9ccf775619956d3af975ba1f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:23:01 +0100 Subject: [PATCH 003/132] merge tools changes --- tools/REQUIREMENTS.txt | 1 + tools/check_boilerplate.py | 37 ++++++++++++++++--------------- tools/check_documentation.py | 28 +++++++++++++++++------ tools/validate_schema.py | 43 ++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 25 deletions(-) create mode 100755 tools/validate_schema.py diff --git a/tools/REQUIREMENTS.txt b/tools/REQUIREMENTS.txt index dca9a909..f53a35cc 100644 --- a/tools/REQUIREMENTS.txt +++ b/tools/REQUIREMENTS.txt @@ -1 +1,2 @@ click +yamale diff --git a/tools/check_boilerplate.py b/tools/check_boilerplate.py index 6bbd4cb2..f4ec5273 100755 --- a/tools/check_boilerplate.py +++ b/tools/check_boilerplate.py @@ -33,22 +33,23 @@ _MATCH_STRING = ( _MATCH_RE = re.compile(_MATCH_STRING, re.M) -def main(dir): - "Cycle through files in dir and check for the Apache 2.0 boilerplate." +def main(base_dirs): + "Cycle through files in base_dirs and check for the Apache 2.0 boilerplate." errors, warnings = [], [] - for root, dirs, files in os.walk(dir): - dirs[:] = [d for d in dirs if d not in _EXCLUDE_DIRS] - for fname in files: - if fname in _MATCH_FILES or os.path.splitext(fname)[1] in _MATCH_FILES: - fpath = os.path.abspath(os.path.join(root, fname)) - content = open(fpath).read() - if _EXCLUDE_RE.search(content): - continue - try: - if not _MATCH_RE.search(content): - errors.append(fpath) - except (IOError, OSError): - warnings.append(fpath) + for dir in base_dirs: + for root, dirs, files in os.walk(dir): + dirs[:] = [d for d in dirs if d not in _EXCLUDE_DIRS] + for fname in files: + if fname in _MATCH_FILES or os.path.splitext(fname)[1] in _MATCH_FILES: + fpath = os.path.abspath(os.path.join(root, fname)) + content = open(fpath).read() + if _EXCLUDE_RE.search(content): + continue + try: + if not _MATCH_RE.search(content): + errors.append(fpath) + except (IOError, OSError): + warnings.append(fpath) if warnings: print('The following files cannot be accessed:') print('\n'.join(' - {}'.format(s) for s in warnings)) @@ -59,6 +60,6 @@ def main(dir): if __name__ == '__main__': - if len(sys.argv) != 2: - raise SystemExit('No directory passed.') - main(sys.argv[1]) + if len(sys.argv) < 2: + raise SystemExit('No directory to check.') + main(sys.argv[1:]) diff --git a/tools/check_documentation.py b/tools/check_documentation.py index 1968f3b4..3996928b 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import difflib import enum import pathlib @@ -32,6 +33,7 @@ def _check_dir(dir_name, files=False, show_extra=False): for readme_path in dir_path.glob('**/README.md'): if '.terraform' in str(readme_path): continue + diff = None readme = readme_path.read_text() mod_name = str(readme_path.relative_to(dir_path).parent) result = tfdoc.get_doc(readme) @@ -44,24 +46,36 @@ def _check_dir(dir_name, files=False, show_extra=False): except SystemExit: state = state.SKIP else: - state = State.OK if new_doc == result['doc'] else State.FAIL - yield mod_name, state + if new_doc == result['doc']: + state = State.OK + else: + state = State.FAIL + diff = '\n'.join( + [f'----- {mod_name} diff -----\n'] + + list(difflib.ndiff( + result['doc'].split('\n'), new_doc.split('\n') + ))) + yield mod_name, state, diff @click.command() @click.argument('dirs', type=str, nargs=-1) -@ click.option('--show-extra/--no-show-extra', default=False) @ click.option('--files/--no-files', default=False) -def main(dirs, files=False, show_extra=False): +@ click.option('--show-diffs/--no-show-diffs', default=False) +@ click.option('--show-extra/--no-show-extra', default=False) +def main(dirs, files=False, show_diffs=False, show_extra=False): 'Cycle through modules and ensure READMEs are up-to-date.' - errors = 0 + errors = [] state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'} for dir_name in dirs: print(f'----- {dir_name} -----') - for mod_name, state in _check_dir(dir_name, files, show_extra): - errors += 1 if state == State.FAIL else 0 + for mod_name, state, diff in _check_dir(dir_name, files, show_extra): + if state == State.FAIL: + errors.append(diff) print(f'[{state_labels[state]}] {mod_name}') if errors: + if show_diffs: + print('\n'.join(errors)) raise SystemExit('Errors found.') diff --git a/tools/validate_schema.py b/tools/validate_schema.py new file mode 100755 index 00000000..77ca29ec --- /dev/null +++ b/tools/validate_schema.py @@ -0,0 +1,43 @@ +import glob +import os + +import click +import yamale + + +@ click.command() +@ click.argument('schema', type=click.Path(exists=True)) +@ click.option('--directory', multiple=True, type=click.Path(exists=True, file_okay=False, dir_okay=True)) +@ click.option('--file', multiple=True, type=click.Path(exists=True, file_okay=True, dir_okay=False)) +@ click.option('--recursive', is_flag=True, default=False) +@ click.option('--quiet', is_flag=True, default=False) +def main(directory=None, file=None, schema=None, recursive=False, quiet=False): + 'Program entry point.' + + yamale_schema = yamale.make_schema(schema) + search = "**/*.yaml" if recursive else "*.yaml" + has_errors = [] + + files = list(file) + for d in directory: + files = files + glob.glob(os.path.join(d, search), recursive=recursive) + + for document in files: + yamale_data = yamale.make_data(document) + try: + yamale.validate(yamale_schema, yamale_data) + if quiet: + pass + else: + print(f'✅ {document} -> {os.path.basename(schema)}') + except ValueError as e: + has_errors.append(document) + print(e) + print(f'❌ {document} -> {os.path.basename(schema)}') + + if len(has_errors) > 0: + raise SystemExit(f"❌ Errors found in {len(has_errors)} documents.") + + +if __name__ == '__main__': + main() From 55a8aca99faeccb44592f803c1fca3c14e167198 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Mon, 17 Jan 2022 10:25:56 +0100 Subject: [PATCH 004/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/03-project-factory/README.md | 6 + fast/stages/03-project-factory/prod/README.md | 130 ++++++++++++++++++ .../prod/data/defaults.yaml.sample | 22 +++ .../prod/data/projects/project.yaml.sample | 99 +++++++++++++ .../03-project-factory/prod/diagram.png | Bin 0 -> 57470 bytes fast/stages/03-project-factory/prod/main.tf | 57 ++++++++ .../stages/03-project-factory/prod/outputs.tf | 20 +++ .../03-project-factory/prod/variables.tf | 54 ++++++++ 8 files changed, 388 insertions(+) create mode 100644 fast/stages/03-project-factory/README.md create mode 100644 fast/stages/03-project-factory/prod/README.md create mode 100644 fast/stages/03-project-factory/prod/data/defaults.yaml.sample create mode 100644 fast/stages/03-project-factory/prod/data/projects/project.yaml.sample create mode 100644 fast/stages/03-project-factory/prod/diagram.png create mode 100644 fast/stages/03-project-factory/prod/main.tf create mode 100644 fast/stages/03-project-factory/prod/outputs.tf create mode 100644 fast/stages/03-project-factory/prod/variables.tf diff --git a/fast/stages/03-project-factory/README.md b/fast/stages/03-project-factory/README.md new file mode 100644 index 00000000..2beb5158 --- /dev/null +++ b/fast/stages/03-project-factory/README.md @@ -0,0 +1,6 @@ +# Project factory + +The Project Factory (or PF) builds on top of your foundations to create and setup projects (and related resources) to be used for your workloads. +It is organized in folders representing enviroments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). + +This directory contains a single project factory ([`prod/`](./prod/)) as an example - in order to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md new file mode 100644 index 00000000..3cc9ac7e --- /dev/null +++ b/fast/stages/03-project-factory/prod/README.md @@ -0,0 +1,130 @@ +# Project factory + +The Project Factory (or PF) builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. +It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). + +## Design overview and choices + +![Diagram](diagram.png) + +A single factory creates projects in a well-defined context, according to your resource management structure. In the diagram above, each Team is structured to have specific folders projects for a given environment, such as Production and Development, per the resource management structure configured in stage `01-resman`. + +Projects for each environment across different teams are created by dedicated service accounts, as exemplified in the diagram above. While there's no intrinsic limitation regarding where the project factory can create a given project, the IAM bindings for the service account effectively enforce boundaries (e.g. the production service account shouldn't be able to create or have any access to the development projects, and vice versa). + +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)) + + +## 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 will be 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 to be 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 provider file, and populate the required variables from the previous stage. + +```bash +# Variable `outputs_location` is set to `../../configs/example` in stage 01-resman +$ cd fabric-fast/stages/03-project-factory/prod +ln -s ../../../configs/example/03-project-factory-prod/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variables with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +If you're not running on top of 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: + + +* `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 generate project `fast-prod-lab0`. + +Once the configuration is complete, the project factory can be run with the usual + +```bash +terraform init +terraform apply +``` + + + + + + +## Files + +| name | description | modules | resources | +| ------------------------------ | ----------------- | ---------------------------- | --------- | +| [main.tf](./main.tf) | Project factory. | project-factory | | +| [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +| -------------------- | --------------------------------------------------------------------- | :-----------------: | :------: | :-------------------------------------------: | :------------------------: | +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | +| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | +| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | +| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | +| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | + +## Outputs + +| name | description | sensitive | consumers | +| -------- | -------------------------------------- | :-------: | --------- | +| projects | Created projects and service accounts. | | | + + + + + diff --git a/fast/stages/03-project-factory/prod/data/defaults.yaml.sample b/fast/stages/03-project-factory/prod/data/defaults.yaml.sample new file mode 100644 index 00000000..577c4bba --- /dev/null +++ b/fast/stages/03-project-factory/prod/data/defaults.yaml.sample @@ -0,0 +1,22 @@ +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Setup for billing alerts +billing_alert: + amount: 1000 + thresholds: + current: [0.5, 0.8] + forecasted: [0.5, 0.8] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: ["team-contacts@example.com"] + +# [opt] Labels set for all projects +labels: + environment: prod + department: accounting + application: example-app + foo: bar + +# [opt] Additional notification channels for billing +notification_channels: [] \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample b/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample new file mode 100644 index 00000000..7994e7f4 --- /dev/null +++ b/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample @@ -0,0 +1,99 @@ +# [opt] Billing account id - overrides default if set +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Billing alerts config - overrides default if set +billing_alert: + amount: 10 + thresholds: + current: + - 0.5 + - 0.8 + forecasted: [] + +# [opt] DNS zones to be created as children of the environment_dns_zone defined in defaults +dns_zones: + - lorem + - ipsum + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: + - team-a-contacts@example.com + +# Folder the project will be created as children of +folder_id: folders/012345678901 + +# [opt] Authoritative IAM bindings in group => [roles] format +group_iam: + test-team-foobar@fast-lab-0.gcp-pso-italy.net: + - roles/compute.admin + +# [opt] Authoritative IAM bindings in role => [principals] format +# Generally used to grant roles to service accounts external to the project +iam: + roles/compute.admin: + - serviceAccount:service-account + +# [opt] Service robots and keys they will be assigned as cryptoKeyEncrypterDecrypter +# in service => [keys] format +kms_service_agents: + compute: [key1, key2] + storage: [key1, key2] + +# [opt] Labels for the project - merged with the ones defined in defaults +labels: + environment: prod + +# [opt] Org policy overrides defined at project level +org_policies: + policy_boolean: + constraints/compute.disableGuestAttributesAccess: true + policy_list: + constraints/compute.trustedImageProjects: + inherit_from_parent: null + status: true + suggested_value: null + values: + - projects/fast-prod-iac-core-0 + +# [opt] Service account to create for the project and their roles on the project +# in name => [roles] format +service_accounts: + another-service-account: + - roles/compute.admin + my-service-account: + - roles/compute.admin + +# [opt] APIs to enable on the project. +services: + - storage.googleapis.com + - stackdriver.googleapis.com + - compute.googleapis.com + +# [opt] Roles to assign to the robots service accounts in robot => [roles] format +services_iam: + compute: + - roles/storage.objectViewer + + # [opt] VPC setup. + # If set enables the `compute.googleapis.com` service and configures + # service project attachment +vpc: + + # [opt] If set, enables the container API + gke_setup: + + # Grants "roles/container.hostServiceAgentUser" to the container robot if set + enable_host_service_agent: false + + # Grants "roles/compute.securityAdmin" to the container robot if set + enable_security_admin: true + + # Host project the project will be service project of + host_project: fast-prod-net-spoke-0 + + # [opt] Subnets in the host project where principals will be granted networkUser + # in region/subnet-name => [principals] + subnets_iam: + europe-west1/prod-default-ew1: + - user:foobar@example.com + - serviceAccount:service-account1 \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/diagram.png b/fast/stages/03-project-factory/prod/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..b942ea47d934695831b1838d41766db699e9b64b GIT binary patch literal 57470 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Ν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 literal 0 HcmV?d00001 diff --git a/fast/stages/03-project-factory/prod/main.tf b/fast/stages/03-project-factory/prod/main.tf new file mode 100644 index 00000000..e1c05e51 --- /dev/null +++ b/fast/stages/03-project-factory/prod/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Project factory. + + +locals { + _defaults = yamldecode(file(var.defaults_file)) + _defaults_net = { + billing_account_id = var.billing_account_id + environment_dns_zone = var.environment_dns_zone + shared_vpc_self_link = var.shared_vpc_self_link + vpc_host_project = var.vpc_host_project + } + defaults = merge(local._defaults, local._defaults_net) + projects = { + for f in fileset("${var.data_dir}", "**/*.yaml") : + trimsuffix(f, ".yaml") => yamldecode(file("${var.data_dir}/${f}")) + } +} + +module "projects" { + #TODO(sruffilli): Pin to release + source = "github.com/terraform-google-modules/cloud-foundation-fabric/examples/factories/project-factory" + for_each = local.projects + defaults = local.defaults + project_id = each.key + billing_account_id = try(each.value.billing_account_id, null) + billing_alert = try(each.value.billing_alert, null) + dns_zones = try(each.value.dns_zones, []) + essential_contacts = try(each.value.essential_contacts, []) + folder_id = each.value.folder_id + group_iam = try(each.value.group_iam, {}) + iam = try(each.value.iam, {}) + kms_service_agents = try(each.value.kms, {}) + labels = try(each.value.labels, {}) + org_policies = try(each.value.org_policies, null) + service_accounts = try(each.value.service_accounts, {}) + services = try(each.value.services, []) + services_iam = try(each.value.services_iam, {}) + vpc = try(each.value.vpc, null) +} + + diff --git a/fast/stages/03-project-factory/prod/outputs.tf b/fast/stages/03-project-factory/prod/outputs.tf new file mode 100644 index 00000000..59ecff95 --- /dev/null +++ b/fast/stages/03-project-factory/prod/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "projects" { + description = "Created projects and service accounts." + value = module.projects +} diff --git a/fast/stages/03-project-factory/prod/variables.tf b/fast/stages/03-project-factory/prod/variables.tf new file mode 100644 index 00000000..8bb9f035 --- /dev/null +++ b/fast/stages/03-project-factory/prod/variables.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#TODO: tfdoc annotations + +variable "billing_account_id" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id." + type = string +} + +variable "data_dir" { + description = "Relative path for the folder storing configuration data." + type = string + default = "data/projects" +} + +variable "environment_dns_zone" { + # tfdoc:variable:source 02-networking + description = "DNS zone suffix for environment." + type = string + default = null +} + +variable "defaults_file" { + description = "Relative path for the file storing the project factory configuration." + type = string + default = "data/defaults.yaml" +} + +variable "shared_vpc_self_link" { + # tfdoc:variable:source 02-networking + description = "Self link for the shared VPC." + type = string +} + +variable "vpc_host_project" { + # tfdoc:variable:source 02-networking + description = "Host project for the shared VPC." + type = string +} From cc895193510c8b59ddd46d8ccc7cfa02df00ba45 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:26:46 +0100 Subject: [PATCH 005/132] add bolierplate to validate_schema Co-authored-by: Julio Castillo --- tools/validate_schema.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/validate_schema.py b/tools/validate_schema.py index 77ca29ec..f003723d 100755 --- a/tools/validate_schema.py +++ b/tools/validate_schema.py @@ -1,3 +1,19 @@ +#!/usr/bin/env python3 + +# 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. + import glob import os From 34e845fcdd4862f37409a843b3ffba52210e9a12 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:30:26 +0100 Subject: [PATCH 006/132] stage 02-security --- fast/stages/02-security/README.md | 319 ++++++++++++++++++ fast/stages/02-security/core-dev.tf | 64 ++++ fast/stages/02-security/core-prod.tf | 64 ++++ fast/stages/02-security/diagram.png | Bin 0 -> 93474 bytes fast/stages/02-security/main.tf | 47 +++ fast/stages/02-security/outputs.tf | 43 +++ fast/stages/02-security/variables.tf | 185 ++++++++++ .../vpc-sc-restricted-services.yaml | 88 +++++ fast/stages/02-security/vpc-sc.tf | 167 +++++++++ 9 files changed, 977 insertions(+) create mode 100644 fast/stages/02-security/README.md create mode 100644 fast/stages/02-security/core-dev.tf create mode 100644 fast/stages/02-security/core-prod.tf create mode 100644 fast/stages/02-security/diagram.png create mode 100644 fast/stages/02-security/main.tf create mode 100644 fast/stages/02-security/outputs.tf create mode 100644 fast/stages/02-security/variables.tf create mode 100644 fast/stages/02-security/vpc-sc-restricted-services.yaml create mode 100644 fast/stages/02-security/vpc-sc.tf diff --git a/fast/stages/02-security/README.md b/fast/stages/02-security/README.md new file mode 100644 index 00000000..79e99a42 --- /dev/null +++ b/fast/stages/02-security/README.md @@ -0,0 +1,319 @@ +# Shared security resources + +This stage sets up security resources and configurations which impact the whole org, or are shared across the hierarchy to other projects and teams. + +Its design is fairly general, and provides a reference example for [Cloud KMS](https://cloud.google.com/security-key-management) and a [VPC Service Controls](https://cloud.google.com/vpc-service-controls) configuration that sets up three perimeters (landing, development, production), the related bridge perimeters, and provides variables to configure their resources, access levels, and directional policies. + +Expanding this stage to include other security-related services like Secret Manager, is fairly simple by using the provided implementation for Cloud KMS, and leveraging the broad permissions on the top-level Security folder of the automation service account used. + +The following diagram illustrates the high level design of created resources, and a schema of the VPC SC design, which can be adapted to specific requirements via variables: + +![Security diagram](diagram.png) + +## Design overview and choices + +Project-level security resources are grouped into two separate projects, one per environment. This matches requirements we frequently observe in real life, and provides enough separation without needlessly complicating operations. + +Cloud KMS is configured and designed mainly to encrypt GCP resources with a [Customer-managed encryption key](https://cloud.google.com/kms/docs/cmek) but it may be used to create cryptokeys used to [encrypt application data](https://cloud.google.com/kms/docs/encrypting-application-data) too. + +IAM for management-related operations is already assigned at the folder level to the security team by the previous stage, but more granularity can of course be added here at the project level, to grant control of separate services across environments to different actors. + +### Cloud KMS + +A reference Cloud KMS implementation is part of this stage, to provide a simple way of managing centralized keys, that are then shared and consumed widely across the org to enable customer-managed encryption. The implementation is also easy to clone and modify to support other services like Secret Manager. + +The Cloud KMS configuration allows defining keys by name (typically matching the downstream service that uses them) in different locations, either based on a common default or a per-key setting, and then takes care internally of provisioning the relevant keyrings and creating keys in the appropriate location. + +IAM roles on keys can of course be configured at the logical level for all locations where a logical key is created, and their management can also be delegated via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) exposed through a simple variable, to allow other identities to set IAM policies on keys. This is particularly useful in setups like project factories, making it possible to configure IAM bindings during project creation for team groups or service agent accounts (compute, storage, etc.). + +### VPC Service Controls + +This stage also provisions the VPC Service Controls configuration on demand for the whole organization, implementing the straightforward design illustrated above: + +- one perimeter for each environment +- one perimeter for centralized services and the landing VPC +- bridge perimeters to connect the landing perimeter to each environment + +The VPC SC configuration is set to dry-run mode, but switching to enforced mode is a simple operation involving the modification of a few lines of code highlighted by ad-hoc comments. Variables are designed to enable easy centralized management of VPC Service Controls, including access levels and [ingress/egress rules](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules) as described below. + +Some care needs to be taken with project membership in perimeters, which can only be implemented here instead of being delegated (all or partially) to different stages, until the [Google Provider feature request](https://github.com/hashicorp/terraform-provider-google/issues/7270) that allows using project-level association for both enforced and dry-run modes is implemented. + +## How to run this stage + +This stage is meant to be executed after the [resouce management](../01-resman) stage has run, as it leverages the folder and automation resources created there. The relevant user groups must also exist, but that's one of the requirements for the previous stages too, so if you ran those successfully, you're good to go. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap stage for the actual roles needed. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in resource management, you can derive the providers file from that stage's outputs: + +```bash +cd ../01-resman +terraform output -json providers | jq -r '.["02-security"]' \ + > ../02-security/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 avalaible: + +```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 +``` + +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. + +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. The sections below also describe some of the possible customizations. + +Once done, you can run this stage: + +```bash +terraform init +terraform apply +``` + +## Customizations + +### KMS keys + +Cloud KMS configuration is split in two variables: + +- `kms_defaults` configures the locations and rotation period, used for keys that don't specifically configure them +- `kms_keys` configures the actual keys to create, and also allows configuring their IAM bindings and labels, and overriding locations and rotation period. When configuring locations for a key, please take into account limitations each cloud product may have. + +The additional `kms_restricted_admins` variable allows granting `roles/cloudkms.admin` to specified principals, restricted via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) so that it only allows granting the roles needed for encryption/decryption on keys. This allows safe delegation of key management to subsequent Terraform stages like the Project Factory, for example to grant usage access on relevant keys to the service agent accounts for compute, storage, etc. + +To support these scenarios, key IAM bindings are configured by default to be additive, so as to enable other stages or Terraform configuration to safely co-manage bindings on the same keys. If this is not desired, follow the comments in the `core-dev.tf` and `core-prod.tf` files to switch to using authoritative bindings on keys. + +An example on how to configure keys: + +```hcl +# terraform.tfvars + +kms_defaults = { + locations = ["europe-west1", "europe-west3", "global"] + rotation_period = "7776000s" +} +kms_keys = { + compute = { + iam = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "user:user1@example.com" + ] + } + labels = { service = "compute" } + locations = null + rotation_period = null + } + storage = { + iam = null + labels = { service = "compute" } + locations = ["europe"] + rotation_period = null + } +} +``` + +The script will create one keyring for each location specified and create necessarily keys on each keyring. + +### VPC Service Controls configuration + +A set of variables allows configuring the VPC SC perimeters described above: + +- `vpc_sc_perimeter_projects` configures project membership in the three regular perimeters +- `vpc_sc_access_levels` configures access levels, which can then be associated to perimeters by key using the `vpc_sc_perimeter_access_levels` +- `vpc_sc_egress_policies` configures directional egress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_egress_policies` +- `vpc_sc_ingress_policies` configures directional ingress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_ingress_policies` + +This allows configuring VPC SC in a fairly flexible and concise way, without having to repeat similar definitions. Bridges perimeters configuration will be computed automatically to allow communication between regular perimeters: `landing <-> prod` and `landing <-> dev`. + +#### Dry-run vs enforced + +The VPC SC configuration is set up by default in dry-run mode, to allow easy experimentation, and detecting violations before enforcement. Once everything has been set up correctly, switching to enforced mode needs to be done in code, by swapping the contents of the `spec` and `status` attributes for perimeters in the `vpc-sc.tf` file. The effort involved is minimal (2 lines of code per perimeter), and comments help with identifying the correct lines. + +#### Perimeter resources + +Project are added to perimeters via the `vpc_sc_perimeter_projects`, and that's currently the only way of doing it without generating permadiffs or conflicts, since the only Terraform resource that works for both enforced and dry-run mode is authoritative (perimeter resources need to be managed in a single place). + +Once the Google Terraform Provider [implements support for dry-run mode in the additive resource](https://github.com/hashicorp/terraform-provider-google/issues/7270), it will be possible to concurrently manage perimeter resources both here and in subsequent Terraform configurations, for example to allow the Project Factory to add a project to a perimeter during the creation process. + +Bridge perimeters are auto-populated with all projects configured for the connected regular perimeters. + +An example of adding projects to perimeters using project numbers: + +```hcl +# terraform.tfvars + +vpc_sc_perimeter_projects = { + dev = ["projects/12345678", "projects/12345679"] + landing = ["projects/12345670"] + prod = ["projects/12345674", "projects/12345675"] +} +``` + +#### Access levels + +Below an example for an access level that allows unconditional ingress from a set of IP CIDR ranges can be configured once, and enabled on selected perimeters: + +```hcl +# terraform.tfvars + +vpc_sc_access_levels = { + on-prem = { + conditions = [{ + ip_subnetworks = ["10.0.0.0/24", "10.0.0.1/24"], + combining_function = null, members = null, negate = null, + regions = null, required_access_levels = null + }] + } +} +vpc_sc_perimeter_access_levels = { + dev = null + landing = ["on-prem"] + prod = ["on-prem"] +} +``` + +#### Ingress and Egress policies + +The same applies to Ingress and Egress policies, as shown in the examples below that reference the automation service account for this stage. + +Below you can find an ingress policy configuration that allows applying Terraform from outside the perimeter, useful when bringing up this stage to avoid generating violations: + +```hcl +# terraform.tfvars + +vpc_sc_ingress_policies = { + iac = { + ingress_from = { + identities = [ + "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com" + ] + source_access_levels = ["*"] + identity_type = null + source_resources = null + } + ingress_to = { + operations = [{ method_selectors = [], service_name = "*" }] + resources = ["*"] + } + } +} +vpc_sc_perimeter_ingress_policies = { + dev = ["iac"] + landing = ["iac"] + prod = ["iac"] +} +``` + +Below you can find an egress policy that allows writing Terraform state to the automation bucket, useful once Terraform starts running inside the perimeter in a pipeline: + +```hcl +# terraform.tfvars + +vpc_sc_egress_policies = { + iac-gcs = { + egress_from = { + identity_type = null + identities = [ + "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + egress_to = { + operations = [{ + method_selectors = ["*"], service_name = "storage.googleapis.com" + }] + resources = ["projects/123456782"] + } + } +} +vpc_sc_perimeter_ingress_policies = { + dev = ["iac-gcs"] + landing = ["iac-gcs"] + prod = ["iac-gcs"] +} +``` + +## Notes + +Some references that might be useful in setting up this stage: + +- [VPC SC CSCC requirements](https://cloud.google.com/security-command-center/docs/troubleshooting). + + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [core-dev.tf](./core-dev.tf) | None | kms · project | google_project_iam_member | +| [core-prod.tf](./core-prod.tf) | None | kms · project | google_project_iam_member | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-sc.tf](./vpc-sc.tf) | None | vpc-sc | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | bootstrap | +| folder_id | Folder to be used for the networking resources in folders/nnnn format. | string | ✓ | | resman | +| organization | Organization details. | object({…}) | ✓ | | bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | bootstrap | +| kms_defaults | Defaults used for KMS keys. | object({…}) | | {…} | | +| kms_keys | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | map(object({…})) | | {} | | +| kms_restricted_admins | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | map(list(string)) | | {} | | +| outputs_location | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| vpc_sc_access_levels | VPC SC access level definitions. | map(object({…})) | | {} | | +| vpc_sc_egress_policies | VPC SC egress policy defnitions. | map(object({…})) | | {} | | +| vpc_sc_ingress_policies | VPC SC ingress policy defnitions. | map(object({…})) | | {} | | +| vpc_sc_perimeter_access_levels | VPC SC perimeter access_levels. | object({…}) | | null | | +| vpc_sc_perimeter_egress_policies | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| vpc_sc_perimeter_ingress_policies | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| vpc_sc_perimeter_projects | VPC SC perimeter resources. | object({…}) | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| stage_perimeter_projects | Security project numbers. They can be added to perimeter resources. | | | + + + + + + + + + + + + + diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf new file mode 100644 index 00000000..b0c78485 --- /dev/null +++ b/fast/stages/02-security/core-dev.tf @@ -0,0 +1,64 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "dev-sec-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "dev-sec-core-0" + parent = var.folder_id + prefix = var.prefix + billing_account = var.billing_account_id + iam = { + "roles/cloudkms.viewer" = try(var.kms_restricted_admins.dev, []) + } + labels = { environment = "dev", team = "security" } + services = local.project_services +} + +module "dev-sec-kms" { + for_each = toset(local.kms_locations) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + project_id = module.dev-sec-project.project_id + keyring = { + location = each.key + name = "dev-${each.key}" + } + # rename to `key_iam` to switch to authoritative bindings + key_iam_additive = { + for k, v in local.kms_locations_keys[each.key] : k => v.iam + } + keys = local.kms_locations_keys[each.key] +} + +# TODO(ludo): add support for conditions to Fabric modules + +resource "google_project_iam_member" "dev_key_admin_delegated" { + for_each = toset(try(var.kms_restricted_admins.dev, [])) + project = module.dev-sec-project.project_id + role = "roles/cloudkms.admin" + member = each.key + condition { + title = "kms_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/cloudkms.cryptoKeyEncrypterDecrypter", + "roles/cloudkms.cryptoKeyEncrypterDecrypterViaDelegation" + ])) + ) + } + depends_on = [module.dev-sec-project] +} diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf new file mode 100644 index 00000000..0524788d --- /dev/null +++ b/fast/stages/02-security/core-prod.tf @@ -0,0 +1,64 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "prod-sec-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "prod-sec-core-0" + parent = var.folder_id + prefix = var.prefix + billing_account = var.billing_account_id + iam = { + "roles/cloudkms.viewer" = try(var.kms_restricted_admins.prod, []) + } + labels = { environment = "prod", team = "security" } + services = local.project_services +} + +module "prod-sec-kms" { + for_each = toset(local.kms_locations) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + project_id = module.prod-sec-project.project_id + keyring = { + location = each.key + name = "prod-${each.key}" + } + # rename to `key_iam` to switch to authoritative bindings + key_iam_additive = { + for k, v in local.kms_locations_keys[each.key] : k => v.iam + } + keys = local.kms_locations_keys[each.key] +} + +# TODO(ludo): add support for conditions to Fabric modules + +resource "google_project_iam_member" "prod_key_admin_delegated" { + for_each = toset(try(var.kms_restricted_admins.prod, [])) + project = module.prod-sec-project.project_id + role = "roles/cloudkms.admin" + member = each.key + condition { + title = "kms_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/cloudkms.cryptoKeyEncrypterDecrypter", + "roles/cloudkms.cryptoKeyEncrypterDecrypterViaDelegation" + ])) + ) + } + depends_on = [module.prod-sec-project] +} diff --git a/fast/stages/02-security/diagram.png b/fast/stages/02-security/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..779c9039ec3d929903113b41c5f0f8b29084291d GIT binary patch literal 93474 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 Y=Y$Le`ng527~r3>yt>?n`>-efA88TRF#rGn literal 0 HcmV?d00001 diff --git a/fast/stages/02-security/main.tf b/fast/stages/02-security/main.tf new file mode 100644 index 00000000..13078d12 --- /dev/null +++ b/fast/stages/02-security/main.tf @@ -0,0 +1,47 @@ +/** + * 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 { + kms_keys = { + for k, v in var.kms_keys : k => { + iam = coalesce(v.iam, {}) + labels = coalesce(v.labels, {}) + locations = ( + v.locations == null + ? var.kms_defaults.locations + : v.locations + ) + rotation_period = ( + v.rotation_period == null + ? var.kms_defaults.rotation_period + : v.rotation_period + ) + } + } + kms_locations = distinct(flatten([ + for k, v in local.kms_keys : v.locations + ])) + kms_locations_keys = { + for loc in local.kms_locations : loc => { + for k, v in local.kms_keys : k => v if contains(v.locations, loc) + } + } + project_services = [ + "cloudkms.googleapis.com", + "secretmanager.googleapis.com", + "stackdriver.googleapis.com" + ] +} diff --git a/fast/stages/02-security/outputs.tf b/fast/stages/02-security/outputs.tf new file mode 100644 index 00000000..8f296d86 --- /dev/null +++ b/fast/stages/02-security/outputs.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 + * + * 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. + */ + +# optionally generate files for subsequent stages + +resource "local_file" "dev_sec_kms" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + filename = "${var.outputs_location}/yamls/02-security-kms-dev-keys.yaml" + content = yamlencode({ + for k, m in module.dev-sec-kms : k => m.key_ids + }) +} + +resource "local_file" "prod_sec_kms" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + filename = "${var.outputs_location}/yamls/02-security-kms-prod-keys.yaml" + content = yamlencode({ + for k, m in module.prod-sec-kms : k => m.key_ids + }) +} + +# outputs + +output "stage_perimeter_projects" { + description = "Security project numbers. They can be added to perimeter resources." + value = { + dev = ["projects/${module.dev-sec-project.number}"] + prod = ["projects/${module.prod-sec-project.number}"] + } +} diff --git a/fast/stages/02-security/variables.tf b/fast/stages/02-security/variables.tf new file mode 100644 index 00000000..0829f098 --- /dev/null +++ b/fast/stages/02-security/variables.tf @@ -0,0 +1,185 @@ +/** + * 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 "billing_account_id" { + # tfdoc:variable:source bootstrap + description = "Billing account id." + type = string +} + +variable "folder_id" { + # tfdoc:variable:source resman + description = "Folder to be used for the networking resources in folders/nnnn format." + type = string +} + +variable "groups" { + # tfdoc:variable:source bootstrap + description = "Group names to grant organization-level permissions." + type = map(string) + # https://cloud.google.com/docs/enterprise/setup-checklist + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "kms_defaults" { + description = "Defaults used for KMS keys." + type = object({ + locations = list(string) + rotation_period = string + }) + default = { + locations = ["europe", "europe-west1", "europe-west3", "global"] + rotation_period = "7776000s" + } +} + +variable "kms_keys" { + description = "KMS keys to create, keyed by name. Null attributes will be interpolated with defaults." + type = map(object({ + iam = map(list(string)) + labels = map(string) + locations = list(string) + rotation_period = string + })) + default = {} +} + +variable "kms_restricted_admins" { + description = "Map of environment => [identities] who can assign the encrypt/decrypt roles on keys." + type = map(list(string)) + default = {} +} + +variable "organization" { + # tfdoc:variable:source bootstrap + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "outputs_location" { + description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "vpc_sc_access_levels" { + description = "VPC SC access level definitions." + type = map(object({ + combining_function = string + conditions = list(object({ + ip_subnetworks = list(string) + members = list(string) + negate = bool + regions = list(string) + required_access_levels = list(string) + })) + })) + default = {} +} + +variable "vpc_sc_egress_policies" { + description = "VPC SC egress policy defnitions." + type = map(object({ + egress_from = object({ + identity_type = string + identities = list(string) + }) + egress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + default = {} +} + +variable "vpc_sc_ingress_policies" { + description = "VPC SC ingress policy defnitions." + type = map(object({ + ingress_from = object({ + identity_type = string + identities = list(string) + source_access_levels = list(string) + source_resources = list(string) + }) + ingress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + default = {} +} + +variable "vpc_sc_perimeter_access_levels" { + description = "VPC SC perimeter access_levels." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_egress_policies" { + description = "VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_ingress_policies" { + description = "VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_projects" { + description = "VPC SC perimeter resources." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} diff --git a/fast/stages/02-security/vpc-sc-restricted-services.yaml b/fast/stages/02-security/vpc-sc-restricted-services.yaml new file mode 100644 index 00000000..89844cd2 --- /dev/null +++ b/fast/stages/02-security/vpc-sc-restricted-services.yaml @@ -0,0 +1,88 @@ +# skip boilerplate check +- accessapproval.googleapis.com +- adsdatahub.googleapis.com +- aiplatform.googleapis.com +- alpha-documentai.googleapis.com +- apigee.googleapis.com +- apigeeconnect.googleapis.com +- artifactregistry.googleapis.com +- assuredworkloads.googleapis.com +- automl.googleapis.com +- bigquery.googleapis.com +- bigquerydatatransfer.googleapis.com +- bigtable.googleapis.com +- binaryauthorization.googleapis.com +- cloudasset.googleapis.com +- cloudbuild.googleapis.com +- cloudfunctions.googleapis.com +- cloudkms.googleapis.com +- cloudprofiler.googleapis.com +- cloudresourcemanager.googleapis.com +- cloudsearch.googleapis.com +- cloudtrace.googleapis.com +- composer.googleapis.com +- compute.googleapis.com +- connectgateway.googleapis.com +- contactcenterinsights.googleapis.com +- container.googleapis.com +- containeranalysis.googleapis.com +- containerregistry.googleapis.com +- containerthreatdetection.googleapis.com +- datacatalog.googleapis.com +- dataflow.googleapis.com +- datafusion.googleapis.com +- dataproc.googleapis.com +- datastream.googleapis.com +- dialogflow.googleapis.com +- dlp.googleapis.com +- dns.googleapis.com +- documentai.googleapis.com +- eventarc.googleapis.com +- file.googleapis.com +- gameservices.googleapis.com +- gkeconnect.googleapis.com +- gkehub.googleapis.com +- healthcare.googleapis.com +- iam.googleapis.com +- iaptunnel.googleapis.com +- language.googleapis.com +- lifesciences.googleapis.com +- logging.googleapis.com +- managedidentities.googleapis.com +- memcache.googleapis.com +- meshca.googleapis.com +- metastore.googleapis.com +- ml.googleapis.com +- monitoring.googleapis.com +- networkconnectivity.googleapis.com +- networkmanagement.googleapis.com +- networksecurity.googleapis.com +- networkservices.googleapis.com +- notebooks.googleapis.com +- opsconfigmonitoring.googleapis.com +- osconfig.googleapis.com +- oslogin.googleapis.com +- privateca.googleapis.com +- pubsub.googleapis.com +- pubsublite.googleapis.com +- recaptchaenterprise.googleapis.com +- recommender.googleapis.com +- redis.googleapis.com +- run.googleapis.com +- secretmanager.googleapis.com +- servicecontrol.googleapis.com +- servicedirectory.googleapis.com +- spanner.googleapis.com +- speakerid.googleapis.com +- speech.googleapis.com +- sqladmin.googleapis.com +- storage.googleapis.com +- storagetransfer.googleapis.com +- texttospeech.googleapis.com +- tpu.googleapis.com +- trafficdirector.googleapis.com +- transcoder.googleapis.com +- translate.googleapis.com +- videointelligence.googleapis.com +- vision.googleapis.com +- vpcaccess.googleapis.com diff --git a/fast/stages/02-security/vpc-sc.tf b/fast/stages/02-security/vpc-sc.tf new file mode 100644 index 00000000..f22added --- /dev/null +++ b/fast/stages/02-security/vpc-sc.tf @@ -0,0 +1,167 @@ +/** + * 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 { + # compute the number of projects in each perimeter to detect which to create + vpc_sc_counts = { + for k in ["dev", "landing", "prod"] : k => length( + coalesce(try(var.vpc_sc_perimeter_projects[k], null), []) + ) + } + # dereference perimeter egress policy names to the actual objects + vpc_sc_perimeter_egress_policies = { + for k, v in coalesce(var.vpc_sc_perimeter_egress_policies, {}) : + k => [ + for i in coalesce(v, []) : var.vpc_sc_egress_policies[i] + if lookup(var.vpc_sc_egress_policies, i, null) != null + ] + } + # dereference perimeter ingress policy names to the actual objects + vpc_sc_perimeter_ingress_policies = { + for k, v in coalesce(var.vpc_sc_perimeter_ingress_policies, {}) : + k => [ + for i in coalesce(v, []) : var.vpc_sc_ingress_policies[i] + if lookup(var.vpc_sc_ingress_policies, i, null) != null + ] + } + # get the list of restricted services from the yaml file + vpcsc_restricted_services = yamldecode( + file("${path.module}/vpc-sc-restricted-services.yaml") + ) +} + +module "vpc-sc" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/vpc-sc?ref=ea17e65" + # only enable if we have projects defined for perimeters + count = anytrue([for k, v in local.vpc_sc_counts : v > 0]) ? 1 : 0 + access_policy = null + access_policy_create = { + parent = "organizations/${var.organization.id}" + title = "default" + } + access_levels = coalesce(try(var.vpc_sc_access_levels, null), {}) + # bridge type perimeters + service_perimeters_bridge = merge( + # landing to dev, only we have projects in landing and dev perimeters + local.vpc_sc_counts.landing * local.vpc_sc_counts.dev == 0 ? {} : { + landing_to_dev = { + status_resources = null + spec_resources = concat( + var.vpc_sc_perimeter_projects.landing, + var.vpc_sc_perimeter_projects.dev + ) + use_explicit_dry_run_spec = true + } + }, + # landing to prod, only we have projects in landing and prod perimeters + local.vpc_sc_counts.landing * local.vpc_sc_counts.prod == 0 ? {} : { + landing_to_prod = { + status_resources = null + spec_resources = concat( + var.vpc_sc_perimeter_projects.landing, + var.vpc_sc_perimeter_projects.prod + ) + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + } + ) + # regular type perimeters + service_perimeters_regular = merge( + # dev if we have projects in var.vpc_sc_perimeter_projects.dev + local.vpc_sc_counts.dev == 0 ? {} : { + dev = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.dev, null), [] + ) + resources = var.vpc_sc_perimeter_projects.dev + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.dev, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.dev, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + }, + # prod if we have projects in var.vpc_sc_perimeter_projects.prod + local.vpc_sc_counts.prod == 0 ? {} : { + prod = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.prod, null), [] + ) + # combine the security project, and any specified in the variable + resources = var.vpc_sc_perimeter_projects.prod + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.prod, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.prod, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + }, + # prod if we have projects in var.vpc_sc_perimeter_projects.prod + local.vpc_sc_counts.landing == 0 ? {} : { + landing = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.landing, null), [] + ) + resources = var.vpc_sc_perimeter_projects.landing + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.landing, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.landing, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + } + ) +} From cee207b4544cfe2bc2eb517fd91c79952e3052b3 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:33:51 +0100 Subject: [PATCH 007/132] Import Fast from dev repository. Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- .gitignore | 5 + .../assets/schemas/firewall_rules.schema.yaml | 15 ++ .../schemas/hierarchical_rules.schema.yaml | 11 + fast/assets/schemas/project.schema.yaml | 43 ++++ .../schemas/project_defaults.schema.yaml | 14 ++ fast/assets/schemas/subnet.schema.yaml | 15 ++ fast/assets/templates/providers.tpl | 30 +++ fast/stages/01-resman/README.md | 193 ++++++++++++++++++ fast/stages/01-resman/billing.tf | 57 ++++++ fast/stages/01-resman/branch-gke.tf | 144 +++++++++++++ fast/stages/01-resman/branch-networking.tf | 59 ++++++ fast/stages/01-resman/branch-sandbox.tf | 59 ++++++ fast/stages/01-resman/branch-security.tf | 61 ++++++ fast/stages/01-resman/branch-teams.tf | 185 +++++++++++++++++ fast/stages/01-resman/diagram.png | Bin 0 -> 109088 bytes fast/stages/01-resman/main.tf | 35 ++++ fast/stages/01-resman/organization.tf | 144 +++++++++++++ fast/stages/01-resman/outputs.tf | 166 +++++++++++++++ fast/stages/01-resman/providers.tf | 1 + fast/stages/01-resman/variables.tf | 104 ++++++++++ tests/fast/stages/__init__.py | 13 ++ tests/fast/stages/s00_bootstrap/__init__.py | 13 ++ .../fast/stages/s00_bootstrap/fixture/main.tf | 29 +++ tests/fast/stages/s00_bootstrap/test_plan.py | 33 +++ tests/fast/stages/s01_resman/__init__.py | 13 ++ tests/fast/stages/s01_resman/fixture/main.tf | 42 ++++ tests/fast/stages/s01_resman/test_plan.py | 20 ++ tests/fast/stages/s02_networking/__init__.py | 13 ++ tests/fast/stages/s02_networking/fixture/data | 1 + .../stages/s02_networking/fixture/main.tf | 30 +++ tests/fast/stages/s02_networking/test_plan.py | 20 ++ tests/fast/stages/s02_security/__init__.py | 13 ++ tests/fast/stages/s02_security/fixture/data | 1 + .../fast/stages/s02_security/fixture/main.tf | 109 ++++++++++ tests/fast/stages/s02_security/test_plan.py | 20 ++ .../stages/s03_project_factory/__init__.py | 13 ++ .../fixture/data/defaults.yaml | 22 ++ .../fixture/data/projects/project.yaml | 98 +++++++++ .../s03_project_factory/fixture/main.tf | 57 ++++++ .../terraform-bootstrap.auto.tfvars.json | 4 + .../terraform-networking.auto.tfvars.json | 5 + .../s03_project_factory/fixture/variables.tf | 61 ++++++ .../stages/s03_project_factory/test_plan.py | 20 ++ 43 files changed, 1991 insertions(+) create mode 100644 fast/assets/schemas/firewall_rules.schema.yaml create mode 100644 fast/assets/schemas/hierarchical_rules.schema.yaml create mode 100644 fast/assets/schemas/project.schema.yaml create mode 100644 fast/assets/schemas/project_defaults.schema.yaml create mode 100644 fast/assets/schemas/subnet.schema.yaml create mode 100644 fast/assets/templates/providers.tpl create mode 100644 fast/stages/01-resman/README.md create mode 100644 fast/stages/01-resman/billing.tf create mode 100644 fast/stages/01-resman/branch-gke.tf create mode 100644 fast/stages/01-resman/branch-networking.tf create mode 100644 fast/stages/01-resman/branch-sandbox.tf create mode 100644 fast/stages/01-resman/branch-security.tf create mode 100644 fast/stages/01-resman/branch-teams.tf create mode 100644 fast/stages/01-resman/diagram.png create mode 100644 fast/stages/01-resman/main.tf create mode 100644 fast/stages/01-resman/organization.tf create mode 100644 fast/stages/01-resman/outputs.tf create mode 120000 fast/stages/01-resman/providers.tf create mode 100644 fast/stages/01-resman/variables.tf create mode 100644 tests/fast/stages/__init__.py create mode 100644 tests/fast/stages/s00_bootstrap/__init__.py create mode 100644 tests/fast/stages/s00_bootstrap/fixture/main.tf create mode 100644 tests/fast/stages/s00_bootstrap/test_plan.py create mode 100644 tests/fast/stages/s01_resman/__init__.py create mode 100644 tests/fast/stages/s01_resman/fixture/main.tf create mode 100644 tests/fast/stages/s01_resman/test_plan.py create mode 100644 tests/fast/stages/s02_networking/__init__.py create mode 120000 tests/fast/stages/s02_networking/fixture/data create mode 100644 tests/fast/stages/s02_networking/fixture/main.tf create mode 100644 tests/fast/stages/s02_networking/test_plan.py create mode 100644 tests/fast/stages/s02_security/__init__.py create mode 120000 tests/fast/stages/s02_security/fixture/data create mode 100644 tests/fast/stages/s02_security/fixture/main.tf create mode 100644 tests/fast/stages/s02_security/test_plan.py create mode 100644 tests/fast/stages/s03_project_factory/__init__.py create mode 100644 tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml create mode 100644 tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml create mode 100644 tests/fast/stages/s03_project_factory/fixture/main.tf create mode 100644 tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json create mode 100644 tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json create mode 100644 tests/fast/stages/s03_project_factory/fixture/variables.tf create mode 100644 tests/fast/stages/s03_project_factory/test_plan.py diff --git a/.gitignore b/.gitignore index 0f0dea4f..d1a5e47c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ bundle.zip **/packer_cache **/*.pkrvars.hcl fixture_* +fast/configs +fast/stages/**/providers.tf +fast/stages/**/terraform.tfvars +fast/stages/**/terraform.tfvars.json +fast/stages/**/terraform-*.auto.tfvars.json diff --git a/fast/assets/schemas/firewall_rules.schema.yaml b/fast/assets/schemas/firewall_rules.schema.yaml new file mode 100644 index 00000000..ebb62772 --- /dev/null +++ b/fast/assets/schemas/firewall_rules.schema.yaml @@ -0,0 +1,15 @@ +map(include('firewall_rule')) +--- +firewall_rule: + description: str() + direction: enum("INGRESS", "EGRESS") + action: enum("allow", "deny") + sources: list(str()) + ranges: list(str()) + targets: list(str()) + use_service_accounts: bool() + rules: list(include('rule')) +--- +rule: + protocol: enum("tcp", "udp", "all") + ports: list(num()) diff --git a/fast/assets/schemas/hierarchical_rules.schema.yaml b/fast/assets/schemas/hierarchical_rules.schema.yaml new file mode 100644 index 00000000..d8c72b1d --- /dev/null +++ b/fast/assets/schemas/hierarchical_rules.schema.yaml @@ -0,0 +1,11 @@ +map(include('hierarchical_rule')) +--- +hierarchical_rule: + description: str() + direction: enum("INGRESS", "EGRESS") + action: enum("allow", "deny") + priority: int() + ranges: list(str()) + target_resources: any(null(), list(str())) + ports: map(list(str(), required=False)) + enable_logging: bool() diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml new file mode 100644 index 00000000..e914e5a7 --- /dev/null +++ b/fast/assets/schemas/project.schema.yaml @@ -0,0 +1,43 @@ +billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) +billing_alert: any(include('billing_alert'), null(), required=False) # If set to null, use defaults +dns_zones: list(str(), required=False) +essential_contacts: list(str(), required=False) # Also used for billing alerts +folder_id: str(matches='(organizations/|folders/)[0-9]*$') +group_iam: map(list(str()), key=str(), required=False) +iam: map(list(str()), key=str(), required=False) +kms_service_agents: map(list(str()), key=str(), required=False) +labels: map(str(), key=str(), required=False) +org_policies: include('org_policies', required=False) +secrets: map(list(str()), key=str(), required=False) +service_accounts: map(list(str()), required=False) +services: list(str(matches='^[a-z-]*\.googleapis\.com$'), required=False) +services_iam: map(list(str()), key=str(), required=False) +vpc: include('vpc', required=False) +--- +billing_alert: + amount: int() + thresholds: include('billing_alert_thresholds') + credit_treatment: enum("INCLUDE_ALL_CREDITS", "EXCLUDE_ALL_CREDITS") +--- +billing_alert_thresholds: + current: list(num(min=0, max=1)) + forecasted: list(num(min=0, max=1)) +--- +gke_setup: + enable_security_admin: bool(required=False) + enable_host_service_agent: bool(required=False) +--- +org_policies: + policy_boolean: map(bool(), key=str(matches='^constraints/[A-z\.]*$'), required=False) + policy_list: map(include('policy_list'), key=str(matches='^constraints/[A-z\.]*$'), required=False) +--- +policy_list: + inherit_from_parent: any(bool(), null()) + suggested_value: any(str(), null()) + status: any(bool(), null()) + values: list(str()) +--- +vpc: + host_project: str(matches='[a-z]([-a-z0-9]*[a-z0-9])?', min=6, max=30) + gke_setup: include('gke_setup', required=False) + subnets_iam: map(list(str()), key=str(matches='^[a-z0-9-]*/[a-z0-9-]*$'), required=False) diff --git a/fast/assets/schemas/project_defaults.schema.yaml b/fast/assets/schemas/project_defaults.schema.yaml new file mode 100644 index 00000000..52676baa --- /dev/null +++ b/fast/assets/schemas/project_defaults.schema.yaml @@ -0,0 +1,14 @@ +billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) +billing_alert: any(include('billing_alert'), null(), required=False) +essential_contacts: list(str(), required=False) +labels: map(str(), key=str(), required=False) +notification_channels: list(str(), required=False) +--- +billing_alert: + amount: int() + thresholds: include('billing_alert_thresholds') + credit_treatment: enum('INCLUDE_ALL_CREDITS', 'EXCLUDE_ALL_CREDITS') +--- +billing_alert_thresholds: + current: list(num(min=0, max=1)) + forecasted: list(num(min=0, max=1)) diff --git a/fast/assets/schemas/subnet.schema.yaml b/fast/assets/schemas/subnet.schema.yaml new file mode 100644 index 00000000..1bf10ee8 --- /dev/null +++ b/fast/assets/schemas/subnet.schema.yaml @@ -0,0 +1,15 @@ +region: str() +description: str() +ip_cidr_range: str() +# optional attributes +private_ip_google_access: bool(required=False) # defaults to true +iam_users: list(str(), required=False) +iam_groups: list(str(), required=False) +iam_service_accounts: list(str(), required=False) +secondary_ip_ranges: list(map(str()), key=str(), required=False) +flow_logs: any(include('flow_logs'), required=False) +--- +flow_logs: + - aggregation_interval: enum('INTERVAL_5_SEC', 'INTERVAL_30_SEC', 'INTERVAL_1_MIN', 'INTERVAL_5_MIN', 'INTERVAL_10_MIN', 'INTERVAL_15_MIN', required=False) + - flow_sampling: num(min=0, max=1, required=False) + - metadata: enum('EXCLUDE_ALL_METADATA', 'INCLUDE_ALL_METADATA', 'CUSTOM_METADATA', required=False) diff --git a/fast/assets/templates/providers.tpl b/fast/assets/templates/providers.tpl new file mode 100644 index 00000000..7f0ce142 --- /dev/null +++ b/fast/assets/templates/providers.tpl @@ -0,0 +1,30 @@ +/** + * 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. + */ + +terraform { + backend "gcs" { + bucket = "${bucket}" + impersonate_service_account = "${sa}" + } +} +provider "google" { + impersonate_service_account = "${sa}" +} +provider "google-beta" { + impersonate_service_account = "${sa}" +} + +# end provider.tf for ${name} diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md new file mode 100644 index 00000000..966e2184 --- /dev/null +++ b/fast/stages/01-resman/README.md @@ -0,0 +1,193 @@ +# Resource hierarchy + +This stage performs two important tasks: + +- create the top-level hierarchy of folders, and the associated resources used later on to automate each part of the hierarchy (eg. Networking) +- set organization policies on the organization, and any exception required on specific folders + +The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. + +The following diagram is a high level reference of the resources created and managed here: + +![Resource-management diagram](diagram.png) + +## Design overview and choices + +Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers, where the hierarchy is laid out following two conceptually different approaches: + +- core or shared resources are grouped in hierarchy branches that map to their type or purpose (e.g. Networking) +- team or application resources are grouped in lower level hierarchy branches that map to management or operational considerations (e.g. which team manages a set of applications, or owns a subset of company data, etc.) + +This split approach usually represents well functional and operational patterns, where core resources are centrally managed by individual teams (e.g. networking, security, fleets of similar VMS, etc.), while teams need more granularity to access managed services used by the applications they maintain. + +The approach also adapts to different high level requirements: + +- it can be used either for single organizations containing multiple environments, or with multiple organizations dedicated to specific environments (e.g. prod/nonprod), as the environment split is implemented at the project or lower folder level +- it adapts to complex scenarios, with different countries or corporate entities using the same GCP organization, as core services are typically shared, and/or an extra layer on top can be used as a drop-in to implement the country/entity separation + +Additionally, a few critical benefits are directly provided by this design: + +- core services are clearly separated, with very few touchpoints where IAM and security policies need to be applied (typically their top-level folder) +- adding a new set of core services (e.g. shared GKE clusters) is a trivial operation that does not break the existing design +- grouping application resources and services using teams or business logic is a flexible approach, which maps well to typical operational or budget requirements +- automation stages (e.g. Networking) can be segregated in a simple and effective way, by creating the required service accounts and buckets for each stage here, and applying a handful of IAM roles to the relevant folder + +For a discussion on naming, please refer to the [Bootstrap stage documentation](../00-bootstrap/README.md#naming), as the same approach is shared by all stages. + +## How to run this stage + +This stage is meant to be executed after the [bootstrap](../00-bootstrap) stage has run, as it leverages the automation service account and bucket created there. The relevant user groups must also exist, but that's one of the requirements for the previous stage too, so if you ran that successfully, you're good to go. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap stage for the actual roles needed. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: + +```bash +cd ../00-bootstrap +terraform output -json providers | jq -r '.["01-resman"]' \ + > ../01-resman/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +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. + +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. The sections below also describe some of the possible customizations. For billing configurations, refer to the [Bootstrap documentation on billing](../00-bootstrap/README.md#billing-account) as the `billing_account` variable is identical across all stages. + +Once done, you can run this stage: + +```bash +terraform init +terraform apply +``` + +## Customizations + +### Team folders + +This stage provides a single built-in customization that offers a minimal (but usable) implementation of the "application" or "business" grouping for resources discussed above. The `team_folders` variable allows you to specify a map of team name and groups, that will result in folders, automation service accounts, and IAM policies applied. + +Consider the following example + +```hcl +team_folders = { + team-a = { + descriptive_name = "Team A" + group_iam = { + "team-a@gcp-pso-italy.net" = [ + "roles/viewer" + ] + } + impersonation_groups = ["team-a-admins@gcp-pso-italy.net"] + } +} +``` + +This will result in + +- a "Team A" folder under the "Teams" folder +- one GCS bucket in the automation project +- one service account in the automation project with the correct IAM policies on the folder and bucket +- a IAM policy on the folder that assigns `roles/viewer` to the `team-a` group +- a IAM policy on the service account that allows `team-a` to impersonate it + +This allows to centralize the minimum set of resources to delegate control of each team's folder to a pipeline, and/or to the team group. This can be used as a starting point for scenarios that implement more complex requirements (e.g. environment folders per team, etc.). + +### Organization policies + +Organization policies are laid out in an explicit manner in the `organization.tf` file, so it's fairly easy to add or remove specific policies. + +For policies where additional data is needed, a root-level `organization_policy_configs` variable allows passing in specific data. Its built-in use to add additional organizations to the [Domain Restricted Sharing](https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains) policy, can be taken as an example on how to leverage it for additional customizations. + +### IAM + +IAM roles can be easily edited in the relevant `branch-xxx.tf` file, following the best practice outlined in the [bootstrap stage](../00-bootstrap#customizations) documentation of separating user-level and service-account level IAM policies in modules' `iam_groups`, `iam`, and `iam_additive` variables. + +### Additional folders + +Due to its simplicity, this stage lends itself easily to customizations: adding a new top-level branch (e.g. for shared GKE clusters) is as easy as cloning one of the `branch-xxx.tf` files, and changing names. + + + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [billing.tf](./billing.tf) | Billing resources for external billing use cases. | organization | google_billing_account_iam_member | +| [branch-gke.tf](./branch-gke.tf) | GKE stage resources. | folder · gcs · iam-service-account | | +| [branch-networking.tf](./branch-networking.tf) | Networking stage resources. | folder · gcs · iam-service-account | | +| [branch-sandbox.tf](./branch-sandbox.tf) | Sandbox stage resources. | folder · gcs · iam-service-account | | +| [branch-security.tf](./branch-security.tf) | Security stage resources. | folder · gcs · iam-service-account | | +| [branch-teams.tf](./branch-teams.tf) | Team stages resources. | folder · gcs · iam-service-account | | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [organization.tf](./organization.tf) | Organization policies. | organization | | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| automation_project_id | Project id for the automation project created by the bootstrap stage. | string | ✓ | | 00-bootstrap | +| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_roles | Custom roles defined at the org level, in key => id format. | map(string) | | {} | 00-bootstrap | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | +| organization_policy_configs | Organization policies customization. | object({…}) | | null | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| team_folders | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| networking | Data for the networking stage. | | 02-networking | +| project_factories | Data for the project factories stage. | | xx-teams | +| providers | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · xx-sandbox · xx-teams | +| sandbox | Data for the sandbox stage. | | xx-sandbox | +| security | Data for the networking stage. | | 02-security | +| teams | Data for the teams stage. | | | +| tfvars | Terraform variable files for the following stages. | ✓ | | + + + + + + + + + + diff --git a/fast/stages/01-resman/billing.tf b/fast/stages/01-resman/billing.tf new file mode 100644 index 00000000..ae753177 --- /dev/null +++ b/fast/stages/01-resman/billing.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Billing resources for external billing use cases. + +locals { + # used here for convenience, in organization.tf members are explicit + billing_ext_users = concat( + [ + module.branch-network-sa.iam_email, + module.branch-security-sa.iam_email, + ], + # enable if individual teams can create their own projects + # [ + # for k, v in module.branch-teams-team-sa : v.iam_email + # ], + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) +} + +# billing account in same org (resources is in the organization.tf file) + +# billing account in a different org + +module "billing-organization-ext" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + count = local.billing_org_ext ? 1 : 0 + organization_id = "organizations/${var.billing_account.organization_id}" + iam_additive = { + "roles/billing.user" = local.billing_ext_users + } +} + +# standalone billing account + +resource "google_billing_account_iam_member" "billing_ext_admin" { + for_each = toset( + local.billing_ext ? local.billing_ext_users : [] + ) + billing_account_id = var.billing_account.id + role = "roles/billing.user" + member = each.key +} diff --git a/fast/stages/01-resman/branch-gke.tf b/fast/stages/01-resman/branch-gke.tf new file mode 100644 index 00000000..d7681995 --- /dev/null +++ b/fast/stages/01-resman/branch-gke.tf @@ -0,0 +1,144 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description GKE stage resources. + +locals { + gke_branch_group_iam = { + (local.groups.gcp-devops) = [ + "roles/viewer", + # ... + ] + } +} + +# top-level GKE folder + +module "branch-gke-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "GKE" +} + +# environment: development folder and automation resources + +moved { + from = module.branch-gke-env-folder["dev"] + to = module.branch-gke-dev-folder +} + +module "branch-gke-dev-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + # naming: environment descriptive name + name = "Development" + parent = module.branch-gke-folder.id + group_iam = local.gke_branch_group_iam + iam = { + "roles/logging.admin" = [ + module.branch-gke-dev-sa.iam_email + ] + "roles/owner" = [ + module.branch-gke-dev-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-gke-dev-sa.iam_email + ] + } +} + +moved { + from = module.branch-gke-env-sa["dev"] + to = module.branch-gke-dev-sa +} + +module "branch-gke-dev-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + description = "Terraform GKE development service account." + prefix = local.prefixes.dev +} + +moved { + from = module.branch-gke-gcs["dev"] + to = module.branch-gke-dev-gcs +} + +module "branch-gke-dev-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gke-dev-sa.iam_email] + } +} + +# environment: production folder and automation resources + +moved { + from = module.branch-gke-env-folder["prod"] + to = module.branch-gke-prod-folder +} + +module "branch-gke-prod-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + # naming: environment descriptive name + name = "Production" + parent = module.branch-gke-folder.id + group_iam = local.gke_branch_group_iam + iam = { + "roles/logging.admin" = [ + module.branch-gke-prod-sa.iam_email + ] + "roles/owner" = [ + module.branch-gke-prod-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-gke-prod-sa.iam_email + ] + } +} + +moved { + from = module.branch-gke-env-sa["prod"] + to = module.branch-gke-prod-sa +} + +module "branch-gke-prod-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + description = "Terraform GKE production service account." + prefix = local.prefixes.prod +} + +moved { + from = module.branch-gke-gcs["prod"] + to = module.branch-gke-prod-gcs +} + +module "branch-gke-prod-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gke-prod-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-networking.tf b/fast/stages/01-resman/branch-networking.tf new file mode 100644 index 00000000..ef95ecf3 --- /dev/null +++ b/fast/stages/01-resman/branch-networking.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Networking stage resources. + +module "branch-network-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Networking" + group_iam = { + (local.groups.gcp-network-admins) = [ + # add any needed roles for resources/services not managed via Terraform, + # or replace editor with ~viewer if no broad resource management needed + # e.g. + # "roles/compute.networkAdmin", + # "roles/dns.admin", + # "roles/compute.securityAdmin", + "roles/editor", + ] + } + iam = { + "roles/logging.admin" = [module.branch-network-sa.iam_email] + "roles/owner" = [module.branch-network-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-network-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-network-sa.iam_email] + } +} + +module "branch-network-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-networking-0" + description = "Terraform resman networking service account." + prefix = local.prefixes.prod +} + +module "branch-network-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-networking-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-network-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-sandbox.tf b/fast/stages/01-resman/branch-sandbox.tf new file mode 100644 index 00000000..c9f244f5 --- /dev/null +++ b/fast/stages/01-resman/branch-sandbox.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Sandbox stage resources. + +module "branch-sandbox-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Sandbox" + iam = { + "roles/logging.admin" = [module.branch-sandbox-sa.iam_email] + "roles/owner" = [module.branch-sandbox-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-sandbox-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-sandbox-sa.iam_email] + } + policy_boolean = { + "constraints/sql.restrictPublicIp" = false + } + policy_list = { + "constraints/compute.vmExternalIpAccess" = { + inherit_from_parent = false + suggested_value = null + status = true + values = [] + } + } +} + +module "branch-sandbox-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-sandbox-0" + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-sandbox-sa.iam_email] + } +} + +module "branch-sandbox-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-sandbox-0" + description = "Terraform resman sandbox service account." + prefix = local.prefixes.dev +} diff --git a/fast/stages/01-resman/branch-security.tf b/fast/stages/01-resman/branch-security.tf new file mode 100644 index 00000000..9f6a12ff --- /dev/null +++ b/fast/stages/01-resman/branch-security.tf @@ -0,0 +1,61 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Security stage resources. + +module "branch-security-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Security" + group_iam = { + (local.groups.gcp-security-admins) = [ + # add any needed roles for resources/services not managed via Terraform, + # e.g. + # "roles/bigquery.admin", + # "roles/cloudasset.owner", + # "roles/cloudkms.admin", + # "roles/logging.admin", + # "roles/secretmanager.admin", + # "roles/storage.admin", + "roles/viewer" + ] + } + iam = { + "roles/logging.admin" = [module.branch-security-sa.iam_email] + "roles/owner" = [module.branch-security-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-security-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email] + } +} + +module "branch-security-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-security-0" + description = "Terraform resman security service account." + prefix = local.prefixes.prod +} + +module "branch-security-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-security-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-security-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-teams.tf b/fast/stages/01-resman/branch-teams.tf new file mode 100644 index 00000000..757c23ad --- /dev/null +++ b/fast/stages/01-resman/branch-teams.tf @@ -0,0 +1,185 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Team stages resources. + +# top-level teams folder and service account + +module "branch-teams-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Teams" +} + +module "branch-teams-prod-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-teams-0" + description = "Terraform resman production service account." + prefix = local.prefixes.prod +} + +# Team-level folders, service accounts and buckets for each individual team + +module "branch-teams-team-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-folder.id + name = each.value.descriptive_name + group_iam = each.value.group_iam == null ? {} : each.value.group_iam +} + +module "branch-teams-team-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + project_id = var.automation_project_id + name = "teams-${each.key}-0" + description = "Terraform team ${each.key} service account." + prefix = local.prefixes.prod + iam = { + "roles/iam.serviceAccountTokenCreator" = ( + each.value.impersonation_groups == null + ? [] + : [for g in each.value.impersonation_groups : "group:${g}"] + ) + } +} + +module "branch-teams-team-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + project_id = var.automation_project_id + name = "teams-${each.key}-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-team-sa[each.key].iam_email] + } +} + +# environment: development folder and project factory automation resources + +module "branch-teams-team-dev-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-team-folder[each.key].id + # naming: environment descriptive name + name = "${module.branch-teams-team-folder[each.key].name} - Development" + # environment-wide human permissions on the whole teams environment + group_iam = {} + iam = { + # remove owner here and at project level if SA does not manage project resources + "roles/owner" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/logging.admin" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/resourcemanager.folderAdmin" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + } +} + +moved { + from = module.branch-teams-project-factory-sa["dev"] + to = module.branch-teams-dev-projectfactory-sa +} + +module "branch-teams-dev-projectfactory-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + # naming: environment in description + description = "Terraform project factory development service account." + prefix = local.prefixes.dev +} + +moved { + from = module.branch-teams-project-factory-gcs["dev"] + to = module.branch-teams-dev-projectfactory-gcs +} + +module "branch-teams-dev-projectfactory-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-dev-projectfactory-sa.iam_email] + } +} + +# environment: production folder and project factory automation resources + +module "branch-teams-team-prod-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-team-folder[each.key].id + # naming: environment descriptive name + name = "${module.branch-teams-team-folder[each.key].name} - Production" + # environment-wide human permissions on the whole teams environment + group_iam = {} + iam = { + # remove owner here and at project level if SA does not manage project resources + "roles/owner" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/logging.admin" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/resourcemanager.folderAdmin" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + } +} + +moved { + from = module.branch-teams-project-factory-sa["prod"] + to = module.branch-teams-prod-projectfactory-sa +} + +module "branch-teams-prod-projectfactory-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + # naming: environment in description + description = "Terraform project factory production service account." + prefix = local.prefixes.prod +} + +moved { + from = module.branch-teams-project-factory-gcs["prod"] + to = module.branch-teams-prod-projectfactory-gcs +} + +module "branch-teams-prod-projectfactory-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-prod-projectfactory-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/diagram.png b/fast/stages/01-resman/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..99c6e26d227fbfe8ad586c818e3fa4fa3b03b38e GIT binary patch literal 109088 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≶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 D?Q6k7 literal 0 HcmV?d00001 diff --git a/fast/stages/01-resman/main.tf b/fast/stages/01-resman/main.tf new file mode 100644 index 00000000..2aedb7ce --- /dev/null +++ b/fast/stages/01-resman/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 { + # convenience flags that express where billing account resides + billing_ext = var.billing_account.organization_id == null + billing_org = var.billing_account.organization_id == var.organization.id + billing_org_ext = !local.billing_ext && !local.billing_org + groups = { + for k, v in var.groups : + k => "${v}@${var.organization.domain}" + } + groups_iam = { + for k, v in local.groups : + k => "group:${v}" + } + # naming: environment names + prefixes = { + dev = "${var.prefix}-dev" + prod = "${var.prefix}-prod" + } +} diff --git a/fast/stages/01-resman/organization.tf b/fast/stages/01-resman/organization.tf new file mode 100644 index 00000000..f8065be4 --- /dev/null +++ b/fast/stages/01-resman/organization.tf @@ -0,0 +1,144 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization policies. + + +locals { + # set to the empty list if you remove the GKE branch + branch_gke_sa_iam_emails = [ + module.branch-gke-dev-sa.iam_email, + module.branch-gke-prod-sa.iam_email + ] + # set to the empty list if you remove the teams branch + branch_teams_pf_sa_iam_emails = [ + module.branch-teams-dev-projectfactory-sa.iam_email, + module.branch-teams-prod-projectfactory-sa.iam_email + ] + list_deny = { + inherit_from_parent = false + suggested_value = null + status = false + values = [] + } + policy_configs = ( + var.organization_policy_configs == null + ? {} + : var.organization_policy_configs + ) +} + +module "organization" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + organization_id = "organizations/${var.organization.id}" + # IAM additive bindings, granted via the restricted Organization Admin custom + # role assigned in stage 00; they need to be additive to avoid conflicts + iam_additive = merge( + { + (var.custom_roles.xpnServiceAdmin) = concat( + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) + "roles/accesscontextmanager.policyAdmin" = [ + module.branch-security-sa.iam_email + ] + "roles/billing.costsManager" = concat( + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ), + "roles/compute.orgFirewallPolicyAdmin" = [ + module.branch-network-sa.iam_email + ] + "roles/compute.xpnAdmin" = [ + module.branch-network-sa.iam_email + ] + "roles/orgpolicy.policyAdmin" = local.branch_teams_pf_sa_iam_emails + }, + local.billing_org ? { + "roles/billing.user" = concat( + [ + module.branch-network-sa.iam_email, + module.branch-security-sa.iam_email, + ], + # enable if individual teams can create their own projects + # [ + # for k, v in module.branch-teams-team-sa : v.iam_email + # ], + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) + } : {} + ) + # sample subset of useful organization policies, edit to suit requirements + policy_boolean = { + "constraints/cloudfunctions.requireVPCConnector" = true + "constraints/compute.disableGuestAttributesAccess" = true + "constraints/compute.disableInternetNetworkEndpointGroup" = true + "constraints/compute.disableNestedVirtualization" = true + "constraints/compute.disableSerialPortAccess" = true + "constraints/compute.requireOsLogin" = true + "constraints/compute.restrictXpnProjectLienRemoval" = true + "constraints/compute.skipDefaultNetworkCreation" = true + "constraints/iam.automaticIamGrantsForDefaultServiceAccounts" = true + "constraints/iam.disableServiceAccountKeyCreation" = true + "constraints/iam.disableServiceAccountKeyUpload" = true + "constraints/sql.restrictPublicIp" = true + "constraints/sql.restrictAuthorizedNetworks" = true + "constraints/storage.uniformBucketLevelAccess" = true + } + policy_list = { + "constraints/cloudfunctions.allowedIngressSettings" = merge( + local.list_deny, { values = ["ALLOW_INTERNAL_ONLY"] } + ) + "constraints/cloudfunctions.allowedVpcConnectorEgressSettings" = merge( + local.list_deny, { values = ["PRIVATE_RANGES_ONLY"] } + ) + "constraints/compute.restrictLoadBalancerCreationForTypes" = merge( + local.list_deny, { values = ["in:INTERNAL"] } + ) + "constraints/compute.vmExternalIpAccess" = local.list_deny + "constraints/iam.allowedPolicyMemberDomains" = { + inherit_from_parent = false + suggested_value = null + status = true + values = concat( + [var.organization.customer_id], + try(local.policy_configs.allowed_policy_member_domains, []) + ) + } + "constraints/run.allowedIngress" = merge( + local.list_deny, { values = ["internal"] } + ) + "constraints/run.allowedVPCEgress" = merge( + local.list_deny, { values = ["private-ranges-only"] } + ) + # "constraints/compute.restrictCloudNATUsage" = local.list_deny + # "constraints/compute.restrictDedicatedInterconnectUsage" = local.list_deny + # "constraints/compute.restrictPartnerInterconnectUsage" = local.list_deny + # "constraints/compute.restrictProtocolForwardingCreationForTypes" = local.list_deny + # "constraints/compute.restrictSharedVpcHostProjects" = local.list_deny + # "constraints/compute.restrictSharedVpcSubnetworks" = local.list_deny + # "constraints/compute.restrictVpcPeering" = local.list_deny + # "constraints/compute.restrictVpnPeerIPs" = local.list_deny + # "constraints/compute.vmCanIpForward" = local.list_deny + # "constraints/gcp.resourceLocations" = { + # inherit_from_parent = false + # suggested_value = null + # status = true + # values = local.allowed_regions + # } + } +} diff --git a/fast/stages/01-resman/outputs.tf b/fast/stages/01-resman/outputs.tf new file mode 100644 index 00000000..7b52a394 --- /dev/null +++ b/fast/stages/01-resman/outputs.tf @@ -0,0 +1,166 @@ +/** + * 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 { + _project_factory_sas = { + dev = module.branch-teams-dev-projectfactory-sa.iam_email + prod = module.branch-teams-prod-projectfactory-sa.iam_email + } + providers = { + "02-networking" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-network-gcs.name + name = "networking" + sa = module.branch-network-sa.email + }) + "02-security" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-security-gcs.name + name = "security" + sa = module.branch-security-sa.email + }) + "99-sandbox" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-sandbox-gcs.name + name = "sandbox" + sa = module.branch-sandbox-sa.email + }) + "03-gke-multitenant-dev" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-gke-dev-gcs.name + name = "gke-dev" + sa = module.branch-gke-dev-sa.email + }) + "03-gke-multitenant-prod" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-gke-prod-gcs.name + name = "gke-prod" + sa = module.branch-gke-prod-sa.email + }) + "03-project-factory-dev" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-teams-dev-projectfactory-gcs.name + name = "team-dev" + sa = module.branch-teams-dev-projectfactory-sa.email + }) + "03-project-factory-prod" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-teams-prod-projectfactory-gcs.name + name = "team-prod" + sa = module.branch-teams-prod-projectfactory-sa.email + }) + } + tfvars = { + "02-networking" = jsonencode({ + folder_id = module.branch-network-folder.id + project_factory_sa = local._project_factory_sas + }) + "02-security" = jsonencode({ + folder_id = module.branch-security-folder.id + kms_restricted_admins = { + for k, v in local._project_factory_sas : k => [v] + } + }) + "03-gke-multitenant-dev" = jsonencode({ + folder_id = module.branch-gke-dev-folder.id + }) + "03-gke-multitenant-prod" = jsonencode({ + folder_id = module.branch-gke-prod-folder.id + }) + } +} + +# optionally generate providers and tfvars files for subsequent stages + +resource "local_file" "providers" { + for_each = var.outputs_location == null ? {} : local.providers + filename = "${var.outputs_location}/${each.key}/providers.tf" + content = each.value +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-resman.auto.tfvars.json" + content = each.value +} + +# outputs + +output "networking" { + # tfdoc:output:consumers 02-networking + description = "Data for the networking stage." + value = { + folder = module.branch-network-folder.id + gcs_bucket = module.branch-network-gcs.name + service_account = module.branch-network-sa.iam_email + } +} + +output "project_factories" { + # tfdoc:output:consumers xx-teams + description = "Data for the project factories stage." + value = { + dev = { + bucket = module.branch-teams-dev-projectfactory-gcs.name + sa = module.branch-teams-dev-projectfactory-sa.email + } + prod = { + bucket = module.branch-teams-prod-projectfactory-gcs.name + sa = module.branch-teams-prod-projectfactory-sa.email + } + } +} + +# ready to use provider configurations for subsequent stages + +output "providers" { + # tfdoc:output:consumers 02-networking 02-security xx-sandbox xx-teams + description = "Terraform provider files for this stage and dependent stages." + sensitive = true + value = local.providers +} + +output "sandbox" { + # tfdoc:output:consumers xx-sandbox + description = "Data for the sandbox stage." + value = { + folder = module.branch-sandbox-folder.id + gcs_bucket = module.branch-sandbox-gcs.name + service_account = module.branch-sandbox-sa.email + } +} + +output "security" { + # tfdoc:output:consumers 02-security + description = "Data for the networking stage." + value = { + folder = module.branch-security-folder.id + gcs_bucket = module.branch-security-gcs.name + service_account = module.branch-security-sa.iam_email + } +} + +output "teams" { + description = "Data for the teams stage." + value = { + for k, v in module.branch-teams-team-folder : k => { + folder = v.id + gcs_bucket = module.branch-teams-team-gcs[k].name + service_account = module.branch-teams-team-sa[k].email + } + } +} + +# ready to use variable values for subsequent stages + +output "tfvars" { + description = "Terraform variable files for the following stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/01-resman/providers.tf b/fast/stages/01-resman/providers.tf new file mode 120000 index 00000000..49577549 --- /dev/null +++ b/fast/stages/01-resman/providers.tf @@ -0,0 +1 @@ +../../configs/jccb/01-resman/providers.tf \ No newline at end of file diff --git a/fast/stages/01-resman/variables.tf b/fast/stages/01-resman/variables.tf new file mode 100644 index 00000000..c1d63c86 --- /dev/null +++ b/fast/stages/01-resman/variables.tf @@ -0,0 +1,104 @@ +/** + * 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. + */ + +# defaults for variables marked with global tfdoc annotations, can be set via +# the tfvars file generated in stage 00 and stored in its outputs + +variable "billing_account" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id and organization id ('nnnnnnnn' or null)." + type = object({ + id = string + organization_id = number + }) +} + +variable "automation_project_id" { + # tfdoc:variable:source 00-bootstrap + description = "Project id for the automation project created by the bootstrap stage." + type = string +} + +variable "custom_roles" { + # tfdoc:variable:source 00-bootstrap + description = "Custom roles defined at the org level, in key => id format." + type = map(string) + default = {} +} + +variable "groups" { + # tfdoc:variable:source 00-bootstrap + description = "Group names to grant organization-level permissions." + type = map(string) + # https://cloud.google.com/docs/enterprise/setup-checklist + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "organization" { + # tfdoc:variable:source 00-bootstrap + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "organization_policy_configs" { + description = "Organization policies customization." + type = object({ + allowed_policy_member_domains = list(string) + }) + default = null +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + # tfdoc:variable:source 00-bootstrap + description = "Prefix used for resources that need unique names." + type = string +} + +variable "team_folders" { + description = "Team folders to be created. Format is described in a code comment." + type = map(object({ + descriptive_name = string + group_iam = map(list(string)) + impersonation_groups = list(string) + })) + default = null + # default = { + # team-a = { + # descriptive_name = "Team A" + # group_iam = { + # team-a-group = [roles/owner, roles/projectCreator] + # } + # impersonation_groups = ["team-a-admins@example.com"] + # } + # } +} diff --git a/tests/fast/stages/__init__.py b/tests/fast/stages/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/__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/fast/stages/s00_bootstrap/__init__.py b/tests/fast/stages/s00_bootstrap/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/__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/fast/stages/s00_bootstrap/fixture/main.tf b/tests/fast/stages/s00_bootstrap/fixture/main.tf new file mode 100644 index 00000000..d6130655 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/fixture/main.tf @@ -0,0 +1,29 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/00-bootstrap" + prefix = "fast" + organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" + } + billing_account = { + id = "000000-111111-222222" + organization_id = 123456789012 + } +} diff --git a/tests/fast/stages/s00_bootstrap/test_plan.py b/tests/fast/stages/s00_bootstrap/test_plan.py new file mode 100644 index 00000000..1af5d507 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/test_plan.py @@ -0,0 +1,33 @@ +# 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. + +# _RESOURCE_COUNT = { +# 'module.organization': 28, +# 'module.automation-project': 23, +# 'module.automation-tf-bootstrap-gcs': 1, +# 'module.automation-tf-bootstrap-sa': 1, +# 'module.automation-tf-resman-gcs': 2, +# 'module.automation-tf-resman-sa': 1, +# 'module.billing-export-dataset': 1, +# 'module.billing-export-project': 7, +# 'module.log-export-dataset': 1, +# 'module.log-export-project': 7, +# } + + +def test_counts(e2e_plan_runner): + "Test stage." + # TODO: to re-enable per-module resource count check print _, then test + num_modules, num_resources, _ = e2e_plan_runner() + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s01_resman/__init__.py b/tests/fast/stages/s01_resman/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s01_resman/__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/fast/stages/s01_resman/fixture/main.tf b/tests/fast/stages/s01_resman/fixture/main.tf new file mode 100644 index 00000000..06ab534c --- /dev/null +++ b/tests/fast/stages/s01_resman/fixture/main.tf @@ -0,0 +1,42 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/01-resman" + automation_project_id = "fast-prod-automation" + billing_account = { + id = "000000-111111-222222" + organization_id = 123456789012 + } + custom_roles = { + "organizationIamAdmin" : "organizations/123456789012/roles/organizationIamAdmin", + "xpnServiceAdmin" : "organizations/123456789012/roles/xpnServiceAdmin" + } + groups = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins", + gcp-organization-admins = "gcp-organization-admins", + gcp-security-admins = "gcp-security-admins", + gcp-support = "gcp-support" + } + organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" + } + prefix = "fast2" +} diff --git a/tests/fast/stages/s01_resman/test_plan.py b/tests/fast/stages/s01_resman/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s01_resman/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_networking/__init__.py b/tests/fast/stages/s02_networking/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s02_networking/__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/fast/stages/s02_networking/fixture/data b/tests/fast/stages/s02_networking/fixture/data new file mode 120000 index 00000000..423e31f5 --- /dev/null +++ b/tests/fast/stages/s02_networking/fixture/data @@ -0,0 +1 @@ +../../../../stages/02-networking/data \ No newline at end of file diff --git a/tests/fast/stages/s02_networking/fixture/main.tf b/tests/fast/stages/s02_networking/fixture/main.tf new file mode 100644 index 00000000..b3fda3d4 --- /dev/null +++ b/tests/fast/stages/s02_networking/fixture/main.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/02-networking" + billing_account_id = "000000-111111-222222" + organization = { + domain = "gcp-pso-italy.net" + id = 856933387836 + customer_id = "C01lmug8b" + } + prefix = "fast" + project_factory_sa = { + dev = "foo@iam" + prod = "bar@iam" + } +} diff --git a/tests/fast/stages/s02_networking/test_plan.py b/tests/fast/stages/s02_networking/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s02_networking/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_security/__init__.py b/tests/fast/stages/s02_security/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s02_security/__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/fast/stages/s02_security/fixture/data b/tests/fast/stages/s02_security/fixture/data new file mode 120000 index 00000000..423e31f5 --- /dev/null +++ b/tests/fast/stages/s02_security/fixture/data @@ -0,0 +1 @@ +../../../../stages/02-networking/data \ No newline at end of file diff --git a/tests/fast/stages/s02_security/fixture/main.tf b/tests/fast/stages/s02_security/fixture/main.tf new file mode 100644 index 00000000..39e1de82 --- /dev/null +++ b/tests/fast/stages/s02_security/fixture/main.tf @@ -0,0 +1,109 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/02-security" + billing_account_id = "000000-111111-222222" + folder_id = "folders/12345678" + organization = { + domain = "gcp-pso-italy.net" + id = 856933387836 + customer_id = "C01lmug8b" + } + prefix = "fast" + kms_restricted_admins = { + "dev" : [ + "serviceAccount:fast-dev-resman-pf-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ], + "prod" : [ + "serviceAccount:fast-prod-resman-pf-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + kms_keys = { + compute = { + iam = { + "roles/cloudkms.admin" = ["user:user1@example.com"] + } + labels = { service = "compute" } + locations = null + rotation_period = null + } + } + vpc_sc_ingress_policies = { + iac = { + ingress_from = { + identities = [ + "serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ], + source_access_levels = ["*"], identity_type = null, source_resources = null + } + ingress_to = { + operations = [{ method_selectors = [], service_name = "*" }] + resources = ["*"] + } + } + } + vpc_sc_perimeter_ingress_policies = { + dev = ["iac"] + landing = null + prod = ["iac"] + } + vpc_sc_perimeter_projects = { + dev = [ + "projects/345678912", # ludo-dev-sec-core-0 + ] + landing = [] + prod = [ + "projects/234567891", # ludo-prod-sec-core-0 + ] + } + + vpc_sc_access_levels = { + all = { + combining_function = null + conditions = [{ + members = [ + "serviceAccount:quota-monitor@foobar.iam.gserviceaccount.com", + ], + ip_subnetworks = null, negate = null, regions = null, + required_access_levels = null + }] + } + } + + vpc_sc_perimeter_access_levels = { + dev = ["all"] + landing = null + prod = ["all"] + } + + vpc_sc_egress_policies = { + iac-gcs = { + egress_from = { + identity_type = null + identities = [ + "serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + egress_to = { + operations = [{ + method_selectors = ["*"], service_name = "storage.googleapis.com" + }] + resources = ["projects/123456789"] + } + } + } +} diff --git a/tests/fast/stages/s02_security/test_plan.py b/tests/fast/stages/s02_security/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s02_security/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s03_project_factory/__init__.py b/tests/fast/stages/s03_project_factory/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/__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/fast/stages/s03_project_factory/fixture/data/defaults.yaml b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml new file mode 100644 index 00000000..ab35a310 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml @@ -0,0 +1,22 @@ +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Setup for billing alerts +billing_alert: + amount: 1000 + thresholds: + current: [0.5, 0.8] + forecasted: [0.5, 0.8] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: ["team-contacts@example.com"] + +# [opt] Labels set for all projects +labels: + environment: prod + department: accounting + application: example-app + foo: bar + +# [opt] Additional notification channels for billing +notification_channels: [] diff --git a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml new file mode 100644 index 00000000..a8b92d60 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml @@ -0,0 +1,98 @@ +# [opt] Billing account id - overrides default if set +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Billing alerts config - overrides default if set +billing_alert: + amount: 10 + thresholds: + current: + - 0.5 + - 0.8 + forecasted: [] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] DNS zones to be created as children of the environment_dns_zone defined in defaults +dns_zones: + - lorem + - ipsum + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: + - team-a-contacts@example.com + +# Folder the project will be created as children of +folder_id: folders/012345678901 + +# [opt] Authoritative IAM bindings in group => [roles] format +group_iam: + test-team-foobar@fast-lab-0.gcp-pso-italy.net: + - roles/compute.admin + +# [opt] Authoritative IAM bindings in role => [principals] format +# Generally used to grant roles to service accounts external to the project +iam: + roles/compute.admin: + - serviceAccount:service-account + +# [opt] Service robots and keys they will be assigned as cryptoKeyEncrypterDecrypter +# in service => [keys] format +kms_service_agents: + compute: [key1, key2] + storage: [key1, key2] + +# [opt] Labels for the project - merged with the ones defined in defaults +labels: + environment: prod + +# [opt] Org policy overrides defined at project level +org_policies: + policy_boolean: + constraints/compute.disableGuestAttributesAccess: true + policy_list: + constraints/compute.trustedImageProjects: + inherit_from_parent: null + status: true + suggested_value: null + values: + - projects/fast-prod-iac-core-0 + +# [opt] Service account to create for the project and their roles on the project +# in name => [roles] format +service_accounts: + another-service-account: + - roles/compute.admin + my-service-account: + - roles/compute.admin + +# [opt] APIs to enable on the project. +services: + - storage.googleapis.com + - stackdriver.googleapis.com + - compute.googleapis.com + +# [opt] Roles to assign to the robots service accounts in robot => [roles] format +services_iam: + compute: + - roles/storage.objectViewer + + # [opt] VPC setup. + # If set enables the `compute.googleapis.com` service and configures + # service project attachment +vpc: + # [opt] If set, enables the container API + gke_setup: + # Grants "roles/container.hostServiceAgentUser" to the container robot if set + enable_host_service_agent: false + + # Grants "roles/compute.securityAdmin" to the container robot if set + enable_security_admin: true + + # Host project the project will be service project of + host_project: fast-prod-net-spoke-0 + + # [opt] Subnets in the host project where principals will be granted networkUser + # in region/subnet-name => [principals] + subnets_iam: + europe-west1/prod-default-ew1: + - user:foobar@example.com + - serviceAccount:service-account1 diff --git a/tests/fast/stages/s03_project_factory/fixture/main.tf b/tests/fast/stages/s03_project_factory/fixture/main.tf new file mode 100644 index 00000000..e1c05e51 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Project factory. + + +locals { + _defaults = yamldecode(file(var.defaults_file)) + _defaults_net = { + billing_account_id = var.billing_account_id + environment_dns_zone = var.environment_dns_zone + shared_vpc_self_link = var.shared_vpc_self_link + vpc_host_project = var.vpc_host_project + } + defaults = merge(local._defaults, local._defaults_net) + projects = { + for f in fileset("${var.data_dir}", "**/*.yaml") : + trimsuffix(f, ".yaml") => yamldecode(file("${var.data_dir}/${f}")) + } +} + +module "projects" { + #TODO(sruffilli): Pin to release + source = "github.com/terraform-google-modules/cloud-foundation-fabric/examples/factories/project-factory" + for_each = local.projects + defaults = local.defaults + project_id = each.key + billing_account_id = try(each.value.billing_account_id, null) + billing_alert = try(each.value.billing_alert, null) + dns_zones = try(each.value.dns_zones, []) + essential_contacts = try(each.value.essential_contacts, []) + folder_id = each.value.folder_id + group_iam = try(each.value.group_iam, {}) + iam = try(each.value.iam, {}) + kms_service_agents = try(each.value.kms, {}) + labels = try(each.value.labels, {}) + org_policies = try(each.value.org_policies, null) + service_accounts = try(each.value.service_accounts, {}) + services = try(each.value.services, []) + services_iam = try(each.value.services_iam, {}) + vpc = try(each.value.vpc, null) +} + + diff --git a/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json b/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json new file mode 100644 index 00000000..d446d643 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json @@ -0,0 +1,4 @@ +{ + "billing_account_id": "012345-67890A-BCDEF0", + "prefix": "fast" +} \ No newline at end of file diff --git a/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json b/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json new file mode 100644 index 00000000..56cfa3de --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json @@ -0,0 +1,5 @@ +{ + "environment_dns_zone": "prod.gcp.example.com.", + "shared_vpc_self_link": "https://www.googleapis.com/compute/v1/projects/fast-example/global/networks/prod-spoke-0", + "vpc_host_project": "fast-example" +} \ No newline at end of file diff --git a/tests/fast/stages/s03_project_factory/fixture/variables.tf b/tests/fast/stages/s03_project_factory/fixture/variables.tf new file mode 100644 index 00000000..b52ebd6c --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/variables.tf @@ -0,0 +1,61 @@ +/** + * 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. + */ + +#TODO: tfdoc annotations + +variable "billing_account_id" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id." + type = string +} + +variable "data_dir" { + description = "Relative path for the folder storing configuration data." + type = string + default = "data/projects" +} + +variable "environment_dns_zone" { + # tfdoc:variable:source 02-networking + description = "DNS zone suffix for environment." + type = string + default = null +} + +variable "defaults_file" { + description = "Relative path for the file storing the project factory configuration." + type = string + default = "data/defaults.yaml" +} + +#TODO(sruffilli): is this really required? +variable "environment" { + description = "Environment where projects will be created (e.g. prod, dev, ...)." + type = string + default = "prod" +} + +variable "shared_vpc_self_link" { + # tfdoc:variable:source 02-networking + description = "Self link for the shared VPC." + type = string +} + +variable "vpc_host_project" { + # tfdoc:variable:source 02-networking + description = "Host project for the shared VPC." + type = string +} diff --git a/tests/fast/stages/s03_project_factory/test_plan.py b/tests/fast/stages/s03_project_factory/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 From 1f41c5a32074ca0cd1ab65d9d72b009da39575b4 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:46:24 +0100 Subject: [PATCH 008/132] Copy FAST top level README --- tests/fast/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/fast/README.md diff --git a/tests/fast/README.md b/tests/fast/README.md new file mode 100644 index 00000000..01ea3e69 --- /dev/null +++ b/tests/fast/README.md @@ -0,0 +1,34 @@ +# Fabric FAST + +Setting up a production-ready GCP organization is often a time-consuming process. Fabric FAST aims to speed up this process via two complementary goals. On the one hand, FAST provides a design of a GCP organization that includes the typical elements required by enterprise customers. Secondly, we provide a reference implementation of the FAST design using Terraform. + +Note that while our implementation is necessarily influenced (and constrained) by the way Terraform works, the design we put forward only refers to GCP constructs and features. In other words, while we use Terraform for our reference implementation, in theory, the FAST design can be implemented using any other tool (e.g., Pulumi, bash scripts, or even calling the relevant APIs directly). + +Fabric FAST comes from engineers in Google Cloud's Professional Services Organization, with a combined experience of decades solving the typical technical problems faced by GCP customers. While every GCP user has specific requirements, many common issues arise repeatedly. Solving those issues correctly from the beginning is key to a robust and scalable GCP setup. It's those common issues and their solutions that Fabric FAST aims to collect and present coherently. + +Fabric FAST was initially conceived to help enterprises quickly set up a GCP organization following battle-tested and widely-used patterns. Despite its origin in enterprise environments, FAST includes many customization points making it an ideal blueprint for organizations of all sizes, ranging from startups to the largest companies. + + +## Guiding principles +### Contracts and stages +FAST uses the concept of stages, which individually perform precise tasks but, taken together, build a functional, ready-to-use GCP organization. More importantly, stages are modeled around the security boundaries that typically appear in mature organizations. This arrangement allows delegating ownership of each stage to the team responsible for the types of resources it manages. For example, as its name suggests, the networking stage sets up all the networking elements and is usually the responsibility of a dedicated networking team within the organization. + +From the perspective of FAST's overall design, stages also work as contacts or interfaces, defining a set of pre-requisites and inputs required to perform their designed task and generating outputs needed by other stages lower in the chain. + +### Security-first design +Security was, from the beginning, one of the most critical elements in the design of Fabric FAST. Many of FAST's design decisions aim to build the foundations of a secure organization. In fact, the first two stages deal mainly with the organization-wide security setup. + +FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. + +### Extensive use of factories +A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. + +FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. + +## High level design + +TBD + +## Implementation + +TBD From e0453f04394059049fbc5060dbf7324d18f7f712 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:46:24 +0100 Subject: [PATCH 009/132] Copy FAST top level README --- fast/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 fast/README.md diff --git a/fast/README.md b/fast/README.md new file mode 100644 index 00000000..01ea3e69 --- /dev/null +++ b/fast/README.md @@ -0,0 +1,34 @@ +# Fabric FAST + +Setting up a production-ready GCP organization is often a time-consuming process. Fabric FAST aims to speed up this process via two complementary goals. On the one hand, FAST provides a design of a GCP organization that includes the typical elements required by enterprise customers. Secondly, we provide a reference implementation of the FAST design using Terraform. + +Note that while our implementation is necessarily influenced (and constrained) by the way Terraform works, the design we put forward only refers to GCP constructs and features. In other words, while we use Terraform for our reference implementation, in theory, the FAST design can be implemented using any other tool (e.g., Pulumi, bash scripts, or even calling the relevant APIs directly). + +Fabric FAST comes from engineers in Google Cloud's Professional Services Organization, with a combined experience of decades solving the typical technical problems faced by GCP customers. While every GCP user has specific requirements, many common issues arise repeatedly. Solving those issues correctly from the beginning is key to a robust and scalable GCP setup. It's those common issues and their solutions that Fabric FAST aims to collect and present coherently. + +Fabric FAST was initially conceived to help enterprises quickly set up a GCP organization following battle-tested and widely-used patterns. Despite its origin in enterprise environments, FAST includes many customization points making it an ideal blueprint for organizations of all sizes, ranging from startups to the largest companies. + + +## Guiding principles +### Contracts and stages +FAST uses the concept of stages, which individually perform precise tasks but, taken together, build a functional, ready-to-use GCP organization. More importantly, stages are modeled around the security boundaries that typically appear in mature organizations. This arrangement allows delegating ownership of each stage to the team responsible for the types of resources it manages. For example, as its name suggests, the networking stage sets up all the networking elements and is usually the responsibility of a dedicated networking team within the organization. + +From the perspective of FAST's overall design, stages also work as contacts or interfaces, defining a set of pre-requisites and inputs required to perform their designed task and generating outputs needed by other stages lower in the chain. + +### Security-first design +Security was, from the beginning, one of the most critical elements in the design of Fabric FAST. Many of FAST's design decisions aim to build the foundations of a secure organization. In fact, the first two stages deal mainly with the organization-wide security setup. + +FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. + +### Extensive use of factories +A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. + +FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. + +## High level design + +TBD + +## Implementation + +TBD From ce1006e53a70c5c48a6444737f6d6f912498864d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:50:34 +0100 Subject: [PATCH 010/132] TODO list --- fast/TODO.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 fast/TODO.txt diff --git a/fast/TODO.txt b/fast/TODO.txt new file mode 100644 index 00000000..439af117 --- /dev/null +++ b/fast/TODO.txt @@ -0,0 +1,10 @@ +TODO before merging + +- [ ] fix tests +- [ ] fix linting errors +- [ ] fast-specific .gitignore +- [ ] YAML samples thingy +- [ ] stages README +- [ ] proper docstring on new tools +- [ ] modify github actions for different fast tfdoc usage +- [ ] add roadmap to top-level fast README From 640022b9756a48525c75d751a7c74d3d7491e03d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:51:22 +0100 Subject: [PATCH 011/132] TODO list --- fast/TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/fast/TODO.txt b/fast/TODO.txt index 439af117..aecf14c5 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -8,3 +8,4 @@ TODO before merging - [ ] proper docstring on new tools - [ ] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README +- [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) From cce95026cfdba1297f2fb50afd2ec234e510a166 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:59:39 +0100 Subject: [PATCH 012/132] fix linting action to account for fast --- .github/workflows/linting.yml | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index e3919451..2e27c6dc 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -51,23 +51,22 @@ jobs: run: | terraform fmt -recursive -check -diff $GITHUB_WORKSPACE - - name: Check documentation - id: documentation + - name: Check documentation (fabric) + id: documentation-fabric run: | - python3 tools/check_documentation.py \ - cloud-operations \ - data-solutions \ - factories \ - foundations \ - modules \ - networking + python3 tools/check_documentation.py examples modules + + - name: Check documentation (fast) + id: documentation-fast + run: | + python3 tools/check_documentation.py --files --show-extra fast markdown-link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: 'yes' - use-verbose-mode: 'yes' - config-file: '.github/workflows/markdown-link-check.json' + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: "yes" + use-verbose-mode: "yes" + config-file: ".github/workflows/markdown-link-check.json" From 714ac319e532e6082e4316ddb086bfcefd9d75da Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:02:24 +0100 Subject: [PATCH 013/132] remove providers file --- fast/stages/01-resman/providers.tf | 1 - 1 file changed, 1 deletion(-) delete mode 120000 fast/stages/01-resman/providers.tf diff --git a/fast/stages/01-resman/providers.tf b/fast/stages/01-resman/providers.tf deleted file mode 120000 index 49577549..00000000 --- a/fast/stages/01-resman/providers.tf +++ /dev/null @@ -1 +0,0 @@ -../../configs/jccb/01-resman/providers.tf \ No newline at end of file From 567bd57085809d6ed49bf941843c0f2ad5bc1529 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:05:42 +0100 Subject: [PATCH 014/132] add missing boilerplate --- fast/assets/schemas/firewall_rules.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/hierarchical_rules.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/project.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/project_defaults.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/subnet.schema.yaml | 14 ++++++++++++++ .../s03_project_factory/fixture/data/defaults.yaml | 14 ++++++++++++++ .../fixture/data/projects/project.yaml | 14 ++++++++++++++ 7 files changed, 98 insertions(+) diff --git a/fast/assets/schemas/firewall_rules.schema.yaml b/fast/assets/schemas/firewall_rules.schema.yaml index ebb62772..1fd96caf 100644 --- a/fast/assets/schemas/firewall_rules.schema.yaml +++ b/fast/assets/schemas/firewall_rules.schema.yaml @@ -1,3 +1,17 @@ +# 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. + map(include('firewall_rule')) --- firewall_rule: diff --git a/fast/assets/schemas/hierarchical_rules.schema.yaml b/fast/assets/schemas/hierarchical_rules.schema.yaml index d8c72b1d..0e0f7b66 100644 --- a/fast/assets/schemas/hierarchical_rules.schema.yaml +++ b/fast/assets/schemas/hierarchical_rules.schema.yaml @@ -1,3 +1,17 @@ +# 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. + map(include('hierarchical_rule')) --- hierarchical_rule: diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml index e914e5a7..f7f89730 100644 --- a/fast/assets/schemas/project.schema.yaml +++ b/fast/assets/schemas/project.schema.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) billing_alert: any(include('billing_alert'), null(), required=False) # If set to null, use defaults dns_zones: list(str(), required=False) diff --git a/fast/assets/schemas/project_defaults.schema.yaml b/fast/assets/schemas/project_defaults.schema.yaml index 52676baa..113fe26b 100644 --- a/fast/assets/schemas/project_defaults.schema.yaml +++ b/fast/assets/schemas/project_defaults.schema.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) billing_alert: any(include('billing_alert'), null(), required=False) essential_contacts: list(str(), required=False) diff --git a/fast/assets/schemas/subnet.schema.yaml b/fast/assets/schemas/subnet.schema.yaml index 1bf10ee8..add0d74b 100644 --- a/fast/assets/schemas/subnet.schema.yaml +++ b/fast/assets/schemas/subnet.schema.yaml @@ -1,3 +1,17 @@ +# 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. + region: str() description: str() ip_cidr_range: str() diff --git a/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml index ab35a310..b050583f 100644 --- a/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml +++ b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: 012345-67890A-BCDEF0 # [opt] Setup for billing alerts diff --git a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml index a8b92d60..d988d9d5 100644 --- a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml +++ b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml @@ -1,3 +1,17 @@ +# 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. + # [opt] Billing account id - overrides default if set billing_account_id: 012345-67890A-BCDEF0 From 2bebff6582a72815fedec3b8e930ecc9e145ef6f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:08:26 +0100 Subject: [PATCH 015/132] update factory README --- .../factories/net-vpc-firewall-yaml/README.md | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/factories/net-vpc-firewall-yaml/README.md b/examples/factories/net-vpc-firewall-yaml/README.md index 064ee30c..89af153e 100644 --- a/examples/factories/net-vpc-firewall-yaml/README.md +++ b/examples/factories/net-vpc-firewall-yaml/README.md @@ -136,25 +136,27 @@ web-app-a-ingress: ``` + ## Variables -| name | description | type | required | default | -| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------: | :------: | :---------------: | -| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | -| network | Name of the network this set of firewall rules applies to. | string | ✓ | | -| project_id | Project Id. | string | ✓ | | -| log_config | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({…}) | | null | +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | +| network | Name of the network this set of firewall rules applies to. | string | ✓ | | +| project_id | Project Id. | string | ✓ | | +| log_config | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({…}) | | null | ## Outputs -| name | description | sensitive | -| ------------------- | -------------------------------- | :-------: | -| egress_allow_rules | Egress rules with allow blocks. | | -| egress_deny_rules | Egress rules with allow blocks. | | -| ingress_allow_rules | Ingress rules with allow blocks. | | -| ingress_deny_rules | Ingress rules with deny blocks. | | +| name | description | sensitive | +|---|---|:---:| +| egress_allow_rules | Egress rules with allow blocks. | | +| egress_deny_rules | Egress rules with allow blocks. | | +| ingress_allow_rules | Ingress rules with allow blocks. | | +| ingress_deny_rules | Ingress rules with deny blocks. | | + From 5f72c9083bfb7877bb2379f6e16329382349ff8a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:10:47 +0100 Subject: [PATCH 016/132] align examples tfdoc --- .../gcs-to-bq-with-dataflow/README.md | 4 ++- .../decentralized-firewall/README.md | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/data-solutions/gcs-to-bq-with-dataflow/README.md b/examples/data-solutions/gcs-to-bq-with-dataflow/README.md index aa6a6eb7..055f93ef 100644 --- a/examples/data-solutions/gcs-to-bq-with-dataflow/README.md +++ b/examples/data-solutions/gcs-to-bq-with-dataflow/README.md @@ -113,14 +113,15 @@ You can check data imported into Google BigQuery from the Google Cloud Console U + ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| prefix | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | ✓ | | | project_id | Project id, references existing project if `project_create` is null. | string | ✓ | | +| prefix | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | | null | | project_create | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format | object({…}) | | null | | region | The region where resources will be deployed. | string | | "europe-west1" | | vpc_subnet_range | Ip range used for the VPC subnet created for the example. | string | | "10.0.0.0/20" | @@ -139,3 +140,4 @@ You can check data imported into Google BigQuery from the Google Cloud Console U + diff --git a/examples/networking/decentralized-firewall/README.md b/examples/networking/decentralized-firewall/README.md index 8bf40135..96c5ac2f 100644 --- a/examples/networking/decentralized-firewall/README.md +++ b/examples/networking/decentralized-firewall/README.md @@ -21,26 +21,28 @@ the two). There is an example of a YAML-based validator using [Yamale](https://g in the [`validator/`](validator/) subdirectory, which can be integrated as part of a CI/CD pipeline. + ## Variables -| name | description | type | required | default | -| ------------------ | --------------------------------------------------------------------------------------------- | :-------------------------------: | :------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | -| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | -| region | Region used. | string | | "europe-west1" | +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | +| region | Region used. | string | | "europe-west1" | ## Outputs -| name | description | sensitive | -| -------- | --------------- | :-------: | -| fw_rules | Firewall rules. | | -| projects | Project ids. | | -| vpc | Shared VPCs. | | +| name | description | sensitive | +|---|---|:---:| +| fw_rules | Firewall rules. | | +| projects | Project ids. | | +| vpc | Shared VPCs. | | + From 0a40fb527e12d553b6cb10ec54d82cde11e78f11 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:11:52 +0100 Subject: [PATCH 017/132] fast readmes tfdoc --- fast/stages/02-networking/README.md | 86 ++++++++++--------- fast/stages/03-project-factory/prod/README.md | 34 ++++---- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md index 4df304a8..b4d82351 100644 --- a/fast/stages/02-networking/README.md +++ b/fast/stages/02-networking/README.md @@ -279,58 +279,59 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res + ## Files -| name | description | modules | resources | -| ---------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | -| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | -| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | -| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | -| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | -| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | -| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | -| [variables.tf](./variables.tf) | Module variables. | | | -| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | -| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | -| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | +| name | description | modules | resources | +|---|---|---|---| +| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | +| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | +| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | +| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | +| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | +| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | ## Variables -| name | description | type | required | default | producer | -| ------------------ | -------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------: | -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | -| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | -| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | -| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | -| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | -| gke | | map(object({…})) | | {} | 01-resman | -| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | -| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | -| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | -| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | -| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | -| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | +| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | +| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | +| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | +| gke | | map(object({…})) | | {} | 01-resman | +| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | +| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | +| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | +| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | +| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | ## Outputs -| name | description | sensitive | consumers | -| ------------------------ | ----------------------------------------------- | :-------: | --------- | -| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | -| project_ids | Network project ids. | | | -| project_numbers | Network project numbers. | | | -| shared_vpc_host_projects | Shared VPC host projects. | | | -| shared_vpc_self_links | Shared VPC host projects. | | | -| tfvars | Network-related variables used in other stages. | ✓ | | -| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | +| name | description | sensitive | consumers | +|---|---|:---:|---| +| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | +| project_ids | Network project ids. | | | +| project_numbers | Network project numbers. | | | +| shared_vpc_host_projects | Shared VPC host projects. | | | +| shared_vpc_self_links | Shared VPC host projects. | | | +| tfvars | Network-related variables used in other stages. | ✓ | | +| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | @@ -351,3 +352,4 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res + diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md index 3cc9ac7e..97cd20cc 100644 --- a/fast/stages/03-project-factory/prod/README.md +++ b/fast/stages/03-project-factory/prod/README.md @@ -97,34 +97,36 @@ terraform apply + ## Files -| name | description | modules | resources | -| ------------------------------ | ----------------- | ---------------------------- | --------- | -| [main.tf](./main.tf) | Project factory. | project-factory | | -| [outputs.tf](./outputs.tf) | Module outputs. | | | -| [variables.tf](./variables.tf) | Module variables. | | | +| name | description | modules | resources | +|---|---|---|---| +| [main.tf](./main.tf) | Project factory. | project-factory | | +| [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables.tf](./variables.tf) | Module variables. | | | ## Variables -| name | description | type | required | default | producer | -| -------------------- | --------------------------------------------------------------------- | :-----------------: | :------: | :-------------------------------------------: | :------------------------: | -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | -| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | -| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | -| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | -| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | +| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | +| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | +| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | +| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | ## Outputs -| name | description | sensitive | consumers | -| -------- | -------------------------------------- | :-------: | --------- | -| projects | Created projects and service accounts. | | | +| name | description | sensitive | consumers | +|---|---|:---:|---| +| projects | Created projects and service accounts. | | | + From bad887e17bacb3894f178ae3af5d3058e9c6dcf4 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:19:14 +0100 Subject: [PATCH 018/132] disable markdown link check --- .../{markdown-link-check.json => markdown-link-check.json_} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{markdown-link-check.json => markdown-link-check.json_} (100%) diff --git a/.github/workflows/markdown-link-check.json b/.github/workflows/markdown-link-check.json_ similarity index 100% rename from .github/workflows/markdown-link-check.json rename to .github/workflows/markdown-link-check.json_ From 7f92ea2bd63daa3eb82370e2b86604ac523f8482 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:19:54 +0100 Subject: [PATCH 019/132] really disable markdown link check --- .github/workflows/linting.yml | 18 +++++++++--------- ...nk-check.json_ => markdown-link-check.json} | 0 2 files changed, 9 insertions(+), 9 deletions(-) rename .github/workflows/{markdown-link-check.json_ => markdown-link-check.json} (100%) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 2e27c6dc..be0d9b84 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -61,12 +61,12 @@ jobs: run: | python3 tools/check_documentation.py --files --show-extra fast - markdown-link-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: "yes" - use-verbose-mode: "yes" - config-file: ".github/workflows/markdown-link-check.json" + # markdown-link-check: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@master + # - uses: gaurav-nelson/github-action-markdown-link-check@v1 + # with: + # use-quiet-mode: "yes" + # use-verbose-mode: "yes" + # config-file: ".github/workflows/markdown-link-check.json" diff --git a/.github/workflows/markdown-link-check.json_ b/.github/workflows/markdown-link-check.json similarity index 100% rename from .github/workflows/markdown-link-check.json_ rename to .github/workflows/markdown-link-check.json From 8911e6fd551c00496ab40a9f19afe758a56e1a76 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:22:08 +0100 Subject: [PATCH 020/132] update TODO --- fast/TODO.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fast/TODO.txt b/fast/TODO.txt index aecf14c5..f94d2e64 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -1,11 +1,11 @@ TODO before merging - [ ] fix tests -- [ ] fix linting errors +- [x] fix linting errors - [ ] fast-specific .gitignore - [ ] YAML samples thingy - [ ] stages README - [ ] proper docstring on new tools -- [ ] modify github actions for different fast tfdoc usage +- [x] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README - [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) From 1442b15ba7f9a31eccf7c11b5a1722f2f49308ee Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:25:53 +0100 Subject: [PATCH 021/132] switch to local module refs in stage0 --- fast/stages/00-bootstrap/automation.tf | 10 +++++----- fast/stages/00-bootstrap/billing.tf | 6 +++--- fast/stages/00-bootstrap/log-export.tf | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fast/stages/00-bootstrap/automation.tf b/fast/stages/00-bootstrap/automation.tf index 8498df00..f55cfdc5 100644 --- a/fast/stages/00-bootstrap/automation.tf +++ b/fast/stages/00-bootstrap/automation.tf @@ -17,7 +17,7 @@ # tfdoc:file:description Automation project and resources. module "automation-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" billing_account = var.billing_account.id name = "iac-core-0" parent = "organizations/${var.organization.id}" @@ -68,7 +68,7 @@ module "automation-project" { # this stage's bucket and service account module "automation-tf-bootstrap-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" project_id = module.automation-project.project_id name = "iac-core-bootstrap-0" prefix = local.prefix @@ -77,7 +77,7 @@ module "automation-tf-bootstrap-gcs" { } module "automation-tf-bootstrap-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "bootstrap-0" description = "Terraform organization bootstrap service account." @@ -87,7 +87,7 @@ module "automation-tf-bootstrap-sa" { # resource hierarchy stage's bucket and service account module "automation-tf-resman-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" project_id = module.automation-project.project_id name = "iac-core-resman-0" prefix = local.prefix @@ -99,7 +99,7 @@ module "automation-tf-resman-gcs" { } module "automation-tf-resman-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "resman-0" description = "Terraform organization bootstrap service account." diff --git a/fast/stages/00-bootstrap/billing.tf b/fast/stages/00-bootstrap/billing.tf index 7d5bcf09..0b161011 100644 --- a/fast/stages/00-bootstrap/billing.tf +++ b/fast/stages/00-bootstrap/billing.tf @@ -33,7 +33,7 @@ moved { } module "billing-export-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" count = local.billing_org ? 1 : 0 billing_account = var.billing_account.id name = "billing-export-0" @@ -58,7 +58,7 @@ moved { } module "billing-export-dataset" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + source = "../../../modules/bigquery-dataset" count = local.billing_org ? 1 : 0 project_id = module.billing-export-project.0.project_id id = "billing_export" @@ -68,7 +68,7 @@ module "billing-export-dataset" { # billing account in a different org module "billing-organization-ext" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + source = "../../../modules/organization" count = local.billing_org_ext ? 1 : 0 organization_id = "organizations/${var.billing_account.organization_id}" iam_additive = { diff --git a/fast/stages/00-bootstrap/log-export.tf b/fast/stages/00-bootstrap/log-export.tf index f6fb51a5..682d473d 100644 --- a/fast/stages/00-bootstrap/log-export.tf +++ b/fast/stages/00-bootstrap/log-export.tf @@ -21,7 +21,7 @@ locals { } module "log-export-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "audit-logs-0" parent = "organizations/${var.organization.id}" prefix = local.prefix @@ -42,7 +42,7 @@ module "log-export-project" { # one log export per type, with conditionals to skip those not needed module "log-export-dataset" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + source = "../../../modules/bigquery-dataset" count = contains(local.log_types, "bigquery") ? 1 : 0 project_id = module.log-export-project.project_id id = "audit_export" @@ -50,7 +50,7 @@ module "log-export-dataset" { } module "log-export-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" count = contains(local.log_types, "storage") ? 1 : 0 project_id = module.log-export-project.project_id name = "audit-logs-0" @@ -58,7 +58,7 @@ module "log-export-gcs" { } module "log-export-logbucket" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/logging-bucket?ref=v12.0.0" + source = "../../../modules/logging-bucket" count = contains(local.log_types, "logging") ? 1 : 0 parent_type = "project" parent = module.log-export-project.project_id @@ -66,7 +66,7 @@ module "log-export-logbucket" { } module "log-export-pubsub" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/pubsub?ref=v12.0.0" + source = "../../../modules/pubsub" for_each = toset([for k, v in var.log_sinks : k if v == "pubsub"]) project_id = module.log-export-project.project_id name = "audit-logs-${each.key}" From fc2f888e35a8f1cb398f9c59cc5c2808bddca37c Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:29:01 +0100 Subject: [PATCH 022/132] replace module refs in 02-sec --- fast/TODO.txt | 7 ++++++- fast/stages/02-security/core-dev.tf | 4 ++-- fast/stages/02-security/core-prod.tf | 4 ++-- fast/stages/02-security/vpc-sc.tf | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fast/TODO.txt b/fast/TODO.txt index f94d2e64..3c99a403 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -8,4 +8,9 @@ TODO before merging - [ ] proper docstring on new tools - [x] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README -- [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) +- [ ] update modules references to local paths + - [x] stage 00 (ludo) + - [ ] stage 01 (julio) + - [ ] stage 02-net (simo) + - [x] stage 02-sec (ludo) + - [ ] stage 03-pf (simo) diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf index b0c78485..4862ef52 100644 --- a/fast/stages/02-security/core-dev.tf +++ b/fast/stages/02-security/core-dev.tf @@ -15,7 +15,7 @@ */ module "dev-sec-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "dev-sec-core-0" parent = var.folder_id prefix = var.prefix @@ -29,7 +29,7 @@ module "dev-sec-project" { module "dev-sec-kms" { for_each = toset(local.kms_locations) - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + source = "../../../modules/kms" project_id = module.dev-sec-project.project_id keyring = { location = each.key diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf index 0524788d..f259a488 100644 --- a/fast/stages/02-security/core-prod.tf +++ b/fast/stages/02-security/core-prod.tf @@ -15,7 +15,7 @@ */ module "prod-sec-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "prod-sec-core-0" parent = var.folder_id prefix = var.prefix @@ -29,7 +29,7 @@ module "prod-sec-project" { module "prod-sec-kms" { for_each = toset(local.kms_locations) - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + source = "../../../modules/kms" project_id = module.prod-sec-project.project_id keyring = { location = each.key diff --git a/fast/stages/02-security/vpc-sc.tf b/fast/stages/02-security/vpc-sc.tf index f22added..855dc1dd 100644 --- a/fast/stages/02-security/vpc-sc.tf +++ b/fast/stages/02-security/vpc-sc.tf @@ -44,7 +44,7 @@ locals { } module "vpc-sc" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/vpc-sc?ref=ea17e65" + source = "../../../modules/vpc-sc" # only enable if we have projects defined for perimeters count = anytrue([for k, v in local.vpc_sc_counts : v > 0]) ? 1 : 0 access_policy = null From 61e011d2854bde4a279622e401444f38cfc3736d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:18:41 +0100 Subject: [PATCH 023/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/00-bootstrap/README.md | 314 +++++++++++++++++++++++ fast/stages/00-bootstrap/automation.tf | 107 ++++++++ fast/stages/00-bootstrap/billing.tf | 112 ++++++++ fast/stages/00-bootstrap/diagram.png | Bin 0 -> 42583 bytes fast/stages/00-bootstrap/log-export.tf | 73 ++++++ fast/stages/00-bootstrap/main.tf | 32 +++ fast/stages/00-bootstrap/organization.tf | 205 +++++++++++++++ fast/stages/00-bootstrap/outputs.tf | 113 ++++++++ fast/stages/00-bootstrap/variables.tf | 100 ++++++++ 9 files changed, 1056 insertions(+) create mode 100644 fast/stages/00-bootstrap/README.md create mode 100644 fast/stages/00-bootstrap/automation.tf create mode 100644 fast/stages/00-bootstrap/billing.tf create mode 100644 fast/stages/00-bootstrap/diagram.png create mode 100644 fast/stages/00-bootstrap/log-export.tf create mode 100644 fast/stages/00-bootstrap/main.tf create mode 100644 fast/stages/00-bootstrap/organization.tf create mode 100644 fast/stages/00-bootstrap/outputs.tf create mode 100644 fast/stages/00-bootstrap/variables.tf diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md new file mode 100644 index 00000000..15d0d500 --- /dev/null +++ b/fast/stages/00-bootstrap/README.md @@ -0,0 +1,314 @@ +# Organization bootstrap + +The main purpose of this stage is to enable critical organization-level functionality that depends on broad administrative permissions, and to prepare the prerequisites needed to enable automation in this and future stages. + +It is intentionally simple, to minimize usage of administrative-level permissions and enable simple auditing and troubleshooting, and only deals with three sets of resources: + +- project, service accounts, and GCS buckets for automation +- projects, BQ datasets, and sinks for audit log and billing exports +- IAM bindings on the organization + +The following diagram can be used as a simple high level reference for the following sections, which describe the stage and its possible customizations in detail. + +![Organization-level diagram](diagram.png) + +## Design overview and choices + +As mentioned above, this stage only does the bare minimum required to bootstrap automation, and to ensure that base audit and billing exports are in place from the start to provide some measure of accountability, even before the security configurations are applied in a later stage. + +It also sets up organization-level IAM bindings so the Organization Administrator role is only used here, trading off some design freedom for ease of auditing and troubleshooting, and reducing the risk of costly security mistakes down the line. The only exception to this rule is for the [Resource Management stage](../01-resman) service account, and is described below. + +### User groups + +User groups are particularly important, not only here but throughout the whole automation process, as they provide a stable frame of reference that allows decoupling the final set of permissions for each group, from the stage where entities and resources are created and their IAM bindings defined. As an example, the final set of roles for the networking group is contributed by this stage at the organization level (XPN Admin, Cloud Asset Viewer, etc.), and by the Resource Management stage at the folder level. + +To simplify adoption, we have standardized the initial set of groups on those outlined in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist), as they provide a comprehensive and flexible starting point that can suit most users. Adding new groups, or deviating from the initial setup is of course possible and reasonably simple, and it's briefly outlined in the customization section below. + +### Organization-level IAM + +The service account used in the [Resource Management stage](../01-resman) needs to be able to grant specific roles at the organizational level (`roles/billing.user`, `roles/compute.xpnAdmin`, etc.), to enable specific functionality for subsequent stages that deal with network or security resources, or billing-related activities. + +In order to be able to assign those roles without having the full authority of the Organization Admin role, this stage defines a custom role that only allows setting IAM policies on the organization, and grants it via a [delegated role grant](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) that only allows it to be used to grant a limited subset of roles. + +In this way, the Resource Management service account can effectively act as an Organization Admin, but only to grant the roles it effectively needs to control. + +One consequence of the above setup, is the need to configure IAM bindings as non-authoritative for the roles included in the IAM condition, since those same roles are effectively under control of two stages: this one, and Resource Management. Using authoritative bindings for these roles instead of non-authoritative ones, would generate potential conflicts where each stage tries to overwrite and negate the bindings applied by the other at each `apply` cycle. + +### Automation project and resources + +One other design choice worth mentioning here, is the use of a single automation project for all foundational stages, trading off some complexity on the API side (single source for usage quota, multiple service activation) for increased flexibility and simpler operations, while still effectively providing the same degree of separation via resource-level IAM. + +### Billing account + +We support three use cases in regards to billing: + +- the billing account is part of this same organization, IAM bindings will be set at the organization level +- the billing account is part of a different organization, billing IAM bindings will be set at the organization level in the billing account owning organization +- the billing account is not considered part of an organization (even though it might be), billing IAM bindings are set on the billing account itself + +For same-organization billing, we configure a custom organization role that can set IAM bindings, via a delegated role grant to limit its scope to the relevant roles. + +For details on how to configure the different billing account modes, refer to the [How to run this stage](#how-to-run-this-stage) section below. + +### Naming + +We are intentionally not supporting random prefix/suffixes for names, as that is an antipattern that is typically only used in development, and does not map to the actual production usage we see at customers, who always adopt a fixed naming convention. + +What is implemented here is a fairly common convention, composed of tokens ordered by relative importance: + +- a static prefix (e.g. `myco` or `myco-gcp`) +- an environment identifier (e.g. `prod`) +- a team/owner identifier (e.g. `sec` for Security) +- a context identifier (e.g. `core` or `kms`) +- an arbitrary identifier used to distinguish similar resources (e.g. `0`, `1`) + +Tokens are joined by a `-` character, which makes it easy to visually separate the individual tokens, and allows to programmatically split them in billing exports to derive initial high-level groupings for cost attribution. + +The convention is used in its full form only for specific resources which have globally unique names (projects, GCS buckets), other resources adopt a shorter version for legibility, as the full context can always be derived from their project. + +The [Customizations](#names-and-naming-convention) section on names below explains how to configure tokens, or how to implement a different naming convention. + +## How to run this stage + +This stage has very simple initial requirements, as it is designed to work on newly created GCP organizations. Four steps are needed to bring up this stage: + +- an Organization Admin self-assigns the required roles listed below +- the same administrator runs the first `init/apply` sequence passing a special variable to `apply` +- the providers configuration file is derived from the Terraform output or linked from the generated file +- a second `init` is run to migrate state, and from then on the stage is run via impersonation + +### Prerequisites + +The roles that the Organization Admin used in the first `apply` needs to self-grant are: + +- Billing Account Administrator (`roles/billing.admin`) + either on the org (if the billing account has been moved to the org) or on the billing account +- Logging Admin (`roles/logging.admin`) +- Organization Role Administrator (`roles/iam.organizationRoleAdmin`) +- Organization Administrator (`roles/resourcemanager.organizationAdmin`) +- Project Creator (`roles/resourcemanager.projectCreator`) + +To quickly self-grant the above roles, run the following code snippet as the initial Organization Admin: + +```bash +export BOOTSTRAP_ORG_ID=123456 +export BOOTSTRAP_USER=$(gcloud config list --format 'value(core.account)') +export BOOTSTRAP_ROLES=(roles/billing.admin roles/logging.admin roles/iam.organizationRoleAdmin roles/resourcemanager.projectCreator) +for role in $BOOTSTRAP_ROLES; do + gcloud organizations add-iam-policy-binding $BOOTSTRAP_ORG_ID \ + --member user:$BOOTSTRAP_USER --role $role +done +``` + +#### Billing account in a different organization + +If you are using a billing account belonging to a different organization (e.g. in multiple organization setups), some initial configurations are needed to ensure the identities running this stage can assign billing-related roles. + +If the billing organization is managed by another version of this stage, we leverage the `organizationIamAdmin` created there, to allow restricted granting of billing roles at the organization level. + +If that's not the case, an equivalent role needs to exist, or the predefined `resourcemanager.organizationAdmin` role can be used if it's not managed authoritatively. The role name then needs to be manually changed in the `billing.tf` file, in the `google_organization_iam_binding` resource. + +The identity applying this stage for the first time also needs two roles in billing organization, they can be removed after the first `apply` completes successfully: + +```bash +export BILLING_ORG_ID=789012 +export BILLING_ROLES=(roles/billing.admin roles/resourcemanager.organizationAdmin) +for role in $BILLING_ROLES; do + gcloud organizations add-iam-policy-binding $BILLING_ORG_ID \ + --member user:$BOOTSTRAP_USER --role $role +done +``` + +#### Standalone billing account + +If you are using a standalone billing account, the identity applying this stage for the first time needs to be a billing account administrator: + +```bash +export BILLING_ACCOUNT_ID=ABCD-01234-ABCD +gcloud beta billing accounts add-iam-policy-binding $BILLING_ACCOUNT \ + --member user:$BOOTSTRAP_USER --role roles/billing.admin +``` + +#### Groups + +Before the first run the following IAM groups must exist to allow IAM bindings to be created (actual names are flexible, see the [Customization](#customizations) section): + +- gcp-billing-admins +- gcp-devops +- gcp-network-admins +- gcp-organization-admins +- gcp-security-admins +- gcp-support + +#### Configure variables + +Then make sure you have configured the correct values for the following variables, by editing the defaults in `variables.tf` or providing a `terraform.tfvars` file (preferred): + +- `billing_account` + an object containing the id of your billing account, derived from the Cloud Console UI or by running `gcloud beta billing accounts list`, and the id of the organization owning it, or `null` to use the billing account in isolation +- `groups` + the name mappings for your groups, if you're following the default convention you can leave this to the provided default +- `organization.id`, `organization.domain`, `organization.customer_id` + the id, domain and customer id of your organization, derived from the Cloud Console UI or by running `gcloud organizations list` +- `prefix` + the fixed prefix used in your naming convention + +### Output files and cross-stage variables + +At any time during the life of this stage, you can configure it to automatically generate providers configuration and variable files, to simplify exchanging inputs and outputs between stages and avoid having to edit files manually. + +This is disabled by default, to enable the mechanism just set the `outputs_location` variable to a valid path on a local filesystem, e.g. + +```hcl +outputs_location = "../../configs" +``` + +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 simply link these files in the relevant stages, instead of having to manually transfer outputs from one stage, to Terraform variables in another. + +This is the outline of the output files generated by this stage: + +```bash +[path specified in outputs_location] +├── 00-bootstrap +│   ├── providers.tf +├── 01-resman +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 02-networking +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 02-security +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-gke-multitenant-dev +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-gke-multitenant-prod +│   ├── providers.tf +│   ├── terraform-bootstrap.auto.tfvars.json +├── 03-project-factory-dev +│   └── terraform-bootstrap.auto.tfvars.json +├── 03-project-factory-prod +│   └── terraform-bootstrap.auto.tfvars.json +``` + +### 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: + +```bash +terraform init +terraform apply \ + -var bootstrap_user=$(gcloud config list --format 'value(core.account)') +``` + +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/* ./ +# or from outputs if not using output files +terraform output -json providers | jq -r '.["00-bootstrap"]' \ + | tee providers.tf +# migrate state to GCS bucket configured in providers file +terraform init -migrate-state +``` + +## Customizations + +Most of the variables (e.g. `billing_account` and `organization`) are only used to input actual values and should be self-explanatory. The only meaningful customizations that apply here are groups, and IAM roles. + +### Group names + +As we mentioned above, groups reflect the convention used in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist), with an added level of indirection: the `groups` variable maps logical names to actual names, so that you don't need to delve into the code if your group names do not comply with the checklist convention. + +For example, if your network admins team is called `net-rockstars@example.com`, simply set that name in the variable, minus the domain which is interpolated internally with the organization domain: + +```hcl +variable "groups" { + description = "Group names to grant organization-level permissions." + type = map(string) + default = { + gcp-network-admins = "net-rockstars" + # [...] + } +} +``` + +If your groups layout differs substantially from the checklist, define all relevant groups in the `groups` variable, then rearrange IAM roles in the code to match your setup. + +### IAM + +One other area where we directly support customizations is IAM. The code here, as in all other stages, follows a simple pattern derived from best practices: + +- operational roles for humans are assigned to groups +- any other principal is a service account + +In code, the distinction above reflects on how IAM bindings are specified in the underlying module variables: + +- group roles "for humans" always use `iam_groups` variables +- service account roles always use `iam` variables + +This makes it easy to tweak user roles by adding mappings to the `iam_groups` variables of the relevant resources, without having to understand and deal with the details of service account roles. + +In those cases where roles need to be assigned to end-user service accounts (e.g. an application or pipeline service account), we offer a stage-level `iam` variable that allows pinpointing individual role/members pairs, without having to touch the code internals, to avoid the risk of breaking a critical role for a robot account. The variable can also be used to assign roles to specific users or to groups external to the organization, e.g. to support external suppliers. + +The one exception to this convention is for roles which are part of the delegated grant condition described above, and which can then be assigned from other stages. In this case, use the `iam_additive` variable as they are implemented with non-authoritative resources. Using non-authoritative bindings ensure that re-executing this stage will not override any bindings set in downstream stages. + +### Names and naming convention + +Configuring the individual tokens for the naming convention described above, has varying degrees of complexity: + +- the static prefix can be set via the `prefix` variable once +- the environment identifier is set to `prod` as resources here influence production and are considered as such, and can be changed in `main.tf` locals + +All other tokens are set directly in resource names, as providing abstractions to manage them would have added too much complexity to the code, making it less readable and more fragile. + +If a different convention is needed, identify names via search/grep (e.g. with `^\s+name\s+=\s+"`) and change them in an editor: it should take a couple of minutes at most, as there's just a handful of modules and resources to change. + +Names used in internal references (e.g. `module.foo-prod.id`) are only used by Terraform and do not influence resource naming, so they are best left untouched to avoid having to debug complex errors. + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [automation.tf](./automation.tf) | Automation project and resources. | gcs · iam-service-account · project | | +| [billing.tf](./billing.tf) | Billing export project and dataset. | bigquery-dataset · organization · project | google_billing_account_iam_member · google_organization_iam_binding | +| [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [organization.tf](./organization.tf) | Organization-level IAM and org policies. | organization | google_organization_iam_binding | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | | +| organization | Organization details. | object({…}) | ✓ | | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | | +| bootstrap_user | Email of the nominal user running this stage for the first time. | string | | null | | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | | +| iam | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| iam_additive | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | +| log_sinks | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| billing_dataset | BigQuery dataset prepared for billing export. | | | +| project_ids | Projects created by this stage. | | | +| providers | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| tfvars | Terraform variable files for the following stages. | ✓ | | + + + + + + + diff --git a/fast/stages/00-bootstrap/automation.tf b/fast/stages/00-bootstrap/automation.tf new file mode 100644 index 00000000..8498df00 --- /dev/null +++ b/fast/stages/00-bootstrap/automation.tf @@ -0,0 +1,107 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Automation project and resources. + +module "automation-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + billing_account = var.billing_account.id + name = "iac-core-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + # human (groups) IAM bindings + group_iam = { + (local.groups.gcp-devops) = [ + "roles/iam.serviceAccountAdmin", + "roles/iam.serviceAccountTokenCreator", + ] + (local.groups.gcp-organization-admins) = [ + "roles/iam.serviceAccountTokenCreator", + ] + } + # machine (service accounts) IAM bindings + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + "roles/iam.serviceAccountAdmin" = [ + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + "roles/storage.admin" = [ + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + } + services = [ + "accesscontextmanager.googleapis.com", + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "billingbudgets.googleapis.com", + "cloudbilling.googleapis.com", + "cloudkms.googleapis.com", + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "essentialcontacts.googleapis.com", + "iam.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "serviceusage.googleapis.com", + "stackdriver.googleapis.com", + "storage-component.googleapis.com", + "storage.googleapis.com", + ] +} + +# this stage's bucket and service account + +module "automation-tf-bootstrap-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "iac-core-bootstrap-0" + prefix = local.prefix + versioning = true + depends_on = [module.organization] +} + +module "automation-tf-bootstrap-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "bootstrap-0" + description = "Terraform organization bootstrap service account." + prefix = local.prefix +} + +# resource hierarchy stage's bucket and service account + +module "automation-tf-resman-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "iac-core-resman-0" + prefix = local.prefix + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.automation-tf-resman-sa.iam_email] + } + depends_on = [module.organization] +} + +module "automation-tf-resman-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = module.automation-project.project_id + name = "resman-0" + description = "Terraform organization bootstrap service account." + prefix = local.prefix +} diff --git a/fast/stages/00-bootstrap/billing.tf b/fast/stages/00-bootstrap/billing.tf new file mode 100644 index 00000000..7d5bcf09 --- /dev/null +++ b/fast/stages/00-bootstrap/billing.tf @@ -0,0 +1,112 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Billing export project and dataset. + +locals { + # used here for convenience, in organization.tf members are explicit + billing_ext_admins = [ + local.groups_iam.gcp-organization-admins, + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] +} + +# billing account in same org (IAM is in the organization.tf file) + +moved { + from = module.billing-export-project + to = module.billing-export-project.0 +} + +module "billing-export-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + count = local.billing_org ? 1 : 0 + billing_account = var.billing_account.id + name = "billing-export-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + } + services = [ + # "cloudresourcemanager.googleapis.com", + # "iam.googleapis.com", + # "serviceusage.googleapis.com", + "bigquery.googleapis.com", + "bigquerydatatransfer.googleapis.com", + "storage.googleapis.com" + ] +} + +moved { + from = module.billing-export-dataset + to = module.billing-export-dataset.0 +} + +module "billing-export-dataset" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + count = local.billing_org ? 1 : 0 + project_id = module.billing-export-project.0.project_id + id = "billing_export" + friendly_name = "Billing export." +} + +# billing account in a different org + +module "billing-organization-ext" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + count = local.billing_org_ext ? 1 : 0 + organization_id = "organizations/${var.billing_account.organization_id}" + iam_additive = { + "roles/billing.admin" = local.billing_ext_admins + } +} + + +resource "google_organization_iam_binding" "billing_org_ext_admin_delegated" { + # refer to organization.tf for the explanation of how this binding works + count = local.billing_org_ext ? 1 : 0 + org_id = var.billing_account.organization_id + # if the billing org does not have our custom role, user the predefined one + # role = "roles/resourcemanager.organizationAdmin" + role = "organizations/${var.billing_account.organization_id}/roles/organizationIamAdmin" + members = [module.automation-tf-resman-sa.iam_email] + condition { + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/billing.costsManager", + "roles/billing.user", + ] + )) + ) + } + depends_on = [module.billing-organization-ext] +} + +# standalone billing account + +resource "google_billing_account_iam_member" "billing_ext_admin" { + for_each = toset( + local.billing_ext ? local.billing_ext_admins : [] + ) + billing_account_id = var.billing_account.id + role = "roles/billing.admin" + member = each.key +} diff --git a/fast/stages/00-bootstrap/diagram.png b/fast/stages/00-bootstrap/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..932dfa8d869f3fb88a155c4d5a54e594bc4a1c4c GIT binary patch literal 42583 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�p0B0Sg}RnZKNt5SPJf~{W5LM8Z)&U; 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 H=h*)Mp1sHv literal 0 HcmV?d00001 diff --git a/fast/stages/00-bootstrap/log-export.tf b/fast/stages/00-bootstrap/log-export.tf new file mode 100644 index 00000000..f6fb51a5 --- /dev/null +++ b/fast/stages/00-bootstrap/log-export.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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Audit log project and sink. + +locals { + log_types = toset([for k, v in var.log_sinks : v.type]) +} + +module "log-export-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "audit-logs-0" + parent = "organizations/${var.organization.id}" + prefix = local.prefix + billing_account = var.billing_account.id + iam = { + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + } + services = [ + # "cloudresourcemanager.googleapis.com", + # "iam.googleapis.com", + # "serviceusage.googleapis.com", + "bigquery.googleapis.com", + "storage.googleapis.com", + "stackdriver.googleapis.com" + ] +} + +# one log export per type, with conditionals to skip those not needed + +module "log-export-dataset" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + count = contains(local.log_types, "bigquery") ? 1 : 0 + project_id = module.log-export-project.project_id + id = "audit_export" + friendly_name = "Audit logs export." +} + +module "log-export-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + count = contains(local.log_types, "storage") ? 1 : 0 + project_id = module.log-export-project.project_id + name = "audit-logs-0" + prefix = local.prefix +} + +module "log-export-logbucket" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/logging-bucket?ref=v12.0.0" + count = contains(local.log_types, "logging") ? 1 : 0 + parent_type = "project" + parent = module.log-export-project.project_id + id = "audit-logs-0" +} + +module "log-export-pubsub" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/pubsub?ref=v12.0.0" + for_each = toset([for k, v in var.log_sinks : k if v == "pubsub"]) + project_id = module.log-export-project.project_id + name = "audit-logs-${each.key}" +} diff --git a/fast/stages/00-bootstrap/main.tf b/fast/stages/00-bootstrap/main.tf new file mode 100644 index 00000000..49fa9140 --- /dev/null +++ b/fast/stages/00-bootstrap/main.tf @@ -0,0 +1,32 @@ +/** + * 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 { + groups = { + for k, v in var.groups : + k => "${v}@${var.organization.domain}" + } + groups_iam = { + for k, v in local.groups : + k => "group:${v}" + } + # convenience flags that express where billing account resides + billing_ext = var.billing_account.organization_id == null + billing_org = var.billing_account.organization_id == var.organization.id + billing_org_ext = !local.billing_ext && !local.billing_org + # naming: environment used in most resource names + prefix = join("-", compact([var.prefix, "prod"])) +} diff --git a/fast/stages/00-bootstrap/organization.tf b/fast/stages/00-bootstrap/organization.tf new file mode 100644 index 00000000..3bf04b73 --- /dev/null +++ b/fast/stages/00-bootstrap/organization.tf @@ -0,0 +1,205 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization-level IAM and org policies. + +locals { + # organization authoritative IAM bindings, in an easy to edit format before + # they are combined with var.iam a bit further in locals + _iam = { + "roles/browser" = [ + "domain:${var.organization.domain}" + ] + "roles/logging.admin" = [ + module.automation-tf-bootstrap-sa.iam_email + ] + "roles/owner" = local._iam_bootstrap_user + "roles/resourcemanager.folderAdmin" = [ + module.automation-tf-resman-sa.iam_email + ] + "roles/resourcemanager.organizationAdmin" = concat( + [module.automation-tf-bootstrap-sa.iam_email], + local._iam_bootstrap_user + ) + "roles/resourcemanager.organizationViewer" = [ + "domain:${var.organization.domain}" + ] + } + # organization additive IAM bindings, in an easy to edit format before + # they are combined with var.iam_additive a bit further in locals + _iam_additive = merge( + { + "roles/accesscontextmanager.policyAdmin" = [ + local.groups_iam.gcp-security-admins + ] + "roles/compute.orgFirewallPolicyAdmin" = [ + local.groups_iam.gcp-network-admins + ] + "roles/compute.xpnAdmin" = [ + local.groups_iam.gcp-network-admins + ] + # use additive to support cross-org roles for billing + "roles/iam.organizationRoleAdmin" = [ + local.groups_iam.gcp-security-admins, + module.automation-tf-bootstrap-sa.iam_email + ] + "roles/orgpolicy.policyAdmin" = [ + module.automation-tf-resman-sa.iam_email, + local.groups_iam.gcp-security-admins + ] + }, + local.billing_org ? { + "roles/billing.admin" = [ + local.groups_iam.gcp-organization-admins, + module.automation-tf-bootstrap-sa.iam_email, + module.automation-tf-resman-sa.iam_email + ] + } : {} + ) + _iam_bootstrap_user = ( + var.bootstrap_user == null ? [] : ["user:${var.bootstrap_user}"] + ) + _log_sink_destinations = { + bigquery = try(module.log-export-dataset.0.id, null), + logging = try(module.log-export-logbucket.0.id, null), + storage = try(module.log-export-gcs.0.name, null) + } + iam = { + for role in local.iam_roles : role => distinct(concat( + try(sort(local._iam[role]), []), + try(sort(var.iam[role]), []) + )) + } + iam_additive = { + for role in local.iam_roles_additive : role => distinct(concat( + try(sort(local._iam_additive[role]), []), + try(sort(var.iam_additive[role]), []) + )) + } + iam_roles = distinct(concat( + keys(local._iam), keys(var.iam) + )) + iam_roles_additive = distinct(concat( + keys(local._iam_additive), keys(var.iam_additive) + )) + log_sink_destinations = { + for k, v in var.log_sinks : k => ( + v.type == "pubsub" + ? module.log-export-pubsub[k] + : local._log_sink_destinations[v.type] + ) + } +} + +module "organization" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + organization_id = "organizations/${var.organization.id}" + # human (groups) IAM bindings + group_iam = { + (local.groups.gcp-organization-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.admin", + "roles/compute.osAdminLogin", + "roles/compute.osLoginExternalUser", + "roles/owner", + "roles/resourcemanager.folderAdmin", + "roles/resourcemanager.organizationAdmin", + "roles/resourcemanager.projectCreator", + ], + (local.groups.gcp-network-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.techSupportEditor", + ] + (local.groups.gcp-security-admins) = [ + "roles/cloudasset.owner", + "roles/cloudsupport.techSupportEditor", + "roles/iam.securityReviewer", + "roles/logging.admin", + "roles/securitycenter.admin", + ], + (local.groups.gcp-support) = [ + "roles/cloudsupport.techSupportEditor", + "roles/logging.viewer", + "roles/monitoring.viewer", + ] + } + # machine (service accounts) IAM bindings + iam = local.iam + # additive bindings, used for roles co-managed by different stages + iam_additive = local.iam_additive + custom_roles = { + # this is needed for use in additive IAM bindings, to avoid conflicts + "organizationIamAdmin" = [ + "resourcemanager.organizations.get", + "resourcemanager.organizations.getIamPolicy", + "resourcemanager.organizations.setIamPolicy" + ] + "xpnServiceAdmin" = [ + "compute.globalOperations.get", + "compute.organizations.disableXpnResource", + "compute.organizations.enableXpnResource", + "compute.projects.get", + "compute.subnetworks.getIamPolicy", + "compute.subnetworks.setIamPolicy", + "dns.networks.bindPrivateDNSZone", + "resourcemanager.projects.get", + ] + } + logging_sinks = { + for name, attrs in var.log_sinks : name => { + bq_partitioned_table = attrs.type == "bigquery" + destination = local.log_sink_destinations[name] + exclusions = {} + filter = attrs.filter + iam = true + include_children = true + type = attrs.type + } + } +} + +# assign the custom restricted Organization Admin role to the relevant service +# accounts, with a condition that only enables granting specific roles; +# these roles use additive bindings everywhere to avoid conflicts / permadiffs + +resource "google_organization_iam_binding" "org_admin_delegated" { + org_id = var.organization.id + count = local.billing_org ? 1 : 0 + role = module.organization.custom_role_id.organizationIamAdmin + members = [module.automation-tf-resman-sa.iam_email] + condition { + title = "automation_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", concat( + [ + "roles/accesscontextmanager.policyAdmin", + "roles/compute.orgFirewallPolicyAdmin", + "roles/compute.xpnAdmin", + "roles/orgpolicy.policyAdmin", + module.organization.custom_role_id.xpnServiceAdmin + ], + local.billing_org ? [ + "roles/billing.admin", + "roles/billing.costsManager", + "roles/billing.user", + ] : [] + ))) + ) + } + depends_on = [module.organization] +} diff --git a/fast/stages/00-bootstrap/outputs.tf b/fast/stages/00-bootstrap/outputs.tf new file mode 100644 index 00000000..c25b9013 --- /dev/null +++ b/fast/stages/00-bootstrap/outputs.tf @@ -0,0 +1,113 @@ +/** + * 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 { + providers = { + "00-bootstrap" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-sa.email + }) + "01-resman" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-sa.email + }) + } + tfvars = { + "01-resman" = jsonencode({ + automation_project_id = module.automation-project.project_id + billing_account = var.billing_account + custom_roles = module.organization.custom_role_id + groups = var.groups + organization = var.organization + prefix = var.prefix + }) + "02-networking" = jsonencode({ + billing_account_id = var.billing_account.id + organization = var.organization + prefix = var.prefix + }) + "02-security" = jsonencode({ + billing_account_id = var.billing_account.id + organization = var.organization + prefix = var.prefix + }) + "03-gke-multitenant-dev" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-gke-multitenant-prod" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-project-factory-dev" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + "03-project-factory-prod" = jsonencode({ + billing_account_id = var.billing_account.id + prefix = var.prefix + }) + } +} + +# optionally generate providers and tfvars files for subsequent stages + +resource "local_file" "providers" { + for_each = var.outputs_location == null ? {} : local.providers + filename = "${var.outputs_location}/${each.key}/providers.tf" + content = each.value +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-bootstrap.auto.tfvars.json" + content = each.value +} + +# outputs + +output "billing_dataset" { + description = "BigQuery dataset prepared for billing export." + value = try(module.billing-export-dataset.0.id, null) +} + +output "project_ids" { + description = "Projects created by this stage." + value = { + automation = module.automation-project.project_id + billing-export = try(module.billing-export-project.0.project_id, null) + log-export = module.log-export-project.project_id + } +} + +# ready to use provider configurations for subsequent stages when not using files + +output "providers" { + # tfdoc:output:consumers stage-01 + description = "Terraform provider files for this stage and dependent stages." + sensitive = true + value = local.providers +} + +# ready to use variable values for subsequent stages + +output "tfvars" { + description = "Terraform variable files for the following stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/00-bootstrap/variables.tf b/fast/stages/00-bootstrap/variables.tf new file mode 100644 index 00000000..9f102c77 --- /dev/null +++ b/fast/stages/00-bootstrap/variables.tf @@ -0,0 +1,100 @@ +/** + * 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 "billing_account" { + description = "Billing account id and organization id ('nnnnnnnn' or null)." + type = object({ + id = string + organization_id = number + }) +} + +variable "bootstrap_user" { + description = "Email of the nominal user running this stage for the first time." + type = string + default = null +} + +variable "groups" { + # https://cloud.google.com/docs/enterprise/setup-checklist + description = "Group names to grant organization-level permissions." + type = map(string) + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "iam" { + description = "Organization-level custom IAM settings in role => [principal] format." + type = map(list(string)) + default = {} +} + +variable "iam_additive" { + description = "Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings." + type = map(list(string)) + default = {} +} + +variable "log_sinks" { + description = "Org-level log sinks, in name => {type, filter} format." + type = map(object({ + filter = string + type = string + })) + default = { + audit-logs = { + filter = "logName:\"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName:\"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" + type = "bigquery" + } + vpc-sc = { + filter = "protoPayload.metadata.@type=\"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata\"" + type = "bigquery" + } + } + validation { + condition = alltrue([ + for k, v in var.log_sinks : + contains(["bigquery", "logging", "pubsub", "storage"], v.type) + ]) + error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'." + } +} + +variable "organization" { + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} From c154ac878ed1642a560aae8e8d15187a8d624be3 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:23:01 +0100 Subject: [PATCH 024/132] merge tools changes --- tools/REQUIREMENTS.txt | 1 + tools/check_boilerplate.py | 37 ++++++++++++++++--------------- tools/check_documentation.py | 28 +++++++++++++++++------ tools/validate_schema.py | 43 ++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 25 deletions(-) create mode 100755 tools/validate_schema.py diff --git a/tools/REQUIREMENTS.txt b/tools/REQUIREMENTS.txt index dca9a909..f53a35cc 100644 --- a/tools/REQUIREMENTS.txt +++ b/tools/REQUIREMENTS.txt @@ -1 +1,2 @@ click +yamale diff --git a/tools/check_boilerplate.py b/tools/check_boilerplate.py index 6bbd4cb2..f4ec5273 100755 --- a/tools/check_boilerplate.py +++ b/tools/check_boilerplate.py @@ -33,22 +33,23 @@ _MATCH_STRING = ( _MATCH_RE = re.compile(_MATCH_STRING, re.M) -def main(dir): - "Cycle through files in dir and check for the Apache 2.0 boilerplate." +def main(base_dirs): + "Cycle through files in base_dirs and check for the Apache 2.0 boilerplate." errors, warnings = [], [] - for root, dirs, files in os.walk(dir): - dirs[:] = [d for d in dirs if d not in _EXCLUDE_DIRS] - for fname in files: - if fname in _MATCH_FILES or os.path.splitext(fname)[1] in _MATCH_FILES: - fpath = os.path.abspath(os.path.join(root, fname)) - content = open(fpath).read() - if _EXCLUDE_RE.search(content): - continue - try: - if not _MATCH_RE.search(content): - errors.append(fpath) - except (IOError, OSError): - warnings.append(fpath) + for dir in base_dirs: + for root, dirs, files in os.walk(dir): + dirs[:] = [d for d in dirs if d not in _EXCLUDE_DIRS] + for fname in files: + if fname in _MATCH_FILES or os.path.splitext(fname)[1] in _MATCH_FILES: + fpath = os.path.abspath(os.path.join(root, fname)) + content = open(fpath).read() + if _EXCLUDE_RE.search(content): + continue + try: + if not _MATCH_RE.search(content): + errors.append(fpath) + except (IOError, OSError): + warnings.append(fpath) if warnings: print('The following files cannot be accessed:') print('\n'.join(' - {}'.format(s) for s in warnings)) @@ -59,6 +60,6 @@ def main(dir): if __name__ == '__main__': - if len(sys.argv) != 2: - raise SystemExit('No directory passed.') - main(sys.argv[1]) + if len(sys.argv) < 2: + raise SystemExit('No directory to check.') + main(sys.argv[1:]) diff --git a/tools/check_documentation.py b/tools/check_documentation.py index 1968f3b4..3996928b 100755 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import difflib import enum import pathlib @@ -32,6 +33,7 @@ def _check_dir(dir_name, files=False, show_extra=False): for readme_path in dir_path.glob('**/README.md'): if '.terraform' in str(readme_path): continue + diff = None readme = readme_path.read_text() mod_name = str(readme_path.relative_to(dir_path).parent) result = tfdoc.get_doc(readme) @@ -44,24 +46,36 @@ def _check_dir(dir_name, files=False, show_extra=False): except SystemExit: state = state.SKIP else: - state = State.OK if new_doc == result['doc'] else State.FAIL - yield mod_name, state + if new_doc == result['doc']: + state = State.OK + else: + state = State.FAIL + diff = '\n'.join( + [f'----- {mod_name} diff -----\n'] + + list(difflib.ndiff( + result['doc'].split('\n'), new_doc.split('\n') + ))) + yield mod_name, state, diff @click.command() @click.argument('dirs', type=str, nargs=-1) -@ click.option('--show-extra/--no-show-extra', default=False) @ click.option('--files/--no-files', default=False) -def main(dirs, files=False, show_extra=False): +@ click.option('--show-diffs/--no-show-diffs', default=False) +@ click.option('--show-extra/--no-show-extra', default=False) +def main(dirs, files=False, show_diffs=False, show_extra=False): 'Cycle through modules and ensure READMEs are up-to-date.' - errors = 0 + errors = [] state_labels = {State.FAIL: '✗', State.OK: '✓', State.SKIP: '?'} for dir_name in dirs: print(f'----- {dir_name} -----') - for mod_name, state in _check_dir(dir_name, files, show_extra): - errors += 1 if state == State.FAIL else 0 + for mod_name, state, diff in _check_dir(dir_name, files, show_extra): + if state == State.FAIL: + errors.append(diff) print(f'[{state_labels[state]}] {mod_name}') if errors: + if show_diffs: + print('\n'.join(errors)) raise SystemExit('Errors found.') diff --git a/tools/validate_schema.py b/tools/validate_schema.py new file mode 100755 index 00000000..77ca29ec --- /dev/null +++ b/tools/validate_schema.py @@ -0,0 +1,43 @@ +import glob +import os + +import click +import yamale + + +@ click.command() +@ click.argument('schema', type=click.Path(exists=True)) +@ click.option('--directory', multiple=True, type=click.Path(exists=True, file_okay=False, dir_okay=True)) +@ click.option('--file', multiple=True, type=click.Path(exists=True, file_okay=True, dir_okay=False)) +@ click.option('--recursive', is_flag=True, default=False) +@ click.option('--quiet', is_flag=True, default=False) +def main(directory=None, file=None, schema=None, recursive=False, quiet=False): + 'Program entry point.' + + yamale_schema = yamale.make_schema(schema) + search = "**/*.yaml" if recursive else "*.yaml" + has_errors = [] + + files = list(file) + for d in directory: + files = files + glob.glob(os.path.join(d, search), recursive=recursive) + + for document in files: + yamale_data = yamale.make_data(document) + try: + yamale.validate(yamale_schema, yamale_data) + if quiet: + pass + else: + print(f'✅ {document} -> {os.path.basename(schema)}') + except ValueError as e: + has_errors.append(document) + print(e) + print(f'❌ {document} -> {os.path.basename(schema)}') + + if len(has_errors) > 0: + raise SystemExit(f"❌ Errors found in {len(has_errors)} documents.") + + +if __name__ == '__main__': + main() From dcf0a40aa7bdf59131e886743074e9f389278963 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Mon, 17 Jan 2022 10:22:48 +0100 Subject: [PATCH 025/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/02-networking/README.md | 353 ++++++++++++++++++ fast/stages/02-networking/data/cidrs.yaml | 15 + .../data/dashboards/firewall_insights.json | 68 ++++ .../02-networking/data/dashboards/vpn.json | 248 ++++++++++++ .../data/firewall-rules/landing/rules.yaml | 15 + .../data/hierarchical-policy-rules.yaml | 49 +++ .../data/subnets/dev/dev-default-ew1.yaml | 5 + .../subnets/landing/landing-default-ew1.yaml | 5 + .../data/subnets/prod/prod-default-ew1.yaml | 5 + .../data/subnets/prod/prod-gke-ew1.yaml | 8 + fast/stages/02-networking/diagram.png | Bin 0 -> 141184 bytes fast/stages/02-networking/dns-dev.tf | 53 +++ fast/stages/02-networking/dns-landing.tf | 93 +++++ fast/stages/02-networking/dns-prod.tf | 53 +++ fast/stages/02-networking/main.tf | 72 ++++ fast/stages/02-networking/monitoring.tf | 32 ++ fast/stages/02-networking/outputs.tf | 95 +++++ fast/stages/02-networking/test-resources.tf | 100 +++++ fast/stages/02-networking/variables.tf | 253 +++++++++++++ fast/stages/02-networking/vpc-landing.tf | 93 +++++ fast/stages/02-networking/vpc-spoke-dev.tf | 105 ++++++ fast/stages/02-networking/vpc-spoke-prod.tf | 105 ++++++ fast/stages/02-networking/vpn-onprem.tf | 50 +++ fast/stages/02-networking/vpn-spoke-dev.tf | 73 ++++ fast/stages/02-networking/vpn-spoke-prod.tf | 121 ++++++ 25 files changed, 2069 insertions(+) create mode 100644 fast/stages/02-networking/README.md create mode 100644 fast/stages/02-networking/data/cidrs.yaml create mode 100644 fast/stages/02-networking/data/dashboards/firewall_insights.json create mode 100644 fast/stages/02-networking/data/dashboards/vpn.json create mode 100644 fast/stages/02-networking/data/firewall-rules/landing/rules.yaml create mode 100644 fast/stages/02-networking/data/hierarchical-policy-rules.yaml create mode 100644 fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml create mode 100644 fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml create mode 100644 fast/stages/02-networking/diagram.png create mode 100644 fast/stages/02-networking/dns-dev.tf create mode 100644 fast/stages/02-networking/dns-landing.tf create mode 100644 fast/stages/02-networking/dns-prod.tf create mode 100644 fast/stages/02-networking/main.tf create mode 100644 fast/stages/02-networking/monitoring.tf create mode 100644 fast/stages/02-networking/outputs.tf create mode 100644 fast/stages/02-networking/test-resources.tf create mode 100644 fast/stages/02-networking/variables.tf create mode 100644 fast/stages/02-networking/vpc-landing.tf create mode 100644 fast/stages/02-networking/vpc-spoke-dev.tf create mode 100644 fast/stages/02-networking/vpc-spoke-prod.tf create mode 100644 fast/stages/02-networking/vpn-onprem.tf create mode 100644 fast/stages/02-networking/vpn-spoke-dev.tf create mode 100644 fast/stages/02-networking/vpn-spoke-prod.tf diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md new file mode 100644 index 00000000..4df304a8 --- /dev/null +++ b/fast/stages/02-networking/README.md @@ -0,0 +1,353 @@ +# Networking + +This stage sets up the shared network infrastructure for the whole org. It adopts the common “hub and spoke” reference design, which is well suited to multiple scenarios, and offers several advantages versus other designs: + +- the “hub” VPC centralizes external connectivity to on-prem or other cloud environments, and is ready to host cross-environment services like CI/CD, code repositories, and monitoring probes +- the “spoke” VPCs allow partitioning workloads (e.g. by environment like in this setup), while still retaining controlled access to central connectivity and services +- Shared VPC in both hub and spokes splits management of network resources in specific (host) projects, while still allowing them to be consumed from workload (service) projects +- the design also lends itself to easy DNS centralization, both from on-prem to cloud and from cloud to on-prem + +Connectivity between hub and spokes is established here via [VPN HA](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) tunnels, which offer easy interoperability with some key GCP features (GKE, services leveraging Service Networking like Cloud SQL, etc.), allowing clear partitioning of quota and limits between environments, and fine-grained control of routing. Different ways of implementing connectivity, and their respective pros and cons, are discussed below. + +The following diagram illustrates the high-level design, and should be used as a reference for the following sections. The final number of subnets, and their IP addressing design will of course depend on customer-specific requirements, and can be easily changed via variables or external data files without having to edit the actual code. + +![Networking diagram](diagram.png) + +## Design overview and choices + +### VPC design + +The hub/landing VPC hosts external connectivity and shared services for spoke VPCs, which are connected to it via VPN HA tunnels. Spokes are used here to partition environments, which is a fairly common pattern: + +- one spoke VPC for the production environment +- one spoke VPC for the development environment + +Each VPC is created into its own project, and each project is configured as a Shared VPC host, so that network-related resources and access configurations via IAM are kept separate for each VPC. + +The design easily lends itself to implementing additional environments, or adopting a different logical mapping for spokes (e.g. one spoke for each company entity, etc.). Adding spokes is a trivial operation, does not increase the design complexity, and is explained in operational terms in the following sections. + +In multi-organization scenarios, where production and non-production resources use different Cloud Identity and GCP organizations, the hub/landing VPC is usually part of the production organization, and establishes connections with production spokes in its same organization, and non-production spokes in a different organization. + +An additional VPC is also deployed by default with the provided code, disconnected from the other VPCs and hosting a single VM that emulates on-prem for testing purposes, via a Docker network and containers for VPN, DNS, HTTP, etc. + +### External connectivity + +External connectivity to on-prem is implemented here via VPN HA (two tunnels per region), as this is the minimum common denominator often used directly, or as a stop-gap solution to validate routing and transfer data, while waiting for [interconnects](https://cloud.google.com/network-connectivity/docs/interconnect) to be provisioned. + +Connectivity to additional on-prem sites or other cloud providers should be implemented in a similar fashion, via VPN tunnels or interconnects in the landing VPC sharing the same regional router. + +### Internal connectivity + +As mentioned initially, there are of course other ways to implement internal connectivity other than VPN HA. These can be easily retrofitted with minimal code changes, but introduce additional considerations for service interoperability, quotas and management. + +This is a summary of the main options: + +- [VPN HA](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) (implemented here) + - Pros: simple compatibility with GCP services that leverage peering internally, better control on routes, avoids peering groups shared quotas and limits + - Cons: additional cost, marginal increase in latency, requires multiple tunnels for full bandwidth +- [VPC Peering](https://cloud.google.com/vpc/docs/vpc-peering) + - Pros: no additional costs, full bandwidth with no configurations, no extra latency + - Cons: no transitivity (e.g. to GKE masters, Cloud SQL, etc.), no selective exchange of routes, several quotas and limits shared between VPCs in a peering group +- [Multi-NIC appliances](https://cloud.google.com/architecture/best-practices-vpc-design#multi-nic) + - Pros: additional security features (e.g. IPS), potentially better integration with on-prem systems by using the same vendor + - Cons: complex HA/failover setup, limited by VM bandwidth and scale, additional costs for VMs and licenses, out of band management of a critical cloud component + +### IP ranges, subnetting, routing + +Minimizing the number of routes (and subnets) in use on the cloud environment is an important consideration, as it simplifies management and avoids hitting [Cloud Router](https://cloud.google.com/network-connectivity/docs/router/quotas) and [VPC](https://cloud.google.com/vpc/docs/quota) quotas and limits. For this reason, we recommend careful planning of the IP space used in your cloud environment, to be able to use large IP CIDR blocks in routes whenever possible. + +This stage uses a dedicated /16 block (which should of course be sized to your needs) for each region in each VPC, and subnets created in each VPC derive their ranges from the relevant block. + +Spoke VPCs also define and reserve two "special" CIDR ranges dedicated to [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access) and [Internal HTTPs Load Balancers (L7ILB)](https://cloud.google.com/load-balancing/docs/l7-internal). + +Routes in GCP are either automatically created for VPC subnets, manually created via static routes, or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) via BGP sessions, which can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements. + +In this setup, the Cloud Routers are configured so as to exclude the default advertisement of VPC ranges, and they only advertise their respective aggregate ranges via custom advertisements. This greatly simplifies the routing configuration, and more importantly it allows to avoid quota or limit issues by keeping the number of routes small, instead of making it proportional to the subnets and secondary ranges in the VPCs. + +The high-level routing plan implemented in this architecture is as follows: + +| source | target | advertisement | +| ----------- | ----------- | ------------------------------ | +| VPC landing | onprem | GCP aggregate | +| VPC landing | onprem | Cloud DNS forwarders | +| VPC landing | onprem | Google private/restricted APIs | +| VPC landing | spokes | RFC1918 | +| VPC spoke | VPC landing | spoke aggregate | +| onprem | VC landing | onprem aggregates | + +As is evident from the table above, the hub/landing VPC acts as the route concentrator for the whole GCP network, implementing a full line of sight between environments, and between GCP and on-prem. While advertisements can be adjusted to selectively exchange routes (e.g. to isolate the production and the development environment), we recommend using [Firewall](#firewall) policies or rules to achieve the desired isolation. + +### Internet egress + +The path of least resistance for Internet egress is using Cloud NAT, and that is what's implemented in this setup, with a NAT gateway configured for each VPC. + +Several other scenarios are possible of course, with varying degrees of complexity: + +- a forward proxy, with optional URL filters +- a default route to on-prem to leverage existing egress infrastructure +- a full-fledged perimeter firewall to control egress and implement additional security features like IPS + +Future pluggable modules will allow to easily experiment, or deploy the above scenarios. + +### Firewall + +The GCP Firewall is a stateful, distributed feature that allows the creation of L4 policies, either via VPC-level rules or more recently via hierarchical policies applied on the resource hierarchy (organization, folders). + +The current setup adopts both firewall types, and uses hierarchical rules on the Networking folder for common ingress rules (egress is open by default), e.g. from health check or IAP forwarders ranges, and VPC rules for the environment or workload-level ingress. + +Rules and policies are defined in simple YAML files, described below. + +### DNS + +DNS often goes hand in hand with networking, especially on GCP where Cloud DNS zones and policies are associated at the VPC level. This setup implements both DNS flows: + +- on-prem to cloud via private zones for cloud-managed domains, and an [inbound policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) used as forwarding target or via delegation (requires some extra configuration) from on-prem DNS resolvers +- cloud to on-prem via forwarding zones for the on-prem managed domains + +DNS configuration is further centralized by leveraging peering zones, so that + +- the hub/landing Cloud DNS hosts configurations for on-prem forwarding and Google API domains, with the spokes consuming them via DNS peering zones +- the spokes Cloud DNS host configurations for the environment-specific domains, with the hub/landing VPC acting as consumer via DNS peering + +To complete the configuration, the 35.199.192.0/19 range should be routed on the VPN tunnels from on-prem, and the following names configured for DNS forwarding to cloud: + +- `private.googleapis.com` +- `restricted.googleapis.com` +- `gcp.example.com` (used as a placeholder) + +From cloud, the `example.com` domain (used as a placeholder) is forwarded to on-prem. + +This configuration is battle-tested, and flexible enough to lend itself to simple modifications without subverting its design, for example by forwarding and peering root zones to bypass Cloud DNS external resolution. + +## How to run this stage + +This stage is meant to be executed after the [resman](../01-resman) stage has run, as it leverages the automation service account and bucket created there, and additional resources configured in the [bootstrap](../00-boostrap) stage. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the previous stages for the environmental requirements. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during the [resource management](./01-resman) stage, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: + +```bash +cd ../00-bootstrap +terraform output -json providers | jq -r '.["02-networking"]' \ + > ../02-networking/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variables with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +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 + +VPCs are defined in separate files, one for `landing` and one for each of `prod` and `dev`. +Each file contains the same resources, described in the following paragraphs. + +The **project** ([`project`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)". + +The **VPC** ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc#subnet-factory). +Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are in variable `psa_ranges` - such variables are consumed by spoke VPCs. + +**Cloud NAT** ([`net-cloudnat`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress. + +### VPNs + +#### External + +Connectivity to on-prem is implemented with VPN HA ([`net-vpn`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpn)) and defined in [`vpn-onprem.tf`](./vpn-onprem.tf). The file provisionally implements a single logical connection between onprem and landing at `europe-west1`, and the relevant parameters for its configuration are found in variable `vpn_onprem_configs`. + +#### Internal + +VPNs ([`net-vpn`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpn)) used to interconnect landing and spokes are managed by `vpn-spoke-*.tf` files, each implementing both sides of the VPN connection. Per-gateway configurations (e.g. BGP advertisements and session ranges) are controlled by variable `vpn_onprem_configs`. VPN gateways and IKE secrets are automatically generated and configured. + +### Routing and BGP + +Each VPC network ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages a separate routing table, which can define static routes (e.g. to private.googleapis.com) and receives dynamic routes from BGP sessions established with neighbor networks (e.g. landing receives routes from onprem and spokes, and spokes receive RFC1918 from landing). + +Static routes are defined in `vpc-*.tf` files, in the `routes` section of each `net-vpc` module. + +BGP sessions for landing-spoke are configured through variable `vpn_spoke_configs`, while the ones for landing-onprem use variable `vpn_onprem_configs` + +### Firewall + +**VPC firewall rules** ([`net-vpc-firewall`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall)) are defined per-vpc on each `vpc-*.tf` file and leverage a resource factory to massively create rules. +To add a new firewall rule, create a new file or edit an existing one in the `data_folder` directory defined in the module `net-vpc-firewall`, following the examples of the "[Rules factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall#rules-factory)" section of the module documentation. + +**Hierarchical firewall policies** ([`folder`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/folder)) are defined in `main.tf`, and managed through a policy factory implemented by the `folder` module, which applies the defined hierarchical to the `Networking` folder, which contains all the core networking infrastructure. Policies are defined in the `rules_file` file - to define a new one simply use the instructions found on "[Firewall policy factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/organization#firewall-policy-factory)". + + +### DNS + +The DNS ([`dns`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/dns)) infrastructure is defined in [`dns.tf`](dns.tf). + +Cloud DNS manages onprem forwarding, the main GCP zone (in this example `gcp.example.com`) and is peered to environment-specific zones (i.e. `dev.gcp.example.com` and `prod.gcp.example.com`). + +#### Cloud environment + +Per the section above Landing acts as the source of truth for DNS within the Cloud environment. Resources defined in the spoke VPCs consume the Landing DNS infrastructure through DNS peering (e.g. `prod-landing-root-dns-peering`). +Spokes can optionally define private zones (e.g. `prod-dns-private-zone`) - granting visibility to the Landing VPC ensures that the whole cloud environment can query such zones. + +#### Cloud to on-prem + +Leveraging the forwarding zones defined on Landing (e.g. `onprem-example-dns-forwarding` and `reverse-10-dns-forwarding`), the cloud environment can resolve `in-addr.arpa.` and `onprem.example.com.` using the on-premises DNS infrastructure. Onprem resolvers IPs are set in variable `dns.onprem`. + +DNS queries sent to the on-premises infrastructure come from the `35.199.192.0/19` source range, which is only accessible from within a VPC or networks connected to one. + +#### On-prem to cloud + +The [Inbound DNS Policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) defined in module `landing-vpc` ([`landing.tf`](./landing.tf)) automatically reserves the first available IP address on each created subnet (typically the third one in a CIDR) to expose the Cloud DNS service so that it can be consumed from outside of GCP. + +### Private Google Access +[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment. + +For PGA to work: + +* Private Google Access should be enabled on the subnet. \ +Subnets created by the `net-vpc` module are PGA-enabled by default. + +* 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \ +Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed. + +* A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain, as implemented in module `googleapis-private-zone` in [`dns.tf`](./dns.tf) + +### Preliminar activities + +Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` to your needs, to update all reference to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. + +If you're not using FAST, you'll also need to create a `providers.tf` file to configure the GCS backend and the service account to use to run the deployment. + +You're now ready to run `terraform init` and `apply`. + +### Post-deployment activities + +* On-prem routers should be configured to advertise all relevant CIDRs to the GCP environments. To avoid hitting GCP quotas, we recomment aggregating routes as much as possible. +* On-prem routers should accept BGP sessions from their cloud peers. +* On-prem DNS servers should have forward zones for GCP-managed ones. + +## Customizations + +### Adding an environment +To create a new environment (e.g. `staging`), a few changes are required. + +Create a `vpc-spoke-staging.tf` file by copying `vpc-spoke-prod.tf` file, +and adapt the new file by replacing the value "prod" with the value "staging". +Running `diff vpc-spoke-dev.tf vpc-spoke-prod.tf` can help to see how environment files differ. + +The new VPC requires a set of dedicated CIDRs, one per region, added to variable `custom_adv` (for example as `spoke_staging_ew1` and `spoke_staging_ew4`). +>`custom_adv` is a map that "resolves" CIDR names to actual addresses, and will be used later to configure routing. +> +Variables managing L7 Interal Load Balancers (`l7ilb_subnets`) and Private Service Access (`psa_ranges`) should also be adapted, and subnets and firewall rules for the new spoke should be added as described above. + +VPN HA connectivity (see also [VPNs](#vpns)) to `landing` is managed by the `vpn-spoke-*.tf` files. +Copy `vpn-spoke-prod.tf` to `vpn-spoke-staging.tf` - replace "prod" with "staging" where relevant. + +VPN configuration also controls BGP advertisements, which requires the following variable changes: +* `router_configs` to configure the new routers (one per region) created for the `staging` VPC +* `vpn_onprem_configs` to configure the new advertisments to on-premises for the new CIDRs +* `vpn_spoke_configs` to configure the new advertisements to `landing` for the new VPC - new keys (one per region) should be added, such as e.g. `staging-ew1` and `staging-ew4` + +DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `staging.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy all the `prod-*` modules in the `dns.tf` file to `staging-*`, and update their content accordingly. Don't forget to add a peering zone from Landing to the newly created environment private zone. + + + + + +## Files + +| name | description | modules | resources | +| ---------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | +| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | +| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | +| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | +| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | +| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | + +## Variables + +| name | description | type | required | default | producer | +| ------------------ | -------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------: | +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | +| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | +| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | +| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | +| gke | | map(object({…})) | | {} | 01-resman | +| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | +| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | +| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | +| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | +| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | + +## Outputs + +| name | description | sensitive | consumers | +| ------------------------ | ----------------------------------------------- | :-------: | --------- | +| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | +| project_ids | Network project ids. | | | +| project_numbers | Network project numbers. | | | +| shared_vpc_host_projects | Shared VPC host projects. | | | +| shared_vpc_self_links | Shared VPC host projects. | | | +| tfvars | Network-related variables used in other stages. | ✓ | | +| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | + + + + + + + + + + + + + + + + + + + + diff --git a/fast/stages/02-networking/data/cidrs.yaml b/fast/stages/02-networking/data/cidrs.yaml new file mode 100644 index 00000000..5f453d8d --- /dev/null +++ b/fast/stages/02-networking/data/cidrs.yaml @@ -0,0 +1,15 @@ +# skip boilerplate check + +healthchecks: + - 35.191.0.0/16 + - 130.211.0.0/22 + - 209.85.152.0/22 + - 209.85.204.0/22 + +rfc1918: + - 10.0.0.0/8 + - 172.16.0.0/16 + - 192.168.0.0/16 + +onprem_probes: + - 10.255.255.254/32 diff --git a/fast/stages/02-networking/data/dashboards/firewall_insights.json b/fast/stages/02-networking/data/dashboards/firewall_insights.json new file mode 100644 index 00000000..e829091c --- /dev/null +++ b/fast/stages/02-networking/data/dashboards/firewall_insights.json @@ -0,0 +1,68 @@ +{ + "displayName": "Firewall Insights Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Subnet Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/subnet/firewall_hit_count\" resource.type=\"gce_subnetwork\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "VM Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/vm/firewall_hit_count\" resource.type=\"gce_instance\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} \ No newline at end of file diff --git a/fast/stages/02-networking/data/dashboards/vpn.json b/fast/stages/02-networking/data/dashboards/vpn.json new file mode 100644 index 00000000..4396cc00 --- /dev/null +++ b/fast/stages/02-networking/data/dashboards/vpn.json @@ -0,0 +1,248 @@ +{ + "displayName": "VPN Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} \ No newline at end of file diff --git a/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml b/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml new file mode 100644 index 00000000..e72b7c9c --- /dev/null +++ b/fast/stages/02-networking/data/firewall-rules/landing/rules.yaml @@ -0,0 +1,15 @@ +# skip boilerplate check + +allow-onprem-probes-example: + description: "Allow traffic from onprem probes" + direction: INGRESS + action: allow + sources: [] + ranges: + - $onprem_probes + targets: [] + use_service_accounts: false + rules: + - protocol: tcp + ports: + - 12345 diff --git a/fast/stages/02-networking/data/hierarchical-policy-rules.yaml b/fast/stages/02-networking/data/hierarchical-policy-rules.yaml new file mode 100644 index 00000000..0172a309 --- /dev/null +++ b/fast/stages/02-networking/data/hierarchical-policy-rules.yaml @@ -0,0 +1,49 @@ +# skip boilerplate check + +allow-admins: + description: Access from the admin subnet to all subnets + direction: INGRESS + action: allow + priority: 1000 + ranges: + - $rfc1918 + ports: + all: [] + target_resources: null + enable_logging: false + +allow-healthchecks: + description: Enable HTTP and HTTPS healthchecks + direction: INGRESS + action: allow + priority: 1001 + ranges: + - $healthchecks + ports: + tcp: ["80", "443"] + target_resources: null + enable_logging: false + +allow-ssh-from-iap: + description: Enable SSH from IAP + direction: INGRESS + action: allow + priority: 1002 + ranges: + - 35.235.240.0/20 + ports: + tcp: ["22"] + target_resources: null + enable_logging: false + +allow-icmp: + description: Enable ICMP + direction: INGRESS + action: allow + priority: 1003 + ranges: + - 0.0.0.0/0 + ports: + icmp: [] + target_resources: null + enable_logging: false diff --git a/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml new file mode 100644 index 00000000..37c28f03 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.144.0.0/24 +description: Default subnet for dev diff --git a/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml b/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml new file mode 100644 index 00000000..5af68db6 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/landing/landing-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.0.0/24 +description: Default subnet for landing diff --git a/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml new file mode 100644 index 00000000..7a77f309 --- /dev/null +++ b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.136.0.0/24 +description: Default subnet for prod diff --git a/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml new file mode 100644 index 00000000..2512947e --- /dev/null +++ b/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +region: europe-west1 +description: GKE subnet for prod +ip_cidr_range: 10.140.0.0/24 +secondary_ip_ranges: + - pods: 10.140.1.0/16 + - services: 10.140.2.0/24 diff --git a/fast/stages/02-networking/diagram.png b/fast/stages/02-networking/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c071ccf1bac3089172b7d67d195ec93fa161e241 GIT binary patch literal 141184 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

NGZ@;#C*C>7?XTUkt-9(V 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ྫ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 Bibenc literal 0 HcmV?d00001 diff --git a/fast/stages/02-networking/dns-dev.tf b/fast/stages/02-networking/dns-dev.tf new file mode 100644 index 00000000..5047f832 --- /dev/null +++ b/fast/stages/02-networking/dns-dev.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Development spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "dev-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "dev-gcp-example-com" + domain = "dev.gcp.example.com." + client_networks = [module.dev-spoke-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "dev-landing-root-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-root-dns-peering" + domain = "." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} + +module "dev-reverse-10-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} diff --git a/fast/stages/02-networking/dns-landing.tf b/fast/stages/02-networking/dns-landing.tf new file mode 100644 index 00000000..4c6bb774 --- /dev/null +++ b/fast/stages/02-networking/dns-landing.tf @@ -0,0 +1,93 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Landing DNS zones and peerings setup. + +# forwarding to on-prem DNS resolvers + +module "onprem-example-dns-forwarding" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "forwarding" + name = "example-com" + domain = "onprem.example.com." + client_networks = [module.landing-vpc.self_link] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "reverse-10-dns-forwarding" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "forwarding" + name = "root-reverse-10" + domain = "10.in-addr.arpa." + client_networks = [module.landing-vpc.self_link] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "gcp-example-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "gcp-example-com" + domain = "gcp.example.com." + client_networks = [module.landing-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# GCP-specific DNS zones peered to the environment spoke that holds the config + +module "prod-gcp-example-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "peering" + name = "prod-root-dns-peering" + domain = "prod.gcp.example.com." + client_networks = [module.landing-vpc.self_link] + peer_network = module.prod-spoke-vpc.self_link +} + +module "dev-gcp-example-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "peering" + name = "dev-root-dns-peering" + domain = "dev.gcp.example.com." + client_networks = [module.landing-vpc.self_link] + peer_network = module.dev-spoke-vpc.self_link +} + +# Google API zone to trigger Private Access + +module "googleapis-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "googleapis-com" + domain = "googleapis.com." + client_networks = [module.landing-vpc.self_link] + recordsets = { + "A private" = { type = "A", ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "A restricted" = { type = "A", ttl = 300, records = [ + "199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7" + ] } + "CNAME *" = { type = "CNAME", ttl = 300, records = ["private.googleapis.com."] } + } +} diff --git a/fast/stages/02-networking/dns-prod.tf b/fast/stages/02-networking/dns-prod.tf new file mode 100644 index 00000000..f9b62ca7 --- /dev/null +++ b/fast/stages/02-networking/dns-prod.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "prod-dns-private-zone" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.landing-project.project_id + type = "private" + name = "prod-gcp-example-com" + domain = "prod.gcp.example.com." + client_networks = [module.prod-spoke-vpc.self_link] + recordsets = { + "A localhost" = { type = "A", ttl = 300, records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "prod-landing-root-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-root-dns-peering" + domain = "." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} + +module "prod-reverse-10-dns-peering" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/dns?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-vpc.self_link +} diff --git a/fast/stages/02-networking/main.tf b/fast/stages/02-networking/main.tf new file mode 100644 index 00000000..41409d1d --- /dev/null +++ b/fast/stages/02-networking/main.tf @@ -0,0 +1,72 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Networking folder and hierarchical policy. + +locals { + # define the structures used for BGP peers in the VPN resources + bgp_peer_options = { + for k, v in var.vpn_spoke_configs : + k => var.vpn_spoke_configs[k].adv == null ? null : { + advertise_groups = [] + advertise_ip_ranges = { + for adv in(var.vpn_spoke_configs[k].adv == null ? [] : var.vpn_spoke_configs[k].adv.custom) : + var.custom_adv[adv] => adv + } + advertise_mode = try(var.vpn_spoke_configs[k].adv.default, false) ? "DEFAULT" : "CUSTOM" + route_priority = null + } + } + bgp_peer_options_onprem = { + for k, v in var.vpn_onprem_configs : + k => var.vpn_onprem_configs[k].adv == null ? null : { + advertise_groups = [] + advertise_ip_ranges = { + for adv in(var.vpn_onprem_configs[k].adv == null ? [] : var.vpn_onprem_configs[k].adv.custom) : + var.custom_adv[adv] => adv + } + advertise_mode = try(var.vpn_onprem_configs[k].adv.default, false) ? "DEFAULT" : "CUSTOM" + route_priority = null + } + } + l7ilb_subnets = { for env, v in var.l7ilb_subnets : env => [ + for s in v : merge(s, { + active = true + name = "${env}-l7ilb-${s.region}" + })] + } + region_trigram = { + europe-west1 = "ew1" + europe-west3 = "ew3" + } +} + +module "folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Networking" + folder_create = var.folder_id == null + id = var.folder_id + firewall_policy_factory = { + cidr_file = "${var.data_dir}/cidrs.yaml" + policy_name = null + rules_file = "${var.data_dir}/hierarchical-policy-rules.yaml" + } + firewall_policy_association = { + factory-policy = "factory" + } +} + diff --git a/fast/stages/02-networking/monitoring.tf b/fast/stages/02-networking/monitoring.tf new file mode 100644 index 00000000..7b8b70c5 --- /dev/null +++ b/fast/stages/02-networking/monitoring.tf @@ -0,0 +1,32 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Network monitoring dashboards. + +locals { + dashboard_path = "${var.data_dir}/dashboards" + dashboard_files = fileset(local.dashboard_path, "*.json") + dashboards = { + for filename in local.dashboard_files : + filename => "${local.dashboard_path}/${filename}" + } +} + +resource "google_monitoring_dashboard" "dashboard" { + for_each = local.dashboards + project = module.landing-project.project_id + dashboard_json = file(each.value) +} diff --git a/fast/stages/02-networking/outputs.tf b/fast/stages/02-networking/outputs.tf new file mode 100644 index 00000000..4efe9bc6 --- /dev/null +++ b/fast/stages/02-networking/outputs.tf @@ -0,0 +1,95 @@ +/** + * 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. + */ +# optionally generate providers and tfvars files for subsequent stages + +locals { + tfvars = { + "03-project-factory-dev" = jsonencode({ + environment_dns_zone = module.dev-dns-private-zone.domain + shared_vpc_self_link = module.dev-spoke-vpc.self_link + vpc_host_project = module.dev-spoke-project.project_id + }) + "03-project-factory-prod" = jsonencode({ + environment_dns_zone = module.prod-dns-private-zone.domain + shared_vpc_self_link = module.prod-spoke-vpc.self_link + vpc_host_project = module.prod-spoke-project.project_id + }) + } +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-networking.auto.tfvars.json" + content = each.value +} + +# outputs + +output "cloud_dns_inbound_policy" { + description = "IP Addresses for Cloud DNS inbound policy." + value = [for s in module.landing-vpc.subnets : cidrhost(s.ip_cidr_range, 2)] +} + +output "project_ids" { + description = "Network project ids." + value = { + dev = module.dev-spoke-project.project_id + landing = module.landing-project.project_id + prod = module.prod-spoke-project.project_id + } +} + +output "project_numbers" { + description = "Network project numbers." + value = { + dev = "projects/${module.dev-spoke-project.number}" + landing = "projects/${module.landing-project.number}" + prod = "projects/${module.prod-spoke-project.number}" + } +} + +output "shared_vpc_host_projects" { + description = "Shared VPC host projects." + value = { + landing = module.landing-project.project_id + dev = module.dev-spoke-project.project_id + prod = module.prod-spoke-project.project_id + } +} + + +output "shared_vpc_self_links" { + description = "Shared VPC host projects." + value = { + landing = module.landing-vpc.self_link + dev = module.dev-spoke-vpc.self_link + prod = module.prod-spoke-vpc.self_link + } +} + + +output "vpn_gateway_endpoints" { + description = "External IP Addresses for the GCP VPN gateways." + value = { + onprem-ew1 = { for v in module.landing-to-onprem-ew1-vpn.gateway.vpn_interfaces : v.id => v.ip_address } + } +} + +output "tfvars" { + description = "Network-related variables used in other stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/02-networking/test-resources.tf b/fast/stages/02-networking/test-resources.tf new file mode 100644 index 00000000..6f67e30d --- /dev/null +++ b/fast/stages/02-networking/test-resources.tf @@ -0,0 +1,100 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description temporary instances for testing + +module "test-vm-landing-0" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/compute-vm?ref=v12.0.0" + project_id = module.landing-project.project_id + zone = "europe-west1-b" + name = "test-vm-1" + network_interfaces = [{ + network = module.landing-vpc.self_link + subnetwork = module.landing-vpc.subnet_self_links["europe-west1/landing-default-ew1"] + alias_ips = {} + nat = false + addresses = null + }] + tags = ["ssh"] + service_account_create = true + boot_disk = { + image = "projects/debian-cloud/global/images/family/debian-10" + type = "pd-balanced" + size = 10 + } + metadata = { + startup-script = < { + address = cidrhost(v, 0) + network = module.dev-spoke-vpc.self_link + prefix_length = split("/", v)[1] + } + } +} diff --git a/fast/stages/02-networking/vpc-spoke-prod.tf b/fast/stages/02-networking/vpc-spoke-prod.tf new file mode 100644 index 00000000..b4bf6c5b --- /dev/null +++ b/fast/stages/02-networking/vpc-spoke-prod.tf @@ -0,0 +1,105 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke VPC and related resources. + +module "prod-spoke-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + billing_account = var.billing_account_id + name = "prod-net-spoke-0" + parent = var.folder_id + prefix = var.prefix + service_config = { + disable_on_destroy = false + disable_dependent_services = false + } + services = [ + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + ] + shared_vpc_host_config = { + enabled = true + service_projects = [] + } + metric_scopes = [module.landing-project.project_id] + iam = { + "roles/dns.admin" = [var.project_factory_sa.prod] + } +} + +module "prod-spoke-vpc" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpc?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + name = "prod-spoke-0" + mtu = 1500 + data_folder = "${var.data_dir}/subnets/prod" + subnets_l7ilb = local.l7ilb_subnets.prod + # set explicit routes for googleapis in case the default route is deleted + routes = { + private-googleapis = { + dest_range = "199.36.153.8/30" + priority = 1000 + tags = [] + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + restricted-googleapis = { + dest_range = "199.36.153.4/30" + priority = 1000 + tags = [] + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + } +} + +module "prod-spoke-firewall" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpc-firewall?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.name + admin_ranges = [] + http_source_ranges = [] + https_source_ranges = [] + ssh_source_ranges = [] + data_folder = "${var.data_dir}/firewall-rules/prod" + cidr_template_file = "${var.data_dir}/cidrs.yaml" +} + +module "prod-spoke-cloudnat" { + for_each = toset(values(module.prod-spoke-vpc.subnet_regions)) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-cloudnat?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + region = each.value + name = "prod-nat-${local.region_trigram[each.value]}" + router_create = true + router_network = module.prod-spoke-vpc.name + router_asn = 4200001024 + logging_filter = "ERRORS_ONLY" +} + +module "prod-spoke-psa-addresses" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-address?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + psa_addresses = { for r, v in var.psa_ranges.prod : r => { + address = cidrhost(v, 0) + network = module.prod-spoke-vpc.self_link + prefix_length = split("/", v)[1] + } + } +} diff --git a/fast/stages/02-networking/vpn-onprem.tf b/fast/stages/02-networking/vpn-onprem.tf new file mode 100644 index 00000000..87a8dd33 --- /dev/null +++ b/fast/stages/02-networking/vpn-onprem.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and onprem. + +module "landing-to-onprem-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-onprem-ew1" + router_create = true + router_name = "dev-spoke-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_external_gateway = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = [{ + id = 0 + # on-prem router ip address + ip_address = var.vpn_onprem_configs.landing-ew1.peer.address + }] + } + tunnels = { for t in range(2) : "remote-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_onprem_configs.landing-ew1.session_range, 1 + (t * 4)) + asn = var.vpn_onprem_configs.landing-ew1.peer.asn + } + bgp_peer_options = local.bgp_peer_options_onprem["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_onprem_configs.landing-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = 0 + router = null + shared_secret = var.vpn_onprem_configs.landing-ew1.peer.secret_id + vpn_gateway_interface = t + } + } +} diff --git a/fast/stages/02-networking/vpn-spoke-dev.tf b/fast/stages/02-networking/vpn-spoke-dev.tf new file mode 100644 index 00000000..78ce21b9 --- /dev/null +++ b/fast/stages/02-networking/vpn-spoke-dev.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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and development spoke. + +module "landing-to-dev-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-dev-ew1" + # The router used for this VPN is managed in vpn-prod.tf + router_create = false + router_name = "landing-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_gcp_gateway = module.dev-to-landing-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-dev-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } + depends_on = [ + module.landing-to-prod-ew1-vpn.router + ] +} + +module "dev-to-landing-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.dev-spoke-project.project_id + network = module.dev-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew1" + router_create = true + router_name = "dev-spoke-vpn-ew1" + router_asn = var.router_configs.spoke-dev-ew1.asn + peer_gcp_gateway = module.landing-to-dev-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["dev-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-dev-ew1-vpn.random_secret + vpn_gateway_interface = t + } + } +} diff --git a/fast/stages/02-networking/vpn-spoke-prod.tf b/fast/stages/02-networking/vpn-spoke-prod.tf new file mode 100644 index 00000000..8936339b --- /dev/null +++ b/fast/stages/02-networking/vpn-spoke-prod.tf @@ -0,0 +1,121 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description VPN between landing and production spoke. + +module "landing-to-prod-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-prod-ew1" + router_create = true + router_name = "landing-vpn-ew1" + router_asn = var.router_configs.landing-ew1.asn + peer_gcp_gateway = module.prod-to-landing-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-prod-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } +} + +module "prod-to-landing-ew1-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew1" + router_create = true + router_name = "prod-spoke-vpn-ew1" + router_asn = var.router_configs.spoke-prod-ew1.asn + peer_gcp_gateway = module.landing-to-prod-ew1-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew1.asn + } + bgp_peer_options = local.bgp_peer_options["prod-ew1"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew1-vpn.random_secret + vpn_gateway_interface = t + } + } +} + +module "landing-to-prod-ew4-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-prod-ew4" + router_create = true + router_name = "landing-vpn-ew4" + router_asn = var.router_configs.landing-ew4.asn + peer_gcp_gateway = module.prod-to-landing-ew4-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4)) + asn = var.router_configs.spoke-prod-ew4.asn + } + bgp_peer_options = local.bgp_peer_options["landing-ew4"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = t + } + } +} + +module "prod-to-landing-ew4-vpn" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/net-vpn-ha?ref=v12.0.0" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew4" + router_create = true + router_name = "prod-spoke-vpn-ew4" + router_asn = var.router_configs.spoke-prod-ew4.asn + peer_gcp_gateway = module.landing-to-prod-ew4-vpn.self_link + tunnels = { for t in range(2) : "tunnel-${t}" => { + bgp_peer = { + address = cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4)) + asn = var.router_configs.landing-ew4.asn + } + bgp_peer_options = local.bgp_peer_options["prod-ew4"] + bgp_session_range = "${cidrhost(var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4))}/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew4-vpn.random_secret + vpn_gateway_interface = t + } + } +} From f4d23b8929b3d69bf573e2690ee39751587df374 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:26:46 +0100 Subject: [PATCH 026/132] add bolierplate to validate_schema Co-authored-by: Julio Castillo --- tools/validate_schema.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/validate_schema.py b/tools/validate_schema.py index 77ca29ec..f003723d 100755 --- a/tools/validate_schema.py +++ b/tools/validate_schema.py @@ -1,3 +1,19 @@ +#!/usr/bin/env python3 + +# 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. + import glob import os From 5a65ba07d31c8de6a5bf0334d4c9805dd295b8f9 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Mon, 17 Jan 2022 10:25:56 +0100 Subject: [PATCH 027/132] Import Fast from dev repository. > > Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- fast/stages/03-project-factory/README.md | 6 + fast/stages/03-project-factory/prod/README.md | 130 ++++++++++++++++++ .../prod/data/defaults.yaml.sample | 22 +++ .../prod/data/projects/project.yaml.sample | 99 +++++++++++++ .../03-project-factory/prod/diagram.png | Bin 0 -> 57470 bytes fast/stages/03-project-factory/prod/main.tf | 57 ++++++++ .../stages/03-project-factory/prod/outputs.tf | 20 +++ .../03-project-factory/prod/variables.tf | 54 ++++++++ 8 files changed, 388 insertions(+) create mode 100644 fast/stages/03-project-factory/README.md create mode 100644 fast/stages/03-project-factory/prod/README.md create mode 100644 fast/stages/03-project-factory/prod/data/defaults.yaml.sample create mode 100644 fast/stages/03-project-factory/prod/data/projects/project.yaml.sample create mode 100644 fast/stages/03-project-factory/prod/diagram.png create mode 100644 fast/stages/03-project-factory/prod/main.tf create mode 100644 fast/stages/03-project-factory/prod/outputs.tf create mode 100644 fast/stages/03-project-factory/prod/variables.tf diff --git a/fast/stages/03-project-factory/README.md b/fast/stages/03-project-factory/README.md new file mode 100644 index 00000000..2beb5158 --- /dev/null +++ b/fast/stages/03-project-factory/README.md @@ -0,0 +1,6 @@ +# Project factory + +The Project Factory (or PF) builds on top of your foundations to create and setup projects (and related resources) to be used for your workloads. +It is organized in folders representing enviroments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). + +This directory contains a single project factory ([`prod/`](./prod/)) as an example - in order to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md new file mode 100644 index 00000000..3cc9ac7e --- /dev/null +++ b/fast/stages/03-project-factory/prod/README.md @@ -0,0 +1,130 @@ +# Project factory + +The Project Factory (or PF) builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. +It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). + +## Design overview and choices + +![Diagram](diagram.png) + +A single factory creates projects in a well-defined context, according to your resource management structure. In the diagram above, each Team is structured to have specific folders projects for a given environment, such as Production and Development, per the resource management structure configured in stage `01-resman`. + +Projects for each environment across different teams are created by dedicated service accounts, as exemplified in the diagram above. While there's no intrinsic limitation regarding where the project factory can create a given project, the IAM bindings for the service account effectively enforce boundaries (e.g. the production service account shouldn't be able to create or have any access to the development projects, and vice versa). + +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)) + + +## 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 will be 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 to be 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 provider file, and populate the required variables from the previous stage. + +```bash +# Variable `outputs_location` is set to `../../configs/example` in stage 01-resman +$ cd fabric-fast/stages/03-project-factory/prod +ln -s ../../../configs/example/03-project-factory-prod/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variables with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +If you're not running on top of 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: + + +* `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 generate project `fast-prod-lab0`. + +Once the configuration is complete, the project factory can be run with the usual + +```bash +terraform init +terraform apply +``` + + + + + + +## Files + +| name | description | modules | resources | +| ------------------------------ | ----------------- | ---------------------------- | --------- | +| [main.tf](./main.tf) | Project factory. | project-factory | | +| [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +| -------------------- | --------------------------------------------------------------------- | :-----------------: | :------: | :-------------------------------------------: | :------------------------: | +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | +| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | +| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | +| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | +| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | + +## Outputs + +| name | description | sensitive | consumers | +| -------- | -------------------------------------- | :-------: | --------- | +| projects | Created projects and service accounts. | | | + + + + + diff --git a/fast/stages/03-project-factory/prod/data/defaults.yaml.sample b/fast/stages/03-project-factory/prod/data/defaults.yaml.sample new file mode 100644 index 00000000..577c4bba --- /dev/null +++ b/fast/stages/03-project-factory/prod/data/defaults.yaml.sample @@ -0,0 +1,22 @@ +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Setup for billing alerts +billing_alert: + amount: 1000 + thresholds: + current: [0.5, 0.8] + forecasted: [0.5, 0.8] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: ["team-contacts@example.com"] + +# [opt] Labels set for all projects +labels: + environment: prod + department: accounting + application: example-app + foo: bar + +# [opt] Additional notification channels for billing +notification_channels: [] \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample b/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample new file mode 100644 index 00000000..7994e7f4 --- /dev/null +++ b/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample @@ -0,0 +1,99 @@ +# [opt] Billing account id - overrides default if set +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Billing alerts config - overrides default if set +billing_alert: + amount: 10 + thresholds: + current: + - 0.5 + - 0.8 + forecasted: [] + +# [opt] DNS zones to be created as children of the environment_dns_zone defined in defaults +dns_zones: + - lorem + - ipsum + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: + - team-a-contacts@example.com + +# Folder the project will be created as children of +folder_id: folders/012345678901 + +# [opt] Authoritative IAM bindings in group => [roles] format +group_iam: + test-team-foobar@fast-lab-0.gcp-pso-italy.net: + - roles/compute.admin + +# [opt] Authoritative IAM bindings in role => [principals] format +# Generally used to grant roles to service accounts external to the project +iam: + roles/compute.admin: + - serviceAccount:service-account + +# [opt] Service robots and keys they will be assigned as cryptoKeyEncrypterDecrypter +# in service => [keys] format +kms_service_agents: + compute: [key1, key2] + storage: [key1, key2] + +# [opt] Labels for the project - merged with the ones defined in defaults +labels: + environment: prod + +# [opt] Org policy overrides defined at project level +org_policies: + policy_boolean: + constraints/compute.disableGuestAttributesAccess: true + policy_list: + constraints/compute.trustedImageProjects: + inherit_from_parent: null + status: true + suggested_value: null + values: + - projects/fast-prod-iac-core-0 + +# [opt] Service account to create for the project and their roles on the project +# in name => [roles] format +service_accounts: + another-service-account: + - roles/compute.admin + my-service-account: + - roles/compute.admin + +# [opt] APIs to enable on the project. +services: + - storage.googleapis.com + - stackdriver.googleapis.com + - compute.googleapis.com + +# [opt] Roles to assign to the robots service accounts in robot => [roles] format +services_iam: + compute: + - roles/storage.objectViewer + + # [opt] VPC setup. + # If set enables the `compute.googleapis.com` service and configures + # service project attachment +vpc: + + # [opt] If set, enables the container API + gke_setup: + + # Grants "roles/container.hostServiceAgentUser" to the container robot if set + enable_host_service_agent: false + + # Grants "roles/compute.securityAdmin" to the container robot if set + enable_security_admin: true + + # Host project the project will be service project of + host_project: fast-prod-net-spoke-0 + + # [opt] Subnets in the host project where principals will be granted networkUser + # in region/subnet-name => [principals] + subnets_iam: + europe-west1/prod-default-ew1: + - user:foobar@example.com + - serviceAccount:service-account1 \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/diagram.png b/fast/stages/03-project-factory/prod/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..b942ea47d934695831b1838d41766db699e9b64b GIT binary patch literal 57470 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Ν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 literal 0 HcmV?d00001 diff --git a/fast/stages/03-project-factory/prod/main.tf b/fast/stages/03-project-factory/prod/main.tf new file mode 100644 index 00000000..e1c05e51 --- /dev/null +++ b/fast/stages/03-project-factory/prod/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Project factory. + + +locals { + _defaults = yamldecode(file(var.defaults_file)) + _defaults_net = { + billing_account_id = var.billing_account_id + environment_dns_zone = var.environment_dns_zone + shared_vpc_self_link = var.shared_vpc_self_link + vpc_host_project = var.vpc_host_project + } + defaults = merge(local._defaults, local._defaults_net) + projects = { + for f in fileset("${var.data_dir}", "**/*.yaml") : + trimsuffix(f, ".yaml") => yamldecode(file("${var.data_dir}/${f}")) + } +} + +module "projects" { + #TODO(sruffilli): Pin to release + source = "github.com/terraform-google-modules/cloud-foundation-fabric/examples/factories/project-factory" + for_each = local.projects + defaults = local.defaults + project_id = each.key + billing_account_id = try(each.value.billing_account_id, null) + billing_alert = try(each.value.billing_alert, null) + dns_zones = try(each.value.dns_zones, []) + essential_contacts = try(each.value.essential_contacts, []) + folder_id = each.value.folder_id + group_iam = try(each.value.group_iam, {}) + iam = try(each.value.iam, {}) + kms_service_agents = try(each.value.kms, {}) + labels = try(each.value.labels, {}) + org_policies = try(each.value.org_policies, null) + service_accounts = try(each.value.service_accounts, {}) + services = try(each.value.services, []) + services_iam = try(each.value.services_iam, {}) + vpc = try(each.value.vpc, null) +} + + diff --git a/fast/stages/03-project-factory/prod/outputs.tf b/fast/stages/03-project-factory/prod/outputs.tf new file mode 100644 index 00000000..59ecff95 --- /dev/null +++ b/fast/stages/03-project-factory/prod/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "projects" { + description = "Created projects and service accounts." + value = module.projects +} diff --git a/fast/stages/03-project-factory/prod/variables.tf b/fast/stages/03-project-factory/prod/variables.tf new file mode 100644 index 00000000..8bb9f035 --- /dev/null +++ b/fast/stages/03-project-factory/prod/variables.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#TODO: tfdoc annotations + +variable "billing_account_id" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id." + type = string +} + +variable "data_dir" { + description = "Relative path for the folder storing configuration data." + type = string + default = "data/projects" +} + +variable "environment_dns_zone" { + # tfdoc:variable:source 02-networking + description = "DNS zone suffix for environment." + type = string + default = null +} + +variable "defaults_file" { + description = "Relative path for the file storing the project factory configuration." + type = string + default = "data/defaults.yaml" +} + +variable "shared_vpc_self_link" { + # tfdoc:variable:source 02-networking + description = "Self link for the shared VPC." + type = string +} + +variable "vpc_host_project" { + # tfdoc:variable:source 02-networking + description = "Host project for the shared VPC." + type = string +} From ade946992c851bc18d59d447577a5b1fb3cfdd77 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:30:26 +0100 Subject: [PATCH 028/132] stage 02-security --- fast/stages/02-security/README.md | 319 ++++++++++++++++++ fast/stages/02-security/core-dev.tf | 64 ++++ fast/stages/02-security/core-prod.tf | 64 ++++ fast/stages/02-security/diagram.png | Bin 0 -> 93474 bytes fast/stages/02-security/main.tf | 47 +++ fast/stages/02-security/outputs.tf | 43 +++ fast/stages/02-security/variables.tf | 185 ++++++++++ .../vpc-sc-restricted-services.yaml | 88 +++++ fast/stages/02-security/vpc-sc.tf | 167 +++++++++ 9 files changed, 977 insertions(+) create mode 100644 fast/stages/02-security/README.md create mode 100644 fast/stages/02-security/core-dev.tf create mode 100644 fast/stages/02-security/core-prod.tf create mode 100644 fast/stages/02-security/diagram.png create mode 100644 fast/stages/02-security/main.tf create mode 100644 fast/stages/02-security/outputs.tf create mode 100644 fast/stages/02-security/variables.tf create mode 100644 fast/stages/02-security/vpc-sc-restricted-services.yaml create mode 100644 fast/stages/02-security/vpc-sc.tf diff --git a/fast/stages/02-security/README.md b/fast/stages/02-security/README.md new file mode 100644 index 00000000..79e99a42 --- /dev/null +++ b/fast/stages/02-security/README.md @@ -0,0 +1,319 @@ +# Shared security resources + +This stage sets up security resources and configurations which impact the whole org, or are shared across the hierarchy to other projects and teams. + +Its design is fairly general, and provides a reference example for [Cloud KMS](https://cloud.google.com/security-key-management) and a [VPC Service Controls](https://cloud.google.com/vpc-service-controls) configuration that sets up three perimeters (landing, development, production), the related bridge perimeters, and provides variables to configure their resources, access levels, and directional policies. + +Expanding this stage to include other security-related services like Secret Manager, is fairly simple by using the provided implementation for Cloud KMS, and leveraging the broad permissions on the top-level Security folder of the automation service account used. + +The following diagram illustrates the high level design of created resources, and a schema of the VPC SC design, which can be adapted to specific requirements via variables: + +![Security diagram](diagram.png) + +## Design overview and choices + +Project-level security resources are grouped into two separate projects, one per environment. This matches requirements we frequently observe in real life, and provides enough separation without needlessly complicating operations. + +Cloud KMS is configured and designed mainly to encrypt GCP resources with a [Customer-managed encryption key](https://cloud.google.com/kms/docs/cmek) but it may be used to create cryptokeys used to [encrypt application data](https://cloud.google.com/kms/docs/encrypting-application-data) too. + +IAM for management-related operations is already assigned at the folder level to the security team by the previous stage, but more granularity can of course be added here at the project level, to grant control of separate services across environments to different actors. + +### Cloud KMS + +A reference Cloud KMS implementation is part of this stage, to provide a simple way of managing centralized keys, that are then shared and consumed widely across the org to enable customer-managed encryption. The implementation is also easy to clone and modify to support other services like Secret Manager. + +The Cloud KMS configuration allows defining keys by name (typically matching the downstream service that uses them) in different locations, either based on a common default or a per-key setting, and then takes care internally of provisioning the relevant keyrings and creating keys in the appropriate location. + +IAM roles on keys can of course be configured at the logical level for all locations where a logical key is created, and their management can also be delegated via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) exposed through a simple variable, to allow other identities to set IAM policies on keys. This is particularly useful in setups like project factories, making it possible to configure IAM bindings during project creation for team groups or service agent accounts (compute, storage, etc.). + +### VPC Service Controls + +This stage also provisions the VPC Service Controls configuration on demand for the whole organization, implementing the straightforward design illustrated above: + +- one perimeter for each environment +- one perimeter for centralized services and the landing VPC +- bridge perimeters to connect the landing perimeter to each environment + +The VPC SC configuration is set to dry-run mode, but switching to enforced mode is a simple operation involving the modification of a few lines of code highlighted by ad-hoc comments. Variables are designed to enable easy centralized management of VPC Service Controls, including access levels and [ingress/egress rules](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules) as described below. + +Some care needs to be taken with project membership in perimeters, which can only be implemented here instead of being delegated (all or partially) to different stages, until the [Google Provider feature request](https://github.com/hashicorp/terraform-provider-google/issues/7270) that allows using project-level association for both enforced and dry-run modes is implemented. + +## How to run this stage + +This stage is meant to be executed after the [resouce management](../01-resman) stage has run, as it leverages the folder and automation resources created there. The relevant user groups must also exist, but that's one of the requirements for the previous stages too, so if you ran those successfully, you're good to go. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap stage for the actual roles needed. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in resource management, you can derive the providers file from that stage's outputs: + +```bash +cd ../01-resman +terraform output -json providers | jq -r '.["02-security"]' \ + > ../02-security/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 avalaible: + +```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 +``` + +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. + +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. The sections below also describe some of the possible customizations. + +Once done, you can run this stage: + +```bash +terraform init +terraform apply +``` + +## Customizations + +### KMS keys + +Cloud KMS configuration is split in two variables: + +- `kms_defaults` configures the locations and rotation period, used for keys that don't specifically configure them +- `kms_keys` configures the actual keys to create, and also allows configuring their IAM bindings and labels, and overriding locations and rotation period. When configuring locations for a key, please take into account limitations each cloud product may have. + +The additional `kms_restricted_admins` variable allows granting `roles/cloudkms.admin` to specified principals, restricted via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) so that it only allows granting the roles needed for encryption/decryption on keys. This allows safe delegation of key management to subsequent Terraform stages like the Project Factory, for example to grant usage access on relevant keys to the service agent accounts for compute, storage, etc. + +To support these scenarios, key IAM bindings are configured by default to be additive, so as to enable other stages or Terraform configuration to safely co-manage bindings on the same keys. If this is not desired, follow the comments in the `core-dev.tf` and `core-prod.tf` files to switch to using authoritative bindings on keys. + +An example on how to configure keys: + +```hcl +# terraform.tfvars + +kms_defaults = { + locations = ["europe-west1", "europe-west3", "global"] + rotation_period = "7776000s" +} +kms_keys = { + compute = { + iam = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "user:user1@example.com" + ] + } + labels = { service = "compute" } + locations = null + rotation_period = null + } + storage = { + iam = null + labels = { service = "compute" } + locations = ["europe"] + rotation_period = null + } +} +``` + +The script will create one keyring for each location specified and create necessarily keys on each keyring. + +### VPC Service Controls configuration + +A set of variables allows configuring the VPC SC perimeters described above: + +- `vpc_sc_perimeter_projects` configures project membership in the three regular perimeters +- `vpc_sc_access_levels` configures access levels, which can then be associated to perimeters by key using the `vpc_sc_perimeter_access_levels` +- `vpc_sc_egress_policies` configures directional egress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_egress_policies` +- `vpc_sc_ingress_policies` configures directional ingress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_ingress_policies` + +This allows configuring VPC SC in a fairly flexible and concise way, without having to repeat similar definitions. Bridges perimeters configuration will be computed automatically to allow communication between regular perimeters: `landing <-> prod` and `landing <-> dev`. + +#### Dry-run vs enforced + +The VPC SC configuration is set up by default in dry-run mode, to allow easy experimentation, and detecting violations before enforcement. Once everything has been set up correctly, switching to enforced mode needs to be done in code, by swapping the contents of the `spec` and `status` attributes for perimeters in the `vpc-sc.tf` file. The effort involved is minimal (2 lines of code per perimeter), and comments help with identifying the correct lines. + +#### Perimeter resources + +Project are added to perimeters via the `vpc_sc_perimeter_projects`, and that's currently the only way of doing it without generating permadiffs or conflicts, since the only Terraform resource that works for both enforced and dry-run mode is authoritative (perimeter resources need to be managed in a single place). + +Once the Google Terraform Provider [implements support for dry-run mode in the additive resource](https://github.com/hashicorp/terraform-provider-google/issues/7270), it will be possible to concurrently manage perimeter resources both here and in subsequent Terraform configurations, for example to allow the Project Factory to add a project to a perimeter during the creation process. + +Bridge perimeters are auto-populated with all projects configured for the connected regular perimeters. + +An example of adding projects to perimeters using project numbers: + +```hcl +# terraform.tfvars + +vpc_sc_perimeter_projects = { + dev = ["projects/12345678", "projects/12345679"] + landing = ["projects/12345670"] + prod = ["projects/12345674", "projects/12345675"] +} +``` + +#### Access levels + +Below an example for an access level that allows unconditional ingress from a set of IP CIDR ranges can be configured once, and enabled on selected perimeters: + +```hcl +# terraform.tfvars + +vpc_sc_access_levels = { + on-prem = { + conditions = [{ + ip_subnetworks = ["10.0.0.0/24", "10.0.0.1/24"], + combining_function = null, members = null, negate = null, + regions = null, required_access_levels = null + }] + } +} +vpc_sc_perimeter_access_levels = { + dev = null + landing = ["on-prem"] + prod = ["on-prem"] +} +``` + +#### Ingress and Egress policies + +The same applies to Ingress and Egress policies, as shown in the examples below that reference the automation service account for this stage. + +Below you can find an ingress policy configuration that allows applying Terraform from outside the perimeter, useful when bringing up this stage to avoid generating violations: + +```hcl +# terraform.tfvars + +vpc_sc_ingress_policies = { + iac = { + ingress_from = { + identities = [ + "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com" + ] + source_access_levels = ["*"] + identity_type = null + source_resources = null + } + ingress_to = { + operations = [{ method_selectors = [], service_name = "*" }] + resources = ["*"] + } + } +} +vpc_sc_perimeter_ingress_policies = { + dev = ["iac"] + landing = ["iac"] + prod = ["iac"] +} +``` + +Below you can find an egress policy that allows writing Terraform state to the automation bucket, useful once Terraform starts running inside the perimeter in a pipeline: + +```hcl +# terraform.tfvars + +vpc_sc_egress_policies = { + iac-gcs = { + egress_from = { + identity_type = null + identities = [ + "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + egress_to = { + operations = [{ + method_selectors = ["*"], service_name = "storage.googleapis.com" + }] + resources = ["projects/123456782"] + } + } +} +vpc_sc_perimeter_ingress_policies = { + dev = ["iac-gcs"] + landing = ["iac-gcs"] + prod = ["iac-gcs"] +} +``` + +## Notes + +Some references that might be useful in setting up this stage: + +- [VPC SC CSCC requirements](https://cloud.google.com/security-command-center/docs/troubleshooting). + + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [core-dev.tf](./core-dev.tf) | None | kms · project | google_project_iam_member | +| [core-prod.tf](./core-prod.tf) | None | kms · project | google_project_iam_member | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-sc.tf](./vpc-sc.tf) | None | vpc-sc | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | bootstrap | +| folder_id | Folder to be used for the networking resources in folders/nnnn format. | string | ✓ | | resman | +| organization | Organization details. | object({…}) | ✓ | | bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | bootstrap | +| kms_defaults | Defaults used for KMS keys. | object({…}) | | {…} | | +| kms_keys | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | map(object({…})) | | {} | | +| kms_restricted_admins | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | map(list(string)) | | {} | | +| outputs_location | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| vpc_sc_access_levels | VPC SC access level definitions. | map(object({…})) | | {} | | +| vpc_sc_egress_policies | VPC SC egress policy defnitions. | map(object({…})) | | {} | | +| vpc_sc_ingress_policies | VPC SC ingress policy defnitions. | map(object({…})) | | {} | | +| vpc_sc_perimeter_access_levels | VPC SC perimeter access_levels. | object({…}) | | null | | +| vpc_sc_perimeter_egress_policies | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| vpc_sc_perimeter_ingress_policies | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| vpc_sc_perimeter_projects | VPC SC perimeter resources. | object({…}) | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| stage_perimeter_projects | Security project numbers. They can be added to perimeter resources. | | | + + + + + + + + + + + + + diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf new file mode 100644 index 00000000..b0c78485 --- /dev/null +++ b/fast/stages/02-security/core-dev.tf @@ -0,0 +1,64 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "dev-sec-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "dev-sec-core-0" + parent = var.folder_id + prefix = var.prefix + billing_account = var.billing_account_id + iam = { + "roles/cloudkms.viewer" = try(var.kms_restricted_admins.dev, []) + } + labels = { environment = "dev", team = "security" } + services = local.project_services +} + +module "dev-sec-kms" { + for_each = toset(local.kms_locations) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + project_id = module.dev-sec-project.project_id + keyring = { + location = each.key + name = "dev-${each.key}" + } + # rename to `key_iam` to switch to authoritative bindings + key_iam_additive = { + for k, v in local.kms_locations_keys[each.key] : k => v.iam + } + keys = local.kms_locations_keys[each.key] +} + +# TODO(ludo): add support for conditions to Fabric modules + +resource "google_project_iam_member" "dev_key_admin_delegated" { + for_each = toset(try(var.kms_restricted_admins.dev, [])) + project = module.dev-sec-project.project_id + role = "roles/cloudkms.admin" + member = each.key + condition { + title = "kms_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/cloudkms.cryptoKeyEncrypterDecrypter", + "roles/cloudkms.cryptoKeyEncrypterDecrypterViaDelegation" + ])) + ) + } + depends_on = [module.dev-sec-project] +} diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf new file mode 100644 index 00000000..0524788d --- /dev/null +++ b/fast/stages/02-security/core-prod.tf @@ -0,0 +1,64 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "prod-sec-project" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + name = "prod-sec-core-0" + parent = var.folder_id + prefix = var.prefix + billing_account = var.billing_account_id + iam = { + "roles/cloudkms.viewer" = try(var.kms_restricted_admins.prod, []) + } + labels = { environment = "prod", team = "security" } + services = local.project_services +} + +module "prod-sec-kms" { + for_each = toset(local.kms_locations) + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + project_id = module.prod-sec-project.project_id + keyring = { + location = each.key + name = "prod-${each.key}" + } + # rename to `key_iam` to switch to authoritative bindings + key_iam_additive = { + for k, v in local.kms_locations_keys[each.key] : k => v.iam + } + keys = local.kms_locations_keys[each.key] +} + +# TODO(ludo): add support for conditions to Fabric modules + +resource "google_project_iam_member" "prod_key_admin_delegated" { + for_each = toset(try(var.kms_restricted_admins.prod, [])) + project = module.prod-sec-project.project_id + role = "roles/cloudkms.admin" + member = each.key + condition { + title = "kms_sa_delegated_grants" + description = "Automation service account delegated grants" + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", [ + "roles/cloudkms.cryptoKeyEncrypterDecrypter", + "roles/cloudkms.cryptoKeyEncrypterDecrypterViaDelegation" + ])) + ) + } + depends_on = [module.prod-sec-project] +} diff --git a/fast/stages/02-security/diagram.png b/fast/stages/02-security/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..779c9039ec3d929903113b41c5f0f8b29084291d GIT binary patch literal 93474 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 Y=Y$Le`ng527~r3>yt>?n`>-efA88TRF#rGn literal 0 HcmV?d00001 diff --git a/fast/stages/02-security/main.tf b/fast/stages/02-security/main.tf new file mode 100644 index 00000000..13078d12 --- /dev/null +++ b/fast/stages/02-security/main.tf @@ -0,0 +1,47 @@ +/** + * 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 { + kms_keys = { + for k, v in var.kms_keys : k => { + iam = coalesce(v.iam, {}) + labels = coalesce(v.labels, {}) + locations = ( + v.locations == null + ? var.kms_defaults.locations + : v.locations + ) + rotation_period = ( + v.rotation_period == null + ? var.kms_defaults.rotation_period + : v.rotation_period + ) + } + } + kms_locations = distinct(flatten([ + for k, v in local.kms_keys : v.locations + ])) + kms_locations_keys = { + for loc in local.kms_locations : loc => { + for k, v in local.kms_keys : k => v if contains(v.locations, loc) + } + } + project_services = [ + "cloudkms.googleapis.com", + "secretmanager.googleapis.com", + "stackdriver.googleapis.com" + ] +} diff --git a/fast/stages/02-security/outputs.tf b/fast/stages/02-security/outputs.tf new file mode 100644 index 00000000..8f296d86 --- /dev/null +++ b/fast/stages/02-security/outputs.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 + * + * 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. + */ + +# optionally generate files for subsequent stages + +resource "local_file" "dev_sec_kms" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + filename = "${var.outputs_location}/yamls/02-security-kms-dev-keys.yaml" + content = yamlencode({ + for k, m in module.dev-sec-kms : k => m.key_ids + }) +} + +resource "local_file" "prod_sec_kms" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + filename = "${var.outputs_location}/yamls/02-security-kms-prod-keys.yaml" + content = yamlencode({ + for k, m in module.prod-sec-kms : k => m.key_ids + }) +} + +# outputs + +output "stage_perimeter_projects" { + description = "Security project numbers. They can be added to perimeter resources." + value = { + dev = ["projects/${module.dev-sec-project.number}"] + prod = ["projects/${module.prod-sec-project.number}"] + } +} diff --git a/fast/stages/02-security/variables.tf b/fast/stages/02-security/variables.tf new file mode 100644 index 00000000..0829f098 --- /dev/null +++ b/fast/stages/02-security/variables.tf @@ -0,0 +1,185 @@ +/** + * 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 "billing_account_id" { + # tfdoc:variable:source bootstrap + description = "Billing account id." + type = string +} + +variable "folder_id" { + # tfdoc:variable:source resman + description = "Folder to be used for the networking resources in folders/nnnn format." + type = string +} + +variable "groups" { + # tfdoc:variable:source bootstrap + description = "Group names to grant organization-level permissions." + type = map(string) + # https://cloud.google.com/docs/enterprise/setup-checklist + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "kms_defaults" { + description = "Defaults used for KMS keys." + type = object({ + locations = list(string) + rotation_period = string + }) + default = { + locations = ["europe", "europe-west1", "europe-west3", "global"] + rotation_period = "7776000s" + } +} + +variable "kms_keys" { + description = "KMS keys to create, keyed by name. Null attributes will be interpolated with defaults." + type = map(object({ + iam = map(list(string)) + labels = map(string) + locations = list(string) + rotation_period = string + })) + default = {} +} + +variable "kms_restricted_admins" { + description = "Map of environment => [identities] who can assign the encrypt/decrypt roles on keys." + type = map(list(string)) + default = {} +} + +variable "organization" { + # tfdoc:variable:source bootstrap + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "outputs_location" { + description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "vpc_sc_access_levels" { + description = "VPC SC access level definitions." + type = map(object({ + combining_function = string + conditions = list(object({ + ip_subnetworks = list(string) + members = list(string) + negate = bool + regions = list(string) + required_access_levels = list(string) + })) + })) + default = {} +} + +variable "vpc_sc_egress_policies" { + description = "VPC SC egress policy defnitions." + type = map(object({ + egress_from = object({ + identity_type = string + identities = list(string) + }) + egress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + default = {} +} + +variable "vpc_sc_ingress_policies" { + description = "VPC SC ingress policy defnitions." + type = map(object({ + ingress_from = object({ + identity_type = string + identities = list(string) + source_access_levels = list(string) + source_resources = list(string) + }) + ingress_to = object({ + operations = list(object({ + method_selectors = list(string) + service_name = string + })) + resources = list(string) + }) + })) + default = {} +} + +variable "vpc_sc_perimeter_access_levels" { + description = "VPC SC perimeter access_levels." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_egress_policies" { + description = "VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_ingress_policies" { + description = "VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} + +variable "vpc_sc_perimeter_projects" { + description = "VPC SC perimeter resources." + type = object({ + dev = list(string) + landing = list(string) + prod = list(string) + }) + default = null +} diff --git a/fast/stages/02-security/vpc-sc-restricted-services.yaml b/fast/stages/02-security/vpc-sc-restricted-services.yaml new file mode 100644 index 00000000..89844cd2 --- /dev/null +++ b/fast/stages/02-security/vpc-sc-restricted-services.yaml @@ -0,0 +1,88 @@ +# skip boilerplate check +- accessapproval.googleapis.com +- adsdatahub.googleapis.com +- aiplatform.googleapis.com +- alpha-documentai.googleapis.com +- apigee.googleapis.com +- apigeeconnect.googleapis.com +- artifactregistry.googleapis.com +- assuredworkloads.googleapis.com +- automl.googleapis.com +- bigquery.googleapis.com +- bigquerydatatransfer.googleapis.com +- bigtable.googleapis.com +- binaryauthorization.googleapis.com +- cloudasset.googleapis.com +- cloudbuild.googleapis.com +- cloudfunctions.googleapis.com +- cloudkms.googleapis.com +- cloudprofiler.googleapis.com +- cloudresourcemanager.googleapis.com +- cloudsearch.googleapis.com +- cloudtrace.googleapis.com +- composer.googleapis.com +- compute.googleapis.com +- connectgateway.googleapis.com +- contactcenterinsights.googleapis.com +- container.googleapis.com +- containeranalysis.googleapis.com +- containerregistry.googleapis.com +- containerthreatdetection.googleapis.com +- datacatalog.googleapis.com +- dataflow.googleapis.com +- datafusion.googleapis.com +- dataproc.googleapis.com +- datastream.googleapis.com +- dialogflow.googleapis.com +- dlp.googleapis.com +- dns.googleapis.com +- documentai.googleapis.com +- eventarc.googleapis.com +- file.googleapis.com +- gameservices.googleapis.com +- gkeconnect.googleapis.com +- gkehub.googleapis.com +- healthcare.googleapis.com +- iam.googleapis.com +- iaptunnel.googleapis.com +- language.googleapis.com +- lifesciences.googleapis.com +- logging.googleapis.com +- managedidentities.googleapis.com +- memcache.googleapis.com +- meshca.googleapis.com +- metastore.googleapis.com +- ml.googleapis.com +- monitoring.googleapis.com +- networkconnectivity.googleapis.com +- networkmanagement.googleapis.com +- networksecurity.googleapis.com +- networkservices.googleapis.com +- notebooks.googleapis.com +- opsconfigmonitoring.googleapis.com +- osconfig.googleapis.com +- oslogin.googleapis.com +- privateca.googleapis.com +- pubsub.googleapis.com +- pubsublite.googleapis.com +- recaptchaenterprise.googleapis.com +- recommender.googleapis.com +- redis.googleapis.com +- run.googleapis.com +- secretmanager.googleapis.com +- servicecontrol.googleapis.com +- servicedirectory.googleapis.com +- spanner.googleapis.com +- speakerid.googleapis.com +- speech.googleapis.com +- sqladmin.googleapis.com +- storage.googleapis.com +- storagetransfer.googleapis.com +- texttospeech.googleapis.com +- tpu.googleapis.com +- trafficdirector.googleapis.com +- transcoder.googleapis.com +- translate.googleapis.com +- videointelligence.googleapis.com +- vision.googleapis.com +- vpcaccess.googleapis.com diff --git a/fast/stages/02-security/vpc-sc.tf b/fast/stages/02-security/vpc-sc.tf new file mode 100644 index 00000000..f22added --- /dev/null +++ b/fast/stages/02-security/vpc-sc.tf @@ -0,0 +1,167 @@ +/** + * 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 { + # compute the number of projects in each perimeter to detect which to create + vpc_sc_counts = { + for k in ["dev", "landing", "prod"] : k => length( + coalesce(try(var.vpc_sc_perimeter_projects[k], null), []) + ) + } + # dereference perimeter egress policy names to the actual objects + vpc_sc_perimeter_egress_policies = { + for k, v in coalesce(var.vpc_sc_perimeter_egress_policies, {}) : + k => [ + for i in coalesce(v, []) : var.vpc_sc_egress_policies[i] + if lookup(var.vpc_sc_egress_policies, i, null) != null + ] + } + # dereference perimeter ingress policy names to the actual objects + vpc_sc_perimeter_ingress_policies = { + for k, v in coalesce(var.vpc_sc_perimeter_ingress_policies, {}) : + k => [ + for i in coalesce(v, []) : var.vpc_sc_ingress_policies[i] + if lookup(var.vpc_sc_ingress_policies, i, null) != null + ] + } + # get the list of restricted services from the yaml file + vpcsc_restricted_services = yamldecode( + file("${path.module}/vpc-sc-restricted-services.yaml") + ) +} + +module "vpc-sc" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/vpc-sc?ref=ea17e65" + # only enable if we have projects defined for perimeters + count = anytrue([for k, v in local.vpc_sc_counts : v > 0]) ? 1 : 0 + access_policy = null + access_policy_create = { + parent = "organizations/${var.organization.id}" + title = "default" + } + access_levels = coalesce(try(var.vpc_sc_access_levels, null), {}) + # bridge type perimeters + service_perimeters_bridge = merge( + # landing to dev, only we have projects in landing and dev perimeters + local.vpc_sc_counts.landing * local.vpc_sc_counts.dev == 0 ? {} : { + landing_to_dev = { + status_resources = null + spec_resources = concat( + var.vpc_sc_perimeter_projects.landing, + var.vpc_sc_perimeter_projects.dev + ) + use_explicit_dry_run_spec = true + } + }, + # landing to prod, only we have projects in landing and prod perimeters + local.vpc_sc_counts.landing * local.vpc_sc_counts.prod == 0 ? {} : { + landing_to_prod = { + status_resources = null + spec_resources = concat( + var.vpc_sc_perimeter_projects.landing, + var.vpc_sc_perimeter_projects.prod + ) + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + } + ) + # regular type perimeters + service_perimeters_regular = merge( + # dev if we have projects in var.vpc_sc_perimeter_projects.dev + local.vpc_sc_counts.dev == 0 ? {} : { + dev = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.dev, null), [] + ) + resources = var.vpc_sc_perimeter_projects.dev + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.dev, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.dev, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + }, + # prod if we have projects in var.vpc_sc_perimeter_projects.prod + local.vpc_sc_counts.prod == 0 ? {} : { + prod = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.prod, null), [] + ) + # combine the security project, and any specified in the variable + resources = var.vpc_sc_perimeter_projects.prod + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.prod, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.prod, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + }, + # prod if we have projects in var.vpc_sc_perimeter_projects.prod + local.vpc_sc_counts.landing == 0 ? {} : { + landing = { + spec = { + access_levels = coalesce( + try(var.vpc_sc_perimeter_access_levels.landing, null), [] + ) + resources = var.vpc_sc_perimeter_projects.landing + restricted_services = local.vpcsc_restricted_services + egress_policies = try( + local.vpc_sc_perimeter_egress_policies.landing, null + ) + ingress_policies = try( + local.vpc_sc_perimeter_ingress_policies.landing, null + ) + # replace with commented block to enable vpc restrictions + vpc_accessible_services = null + # vpc_accessible_services = { + # allowed_services = ["RESTRICTED-SERVICES"] + # enable_restriction = true + # } + } + status = null + # set to null and switch spec and status above to enforce + use_explicit_dry_run_spec = true + } + } + ) +} From 0c3ecb374514b373538209f2b3f0fd68d1ca4966 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:33:51 +0100 Subject: [PATCH 029/132] Import Fast from dev repository. Co-authored-by: Julio Castillo Co-authored-by: Ludovico Magnocavallo Co-authored-by: Simone Ruffilli --- .gitignore | 5 + .../assets/schemas/firewall_rules.schema.yaml | 15 ++ .../schemas/hierarchical_rules.schema.yaml | 11 + fast/assets/schemas/project.schema.yaml | 43 ++++ .../schemas/project_defaults.schema.yaml | 14 ++ fast/assets/schemas/subnet.schema.yaml | 15 ++ fast/assets/templates/providers.tpl | 30 +++ fast/stages/01-resman/README.md | 193 ++++++++++++++++++ fast/stages/01-resman/billing.tf | 57 ++++++ fast/stages/01-resman/branch-gke.tf | 144 +++++++++++++ fast/stages/01-resman/branch-networking.tf | 59 ++++++ fast/stages/01-resman/branch-sandbox.tf | 59 ++++++ fast/stages/01-resman/branch-security.tf | 61 ++++++ fast/stages/01-resman/branch-teams.tf | 185 +++++++++++++++++ fast/stages/01-resman/diagram.png | Bin 0 -> 109088 bytes fast/stages/01-resman/main.tf | 35 ++++ fast/stages/01-resman/organization.tf | 144 +++++++++++++ fast/stages/01-resman/outputs.tf | 166 +++++++++++++++ fast/stages/01-resman/providers.tf | 1 + fast/stages/01-resman/variables.tf | 104 ++++++++++ tests/fast/stages/__init__.py | 13 ++ tests/fast/stages/s00_bootstrap/__init__.py | 13 ++ .../fast/stages/s00_bootstrap/fixture/main.tf | 29 +++ tests/fast/stages/s00_bootstrap/test_plan.py | 33 +++ tests/fast/stages/s01_resman/__init__.py | 13 ++ tests/fast/stages/s01_resman/fixture/main.tf | 42 ++++ tests/fast/stages/s01_resman/test_plan.py | 20 ++ tests/fast/stages/s02_networking/__init__.py | 13 ++ tests/fast/stages/s02_networking/fixture/data | 1 + .../stages/s02_networking/fixture/main.tf | 30 +++ tests/fast/stages/s02_networking/test_plan.py | 20 ++ tests/fast/stages/s02_security/__init__.py | 13 ++ tests/fast/stages/s02_security/fixture/data | 1 + .../fast/stages/s02_security/fixture/main.tf | 109 ++++++++++ tests/fast/stages/s02_security/test_plan.py | 20 ++ .../stages/s03_project_factory/__init__.py | 13 ++ .../fixture/data/defaults.yaml | 22 ++ .../fixture/data/projects/project.yaml | 98 +++++++++ .../s03_project_factory/fixture/main.tf | 57 ++++++ .../terraform-bootstrap.auto.tfvars.json | 4 + .../terraform-networking.auto.tfvars.json | 5 + .../s03_project_factory/fixture/variables.tf | 61 ++++++ .../stages/s03_project_factory/test_plan.py | 20 ++ 43 files changed, 1991 insertions(+) create mode 100644 fast/assets/schemas/firewall_rules.schema.yaml create mode 100644 fast/assets/schemas/hierarchical_rules.schema.yaml create mode 100644 fast/assets/schemas/project.schema.yaml create mode 100644 fast/assets/schemas/project_defaults.schema.yaml create mode 100644 fast/assets/schemas/subnet.schema.yaml create mode 100644 fast/assets/templates/providers.tpl create mode 100644 fast/stages/01-resman/README.md create mode 100644 fast/stages/01-resman/billing.tf create mode 100644 fast/stages/01-resman/branch-gke.tf create mode 100644 fast/stages/01-resman/branch-networking.tf create mode 100644 fast/stages/01-resman/branch-sandbox.tf create mode 100644 fast/stages/01-resman/branch-security.tf create mode 100644 fast/stages/01-resman/branch-teams.tf create mode 100644 fast/stages/01-resman/diagram.png create mode 100644 fast/stages/01-resman/main.tf create mode 100644 fast/stages/01-resman/organization.tf create mode 100644 fast/stages/01-resman/outputs.tf create mode 120000 fast/stages/01-resman/providers.tf create mode 100644 fast/stages/01-resman/variables.tf create mode 100644 tests/fast/stages/__init__.py create mode 100644 tests/fast/stages/s00_bootstrap/__init__.py create mode 100644 tests/fast/stages/s00_bootstrap/fixture/main.tf create mode 100644 tests/fast/stages/s00_bootstrap/test_plan.py create mode 100644 tests/fast/stages/s01_resman/__init__.py create mode 100644 tests/fast/stages/s01_resman/fixture/main.tf create mode 100644 tests/fast/stages/s01_resman/test_plan.py create mode 100644 tests/fast/stages/s02_networking/__init__.py create mode 120000 tests/fast/stages/s02_networking/fixture/data create mode 100644 tests/fast/stages/s02_networking/fixture/main.tf create mode 100644 tests/fast/stages/s02_networking/test_plan.py create mode 100644 tests/fast/stages/s02_security/__init__.py create mode 120000 tests/fast/stages/s02_security/fixture/data create mode 100644 tests/fast/stages/s02_security/fixture/main.tf create mode 100644 tests/fast/stages/s02_security/test_plan.py create mode 100644 tests/fast/stages/s03_project_factory/__init__.py create mode 100644 tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml create mode 100644 tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml create mode 100644 tests/fast/stages/s03_project_factory/fixture/main.tf create mode 100644 tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json create mode 100644 tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json create mode 100644 tests/fast/stages/s03_project_factory/fixture/variables.tf create mode 100644 tests/fast/stages/s03_project_factory/test_plan.py diff --git a/.gitignore b/.gitignore index 0f0dea4f..d1a5e47c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ bundle.zip **/packer_cache **/*.pkrvars.hcl fixture_* +fast/configs +fast/stages/**/providers.tf +fast/stages/**/terraform.tfvars +fast/stages/**/terraform.tfvars.json +fast/stages/**/terraform-*.auto.tfvars.json diff --git a/fast/assets/schemas/firewall_rules.schema.yaml b/fast/assets/schemas/firewall_rules.schema.yaml new file mode 100644 index 00000000..ebb62772 --- /dev/null +++ b/fast/assets/schemas/firewall_rules.schema.yaml @@ -0,0 +1,15 @@ +map(include('firewall_rule')) +--- +firewall_rule: + description: str() + direction: enum("INGRESS", "EGRESS") + action: enum("allow", "deny") + sources: list(str()) + ranges: list(str()) + targets: list(str()) + use_service_accounts: bool() + rules: list(include('rule')) +--- +rule: + protocol: enum("tcp", "udp", "all") + ports: list(num()) diff --git a/fast/assets/schemas/hierarchical_rules.schema.yaml b/fast/assets/schemas/hierarchical_rules.schema.yaml new file mode 100644 index 00000000..d8c72b1d --- /dev/null +++ b/fast/assets/schemas/hierarchical_rules.schema.yaml @@ -0,0 +1,11 @@ +map(include('hierarchical_rule')) +--- +hierarchical_rule: + description: str() + direction: enum("INGRESS", "EGRESS") + action: enum("allow", "deny") + priority: int() + ranges: list(str()) + target_resources: any(null(), list(str())) + ports: map(list(str(), required=False)) + enable_logging: bool() diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml new file mode 100644 index 00000000..e914e5a7 --- /dev/null +++ b/fast/assets/schemas/project.schema.yaml @@ -0,0 +1,43 @@ +billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) +billing_alert: any(include('billing_alert'), null(), required=False) # If set to null, use defaults +dns_zones: list(str(), required=False) +essential_contacts: list(str(), required=False) # Also used for billing alerts +folder_id: str(matches='(organizations/|folders/)[0-9]*$') +group_iam: map(list(str()), key=str(), required=False) +iam: map(list(str()), key=str(), required=False) +kms_service_agents: map(list(str()), key=str(), required=False) +labels: map(str(), key=str(), required=False) +org_policies: include('org_policies', required=False) +secrets: map(list(str()), key=str(), required=False) +service_accounts: map(list(str()), required=False) +services: list(str(matches='^[a-z-]*\.googleapis\.com$'), required=False) +services_iam: map(list(str()), key=str(), required=False) +vpc: include('vpc', required=False) +--- +billing_alert: + amount: int() + thresholds: include('billing_alert_thresholds') + credit_treatment: enum("INCLUDE_ALL_CREDITS", "EXCLUDE_ALL_CREDITS") +--- +billing_alert_thresholds: + current: list(num(min=0, max=1)) + forecasted: list(num(min=0, max=1)) +--- +gke_setup: + enable_security_admin: bool(required=False) + enable_host_service_agent: bool(required=False) +--- +org_policies: + policy_boolean: map(bool(), key=str(matches='^constraints/[A-z\.]*$'), required=False) + policy_list: map(include('policy_list'), key=str(matches='^constraints/[A-z\.]*$'), required=False) +--- +policy_list: + inherit_from_parent: any(bool(), null()) + suggested_value: any(str(), null()) + status: any(bool(), null()) + values: list(str()) +--- +vpc: + host_project: str(matches='[a-z]([-a-z0-9]*[a-z0-9])?', min=6, max=30) + gke_setup: include('gke_setup', required=False) + subnets_iam: map(list(str()), key=str(matches='^[a-z0-9-]*/[a-z0-9-]*$'), required=False) diff --git a/fast/assets/schemas/project_defaults.schema.yaml b/fast/assets/schemas/project_defaults.schema.yaml new file mode 100644 index 00000000..52676baa --- /dev/null +++ b/fast/assets/schemas/project_defaults.schema.yaml @@ -0,0 +1,14 @@ +billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) +billing_alert: any(include('billing_alert'), null(), required=False) +essential_contacts: list(str(), required=False) +labels: map(str(), key=str(), required=False) +notification_channels: list(str(), required=False) +--- +billing_alert: + amount: int() + thresholds: include('billing_alert_thresholds') + credit_treatment: enum('INCLUDE_ALL_CREDITS', 'EXCLUDE_ALL_CREDITS') +--- +billing_alert_thresholds: + current: list(num(min=0, max=1)) + forecasted: list(num(min=0, max=1)) diff --git a/fast/assets/schemas/subnet.schema.yaml b/fast/assets/schemas/subnet.schema.yaml new file mode 100644 index 00000000..1bf10ee8 --- /dev/null +++ b/fast/assets/schemas/subnet.schema.yaml @@ -0,0 +1,15 @@ +region: str() +description: str() +ip_cidr_range: str() +# optional attributes +private_ip_google_access: bool(required=False) # defaults to true +iam_users: list(str(), required=False) +iam_groups: list(str(), required=False) +iam_service_accounts: list(str(), required=False) +secondary_ip_ranges: list(map(str()), key=str(), required=False) +flow_logs: any(include('flow_logs'), required=False) +--- +flow_logs: + - aggregation_interval: enum('INTERVAL_5_SEC', 'INTERVAL_30_SEC', 'INTERVAL_1_MIN', 'INTERVAL_5_MIN', 'INTERVAL_10_MIN', 'INTERVAL_15_MIN', required=False) + - flow_sampling: num(min=0, max=1, required=False) + - metadata: enum('EXCLUDE_ALL_METADATA', 'INCLUDE_ALL_METADATA', 'CUSTOM_METADATA', required=False) diff --git a/fast/assets/templates/providers.tpl b/fast/assets/templates/providers.tpl new file mode 100644 index 00000000..7f0ce142 --- /dev/null +++ b/fast/assets/templates/providers.tpl @@ -0,0 +1,30 @@ +/** + * 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. + */ + +terraform { + backend "gcs" { + bucket = "${bucket}" + impersonate_service_account = "${sa}" + } +} +provider "google" { + impersonate_service_account = "${sa}" +} +provider "google-beta" { + impersonate_service_account = "${sa}" +} + +# end provider.tf for ${name} diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md new file mode 100644 index 00000000..966e2184 --- /dev/null +++ b/fast/stages/01-resman/README.md @@ -0,0 +1,193 @@ +# Resource hierarchy + +This stage performs two important tasks: + +- create the top-level hierarchy of folders, and the associated resources used later on to automate each part of the hierarchy (eg. Networking) +- set organization policies on the organization, and any exception required on specific folders + +The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. + +The following diagram is a high level reference of the resources created and managed here: + +![Resource-management diagram](diagram.png) + +## Design overview and choices + +Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers, where the hierarchy is laid out following two conceptually different approaches: + +- core or shared resources are grouped in hierarchy branches that map to their type or purpose (e.g. Networking) +- team or application resources are grouped in lower level hierarchy branches that map to management or operational considerations (e.g. which team manages a set of applications, or owns a subset of company data, etc.) + +This split approach usually represents well functional and operational patterns, where core resources are centrally managed by individual teams (e.g. networking, security, fleets of similar VMS, etc.), while teams need more granularity to access managed services used by the applications they maintain. + +The approach also adapts to different high level requirements: + +- it can be used either for single organizations containing multiple environments, or with multiple organizations dedicated to specific environments (e.g. prod/nonprod), as the environment split is implemented at the project or lower folder level +- it adapts to complex scenarios, with different countries or corporate entities using the same GCP organization, as core services are typically shared, and/or an extra layer on top can be used as a drop-in to implement the country/entity separation + +Additionally, a few critical benefits are directly provided by this design: + +- core services are clearly separated, with very few touchpoints where IAM and security policies need to be applied (typically their top-level folder) +- adding a new set of core services (e.g. shared GKE clusters) is a trivial operation that does not break the existing design +- grouping application resources and services using teams or business logic is a flexible approach, which maps well to typical operational or budget requirements +- automation stages (e.g. Networking) can be segregated in a simple and effective way, by creating the required service accounts and buckets for each stage here, and applying a handful of IAM roles to the relevant folder + +For a discussion on naming, please refer to the [Bootstrap stage documentation](../00-bootstrap/README.md#naming), as the same approach is shared by all stages. + +## How to run this stage + +This stage is meant to be executed after the [bootstrap](../00-bootstrap) stage has run, as it leverages the automation service account and bucket created there. The relevant user groups must also exist, but that's one of the requirements for the previous stage too, so if you ran that successfully, you're good to go. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap stage for the actual roles needed. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Providers configuration + +The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). + +To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. + +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 +``` + +If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: + +```bash +cd ../00-bootstrap +terraform output -json providers | jq -r '.["01-resman"]' \ + > ../01-resman/providers.tf +``` + +### Variable configuration + +There are two broad sets of variables you will need to fill in: + +- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) +- variables specific to resources managed by this stage + +To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. + +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 +``` + +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. + +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. The sections below also describe some of the possible customizations. For billing configurations, refer to the [Bootstrap documentation on billing](../00-bootstrap/README.md#billing-account) as the `billing_account` variable is identical across all stages. + +Once done, you can run this stage: + +```bash +terraform init +terraform apply +``` + +## Customizations + +### Team folders + +This stage provides a single built-in customization that offers a minimal (but usable) implementation of the "application" or "business" grouping for resources discussed above. The `team_folders` variable allows you to specify a map of team name and groups, that will result in folders, automation service accounts, and IAM policies applied. + +Consider the following example + +```hcl +team_folders = { + team-a = { + descriptive_name = "Team A" + group_iam = { + "team-a@gcp-pso-italy.net" = [ + "roles/viewer" + ] + } + impersonation_groups = ["team-a-admins@gcp-pso-italy.net"] + } +} +``` + +This will result in + +- a "Team A" folder under the "Teams" folder +- one GCS bucket in the automation project +- one service account in the automation project with the correct IAM policies on the folder and bucket +- a IAM policy on the folder that assigns `roles/viewer` to the `team-a` group +- a IAM policy on the service account that allows `team-a` to impersonate it + +This allows to centralize the minimum set of resources to delegate control of each team's folder to a pipeline, and/or to the team group. This can be used as a starting point for scenarios that implement more complex requirements (e.g. environment folders per team, etc.). + +### Organization policies + +Organization policies are laid out in an explicit manner in the `organization.tf` file, so it's fairly easy to add or remove specific policies. + +For policies where additional data is needed, a root-level `organization_policy_configs` variable allows passing in specific data. Its built-in use to add additional organizations to the [Domain Restricted Sharing](https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains) policy, can be taken as an example on how to leverage it for additional customizations. + +### IAM + +IAM roles can be easily edited in the relevant `branch-xxx.tf` file, following the best practice outlined in the [bootstrap stage](../00-bootstrap#customizations) documentation of separating user-level and service-account level IAM policies in modules' `iam_groups`, `iam`, and `iam_additive` variables. + +### Additional folders + +Due to its simplicity, this stage lends itself easily to customizations: adding a new top-level branch (e.g. for shared GKE clusters) is as easy as cloning one of the `branch-xxx.tf` files, and changing names. + + + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [billing.tf](./billing.tf) | Billing resources for external billing use cases. | organization | google_billing_account_iam_member | +| [branch-gke.tf](./branch-gke.tf) | GKE stage resources. | folder · gcs · iam-service-account | | +| [branch-networking.tf](./branch-networking.tf) | Networking stage resources. | folder · gcs · iam-service-account | | +| [branch-sandbox.tf](./branch-sandbox.tf) | Sandbox stage resources. | folder · gcs · iam-service-account | | +| [branch-security.tf](./branch-security.tf) | Security stage resources. | folder · gcs · iam-service-account | | +| [branch-teams.tf](./branch-teams.tf) | Team stages resources. | folder · gcs · iam-service-account | | +| [main.tf](./main.tf) | Module-level locals and resources. | | | +| [organization.tf](./organization.tf) | Organization policies. | organization | | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| automation_project_id | Project id for the automation project created by the bootstrap stage. | string | ✓ | | 00-bootstrap | +| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_roles | Custom roles defined at the org level, in key => id format. | map(string) | | {} | 00-bootstrap | +| groups | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | +| organization_policy_configs | Organization policies customization. | object({…}) | | null | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| team_folders | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| networking | Data for the networking stage. | | 02-networking | +| project_factories | Data for the project factories stage. | | xx-teams | +| providers | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · xx-sandbox · xx-teams | +| sandbox | Data for the sandbox stage. | | xx-sandbox | +| security | Data for the networking stage. | | 02-security | +| teams | Data for the teams stage. | | | +| tfvars | Terraform variable files for the following stages. | ✓ | | + + + + + + + + + + diff --git a/fast/stages/01-resman/billing.tf b/fast/stages/01-resman/billing.tf new file mode 100644 index 00000000..ae753177 --- /dev/null +++ b/fast/stages/01-resman/billing.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Billing resources for external billing use cases. + +locals { + # used here for convenience, in organization.tf members are explicit + billing_ext_users = concat( + [ + module.branch-network-sa.iam_email, + module.branch-security-sa.iam_email, + ], + # enable if individual teams can create their own projects + # [ + # for k, v in module.branch-teams-team-sa : v.iam_email + # ], + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) +} + +# billing account in same org (resources is in the organization.tf file) + +# billing account in a different org + +module "billing-organization-ext" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + count = local.billing_org_ext ? 1 : 0 + organization_id = "organizations/${var.billing_account.organization_id}" + iam_additive = { + "roles/billing.user" = local.billing_ext_users + } +} + +# standalone billing account + +resource "google_billing_account_iam_member" "billing_ext_admin" { + for_each = toset( + local.billing_ext ? local.billing_ext_users : [] + ) + billing_account_id = var.billing_account.id + role = "roles/billing.user" + member = each.key +} diff --git a/fast/stages/01-resman/branch-gke.tf b/fast/stages/01-resman/branch-gke.tf new file mode 100644 index 00000000..d7681995 --- /dev/null +++ b/fast/stages/01-resman/branch-gke.tf @@ -0,0 +1,144 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description GKE stage resources. + +locals { + gke_branch_group_iam = { + (local.groups.gcp-devops) = [ + "roles/viewer", + # ... + ] + } +} + +# top-level GKE folder + +module "branch-gke-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "GKE" +} + +# environment: development folder and automation resources + +moved { + from = module.branch-gke-env-folder["dev"] + to = module.branch-gke-dev-folder +} + +module "branch-gke-dev-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + # naming: environment descriptive name + name = "Development" + parent = module.branch-gke-folder.id + group_iam = local.gke_branch_group_iam + iam = { + "roles/logging.admin" = [ + module.branch-gke-dev-sa.iam_email + ] + "roles/owner" = [ + module.branch-gke-dev-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-gke-dev-sa.iam_email + ] + } +} + +moved { + from = module.branch-gke-env-sa["dev"] + to = module.branch-gke-dev-sa +} + +module "branch-gke-dev-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + description = "Terraform GKE development service account." + prefix = local.prefixes.dev +} + +moved { + from = module.branch-gke-gcs["dev"] + to = module.branch-gke-dev-gcs +} + +module "branch-gke-dev-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gke-dev-sa.iam_email] + } +} + +# environment: production folder and automation resources + +moved { + from = module.branch-gke-env-folder["prod"] + to = module.branch-gke-prod-folder +} + +module "branch-gke-prod-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + # naming: environment descriptive name + name = "Production" + parent = module.branch-gke-folder.id + group_iam = local.gke_branch_group_iam + iam = { + "roles/logging.admin" = [ + module.branch-gke-prod-sa.iam_email + ] + "roles/owner" = [ + module.branch-gke-prod-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-gke-prod-sa.iam_email + ] + } +} + +moved { + from = module.branch-gke-env-sa["prod"] + to = module.branch-gke-prod-sa +} + +module "branch-gke-prod-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + description = "Terraform GKE production service account." + prefix = local.prefixes.prod +} + +moved { + from = module.branch-gke-gcs["prod"] + to = module.branch-gke-prod-gcs +} + +module "branch-gke-prod-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + name = "resman-gke-0" + project_id = var.automation_project_id + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gke-prod-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-networking.tf b/fast/stages/01-resman/branch-networking.tf new file mode 100644 index 00000000..ef95ecf3 --- /dev/null +++ b/fast/stages/01-resman/branch-networking.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Networking stage resources. + +module "branch-network-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Networking" + group_iam = { + (local.groups.gcp-network-admins) = [ + # add any needed roles for resources/services not managed via Terraform, + # or replace editor with ~viewer if no broad resource management needed + # e.g. + # "roles/compute.networkAdmin", + # "roles/dns.admin", + # "roles/compute.securityAdmin", + "roles/editor", + ] + } + iam = { + "roles/logging.admin" = [module.branch-network-sa.iam_email] + "roles/owner" = [module.branch-network-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-network-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-network-sa.iam_email] + } +} + +module "branch-network-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-networking-0" + description = "Terraform resman networking service account." + prefix = local.prefixes.prod +} + +module "branch-network-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-networking-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-network-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-sandbox.tf b/fast/stages/01-resman/branch-sandbox.tf new file mode 100644 index 00000000..c9f244f5 --- /dev/null +++ b/fast/stages/01-resman/branch-sandbox.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Sandbox stage resources. + +module "branch-sandbox-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Sandbox" + iam = { + "roles/logging.admin" = [module.branch-sandbox-sa.iam_email] + "roles/owner" = [module.branch-sandbox-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-sandbox-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-sandbox-sa.iam_email] + } + policy_boolean = { + "constraints/sql.restrictPublicIp" = false + } + policy_list = { + "constraints/compute.vmExternalIpAccess" = { + inherit_from_parent = false + suggested_value = null + status = true + values = [] + } + } +} + +module "branch-sandbox-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-sandbox-0" + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-sandbox-sa.iam_email] + } +} + +module "branch-sandbox-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-sandbox-0" + description = "Terraform resman sandbox service account." + prefix = local.prefixes.dev +} diff --git a/fast/stages/01-resman/branch-security.tf b/fast/stages/01-resman/branch-security.tf new file mode 100644 index 00000000..9f6a12ff --- /dev/null +++ b/fast/stages/01-resman/branch-security.tf @@ -0,0 +1,61 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Security stage resources. + +module "branch-security-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Security" + group_iam = { + (local.groups.gcp-security-admins) = [ + # add any needed roles for resources/services not managed via Terraform, + # e.g. + # "roles/bigquery.admin", + # "roles/cloudasset.owner", + # "roles/cloudkms.admin", + # "roles/logging.admin", + # "roles/secretmanager.admin", + # "roles/storage.admin", + "roles/viewer" + ] + } + iam = { + "roles/logging.admin" = [module.branch-security-sa.iam_email] + "roles/owner" = [module.branch-security-sa.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-security-sa.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email] + } +} + +module "branch-security-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-security-0" + description = "Terraform resman security service account." + prefix = local.prefixes.prod +} + +module "branch-security-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-security-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-security-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/branch-teams.tf b/fast/stages/01-resman/branch-teams.tf new file mode 100644 index 00000000..757c23ad --- /dev/null +++ b/fast/stages/01-resman/branch-teams.tf @@ -0,0 +1,185 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Team stages resources. + +# top-level teams folder and service account + +module "branch-teams-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + parent = "organizations/${var.organization.id}" + name = "Teams" +} + +module "branch-teams-prod-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-teams-0" + description = "Terraform resman production service account." + prefix = local.prefixes.prod +} + +# Team-level folders, service accounts and buckets for each individual team + +module "branch-teams-team-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-folder.id + name = each.value.descriptive_name + group_iam = each.value.group_iam == null ? {} : each.value.group_iam +} + +module "branch-teams-team-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + project_id = var.automation_project_id + name = "teams-${each.key}-0" + description = "Terraform team ${each.key} service account." + prefix = local.prefixes.prod + iam = { + "roles/iam.serviceAccountTokenCreator" = ( + each.value.impersonation_groups == null + ? [] + : [for g in each.value.impersonation_groups : "group:${g}"] + ) + } +} + +module "branch-teams-team-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + project_id = var.automation_project_id + name = "teams-${each.key}-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-team-sa[each.key].iam_email] + } +} + +# environment: development folder and project factory automation resources + +module "branch-teams-team-dev-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-team-folder[each.key].id + # naming: environment descriptive name + name = "${module.branch-teams-team-folder[each.key].name} - Development" + # environment-wide human permissions on the whole teams environment + group_iam = {} + iam = { + # remove owner here and at project level if SA does not manage project resources + "roles/owner" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/logging.admin" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/resourcemanager.folderAdmin" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-teams-dev-projectfactory-sa.iam_email + ] + } +} + +moved { + from = module.branch-teams-project-factory-sa["dev"] + to = module.branch-teams-dev-projectfactory-sa +} + +module "branch-teams-dev-projectfactory-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + # naming: environment in description + description = "Terraform project factory development service account." + prefix = local.prefixes.dev +} + +moved { + from = module.branch-teams-project-factory-gcs["dev"] + to = module.branch-teams-dev-projectfactory-gcs +} + +module "branch-teams-dev-projectfactory-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + prefix = local.prefixes.dev + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-dev-projectfactory-sa.iam_email] + } +} + +# environment: production folder and project factory automation resources + +module "branch-teams-team-prod-folder" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" + for_each = coalesce(var.team_folders, {}) + parent = module.branch-teams-team-folder[each.key].id + # naming: environment descriptive name + name = "${module.branch-teams-team-folder[each.key].name} - Production" + # environment-wide human permissions on the whole teams environment + group_iam = {} + iam = { + # remove owner here and at project level if SA does not manage project resources + "roles/owner" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/logging.admin" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/resourcemanager.folderAdmin" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + "roles/resourcemanager.projectCreator" = [ + module.branch-teams-prod-projectfactory-sa.iam_email + ] + } +} + +moved { + from = module.branch-teams-project-factory-sa["prod"] + to = module.branch-teams-prod-projectfactory-sa +} + +module "branch-teams-prod-projectfactory-sa" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + # naming: environment in description + description = "Terraform project factory production service account." + prefix = local.prefixes.prod +} + +moved { + from = module.branch-teams-project-factory-gcs["prod"] + to = module.branch-teams-prod-projectfactory-gcs +} + +module "branch-teams-prod-projectfactory-gcs" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + project_id = var.automation_project_id + name = "resman-pf-0" + prefix = local.prefixes.prod + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-teams-prod-projectfactory-sa.iam_email] + } +} diff --git a/fast/stages/01-resman/diagram.png b/fast/stages/01-resman/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..99c6e26d227fbfe8ad586c818e3fa4fa3b03b38e GIT binary patch literal 109088 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≶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 D?Q6k7 literal 0 HcmV?d00001 diff --git a/fast/stages/01-resman/main.tf b/fast/stages/01-resman/main.tf new file mode 100644 index 00000000..2aedb7ce --- /dev/null +++ b/fast/stages/01-resman/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 { + # convenience flags that express where billing account resides + billing_ext = var.billing_account.organization_id == null + billing_org = var.billing_account.organization_id == var.organization.id + billing_org_ext = !local.billing_ext && !local.billing_org + groups = { + for k, v in var.groups : + k => "${v}@${var.organization.domain}" + } + groups_iam = { + for k, v in local.groups : + k => "group:${v}" + } + # naming: environment names + prefixes = { + dev = "${var.prefix}-dev" + prod = "${var.prefix}-prod" + } +} diff --git a/fast/stages/01-resman/organization.tf b/fast/stages/01-resman/organization.tf new file mode 100644 index 00000000..f8065be4 --- /dev/null +++ b/fast/stages/01-resman/organization.tf @@ -0,0 +1,144 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization policies. + + +locals { + # set to the empty list if you remove the GKE branch + branch_gke_sa_iam_emails = [ + module.branch-gke-dev-sa.iam_email, + module.branch-gke-prod-sa.iam_email + ] + # set to the empty list if you remove the teams branch + branch_teams_pf_sa_iam_emails = [ + module.branch-teams-dev-projectfactory-sa.iam_email, + module.branch-teams-prod-projectfactory-sa.iam_email + ] + list_deny = { + inherit_from_parent = false + suggested_value = null + status = false + values = [] + } + policy_configs = ( + var.organization_policy_configs == null + ? {} + : var.organization_policy_configs + ) +} + +module "organization" { + source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + organization_id = "organizations/${var.organization.id}" + # IAM additive bindings, granted via the restricted Organization Admin custom + # role assigned in stage 00; they need to be additive to avoid conflicts + iam_additive = merge( + { + (var.custom_roles.xpnServiceAdmin) = concat( + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) + "roles/accesscontextmanager.policyAdmin" = [ + module.branch-security-sa.iam_email + ] + "roles/billing.costsManager" = concat( + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ), + "roles/compute.orgFirewallPolicyAdmin" = [ + module.branch-network-sa.iam_email + ] + "roles/compute.xpnAdmin" = [ + module.branch-network-sa.iam_email + ] + "roles/orgpolicy.policyAdmin" = local.branch_teams_pf_sa_iam_emails + }, + local.billing_org ? { + "roles/billing.user" = concat( + [ + module.branch-network-sa.iam_email, + module.branch-security-sa.iam_email, + ], + # enable if individual teams can create their own projects + # [ + # for k, v in module.branch-teams-team-sa : v.iam_email + # ], + local.branch_gke_sa_iam_emails, + local.branch_teams_pf_sa_iam_emails + ) + } : {} + ) + # sample subset of useful organization policies, edit to suit requirements + policy_boolean = { + "constraints/cloudfunctions.requireVPCConnector" = true + "constraints/compute.disableGuestAttributesAccess" = true + "constraints/compute.disableInternetNetworkEndpointGroup" = true + "constraints/compute.disableNestedVirtualization" = true + "constraints/compute.disableSerialPortAccess" = true + "constraints/compute.requireOsLogin" = true + "constraints/compute.restrictXpnProjectLienRemoval" = true + "constraints/compute.skipDefaultNetworkCreation" = true + "constraints/iam.automaticIamGrantsForDefaultServiceAccounts" = true + "constraints/iam.disableServiceAccountKeyCreation" = true + "constraints/iam.disableServiceAccountKeyUpload" = true + "constraints/sql.restrictPublicIp" = true + "constraints/sql.restrictAuthorizedNetworks" = true + "constraints/storage.uniformBucketLevelAccess" = true + } + policy_list = { + "constraints/cloudfunctions.allowedIngressSettings" = merge( + local.list_deny, { values = ["ALLOW_INTERNAL_ONLY"] } + ) + "constraints/cloudfunctions.allowedVpcConnectorEgressSettings" = merge( + local.list_deny, { values = ["PRIVATE_RANGES_ONLY"] } + ) + "constraints/compute.restrictLoadBalancerCreationForTypes" = merge( + local.list_deny, { values = ["in:INTERNAL"] } + ) + "constraints/compute.vmExternalIpAccess" = local.list_deny + "constraints/iam.allowedPolicyMemberDomains" = { + inherit_from_parent = false + suggested_value = null + status = true + values = concat( + [var.organization.customer_id], + try(local.policy_configs.allowed_policy_member_domains, []) + ) + } + "constraints/run.allowedIngress" = merge( + local.list_deny, { values = ["internal"] } + ) + "constraints/run.allowedVPCEgress" = merge( + local.list_deny, { values = ["private-ranges-only"] } + ) + # "constraints/compute.restrictCloudNATUsage" = local.list_deny + # "constraints/compute.restrictDedicatedInterconnectUsage" = local.list_deny + # "constraints/compute.restrictPartnerInterconnectUsage" = local.list_deny + # "constraints/compute.restrictProtocolForwardingCreationForTypes" = local.list_deny + # "constraints/compute.restrictSharedVpcHostProjects" = local.list_deny + # "constraints/compute.restrictSharedVpcSubnetworks" = local.list_deny + # "constraints/compute.restrictVpcPeering" = local.list_deny + # "constraints/compute.restrictVpnPeerIPs" = local.list_deny + # "constraints/compute.vmCanIpForward" = local.list_deny + # "constraints/gcp.resourceLocations" = { + # inherit_from_parent = false + # suggested_value = null + # status = true + # values = local.allowed_regions + # } + } +} diff --git a/fast/stages/01-resman/outputs.tf b/fast/stages/01-resman/outputs.tf new file mode 100644 index 00000000..7b52a394 --- /dev/null +++ b/fast/stages/01-resman/outputs.tf @@ -0,0 +1,166 @@ +/** + * 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 { + _project_factory_sas = { + dev = module.branch-teams-dev-projectfactory-sa.iam_email + prod = module.branch-teams-prod-projectfactory-sa.iam_email + } + providers = { + "02-networking" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-network-gcs.name + name = "networking" + sa = module.branch-network-sa.email + }) + "02-security" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-security-gcs.name + name = "security" + sa = module.branch-security-sa.email + }) + "99-sandbox" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-sandbox-gcs.name + name = "sandbox" + sa = module.branch-sandbox-sa.email + }) + "03-gke-multitenant-dev" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-gke-dev-gcs.name + name = "gke-dev" + sa = module.branch-gke-dev-sa.email + }) + "03-gke-multitenant-prod" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-gke-prod-gcs.name + name = "gke-prod" + sa = module.branch-gke-prod-sa.email + }) + "03-project-factory-dev" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-teams-dev-projectfactory-gcs.name + name = "team-dev" + sa = module.branch-teams-dev-projectfactory-sa.email + }) + "03-project-factory-prod" = templatefile("${path.module}/../../assets/templates/providers.tpl", { + bucket = module.branch-teams-prod-projectfactory-gcs.name + name = "team-prod" + sa = module.branch-teams-prod-projectfactory-sa.email + }) + } + tfvars = { + "02-networking" = jsonencode({ + folder_id = module.branch-network-folder.id + project_factory_sa = local._project_factory_sas + }) + "02-security" = jsonencode({ + folder_id = module.branch-security-folder.id + kms_restricted_admins = { + for k, v in local._project_factory_sas : k => [v] + } + }) + "03-gke-multitenant-dev" = jsonencode({ + folder_id = module.branch-gke-dev-folder.id + }) + "03-gke-multitenant-prod" = jsonencode({ + folder_id = module.branch-gke-prod-folder.id + }) + } +} + +# optionally generate providers and tfvars files for subsequent stages + +resource "local_file" "providers" { + for_each = var.outputs_location == null ? {} : local.providers + filename = "${var.outputs_location}/${each.key}/providers.tf" + content = each.value +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : local.tfvars + filename = "${var.outputs_location}/${each.key}/terraform-resman.auto.tfvars.json" + content = each.value +} + +# outputs + +output "networking" { + # tfdoc:output:consumers 02-networking + description = "Data for the networking stage." + value = { + folder = module.branch-network-folder.id + gcs_bucket = module.branch-network-gcs.name + service_account = module.branch-network-sa.iam_email + } +} + +output "project_factories" { + # tfdoc:output:consumers xx-teams + description = "Data for the project factories stage." + value = { + dev = { + bucket = module.branch-teams-dev-projectfactory-gcs.name + sa = module.branch-teams-dev-projectfactory-sa.email + } + prod = { + bucket = module.branch-teams-prod-projectfactory-gcs.name + sa = module.branch-teams-prod-projectfactory-sa.email + } + } +} + +# ready to use provider configurations for subsequent stages + +output "providers" { + # tfdoc:output:consumers 02-networking 02-security xx-sandbox xx-teams + description = "Terraform provider files for this stage and dependent stages." + sensitive = true + value = local.providers +} + +output "sandbox" { + # tfdoc:output:consumers xx-sandbox + description = "Data for the sandbox stage." + value = { + folder = module.branch-sandbox-folder.id + gcs_bucket = module.branch-sandbox-gcs.name + service_account = module.branch-sandbox-sa.email + } +} + +output "security" { + # tfdoc:output:consumers 02-security + description = "Data for the networking stage." + value = { + folder = module.branch-security-folder.id + gcs_bucket = module.branch-security-gcs.name + service_account = module.branch-security-sa.iam_email + } +} + +output "teams" { + description = "Data for the teams stage." + value = { + for k, v in module.branch-teams-team-folder : k => { + folder = v.id + gcs_bucket = module.branch-teams-team-gcs[k].name + service_account = module.branch-teams-team-sa[k].email + } + } +} + +# ready to use variable values for subsequent stages + +output "tfvars" { + description = "Terraform variable files for the following stages." + sensitive = true + value = local.tfvars +} diff --git a/fast/stages/01-resman/providers.tf b/fast/stages/01-resman/providers.tf new file mode 120000 index 00000000..49577549 --- /dev/null +++ b/fast/stages/01-resman/providers.tf @@ -0,0 +1 @@ +../../configs/jccb/01-resman/providers.tf \ No newline at end of file diff --git a/fast/stages/01-resman/variables.tf b/fast/stages/01-resman/variables.tf new file mode 100644 index 00000000..c1d63c86 --- /dev/null +++ b/fast/stages/01-resman/variables.tf @@ -0,0 +1,104 @@ +/** + * 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. + */ + +# defaults for variables marked with global tfdoc annotations, can be set via +# the tfvars file generated in stage 00 and stored in its outputs + +variable "billing_account" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id and organization id ('nnnnnnnn' or null)." + type = object({ + id = string + organization_id = number + }) +} + +variable "automation_project_id" { + # tfdoc:variable:source 00-bootstrap + description = "Project id for the automation project created by the bootstrap stage." + type = string +} + +variable "custom_roles" { + # tfdoc:variable:source 00-bootstrap + description = "Custom roles defined at the org level, in key => id format." + type = map(string) + default = {} +} + +variable "groups" { + # tfdoc:variable:source 00-bootstrap + description = "Group names to grant organization-level permissions." + type = map(string) + # https://cloud.google.com/docs/enterprise/setup-checklist + default = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins" + gcp-organization-admins = "gcp-organization-admins" + gcp-security-admins = "gcp-security-admins" + gcp-support = "gcp-support" + } +} + +variable "organization" { + # tfdoc:variable:source 00-bootstrap + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "organization_policy_configs" { + description = "Organization policies customization." + type = object({ + allowed_policy_member_domains = list(string) + }) + default = null +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + # tfdoc:variable:source 00-bootstrap + description = "Prefix used for resources that need unique names." + type = string +} + +variable "team_folders" { + description = "Team folders to be created. Format is described in a code comment." + type = map(object({ + descriptive_name = string + group_iam = map(list(string)) + impersonation_groups = list(string) + })) + default = null + # default = { + # team-a = { + # descriptive_name = "Team A" + # group_iam = { + # team-a-group = [roles/owner, roles/projectCreator] + # } + # impersonation_groups = ["team-a-admins@example.com"] + # } + # } +} diff --git a/tests/fast/stages/__init__.py b/tests/fast/stages/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/__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/fast/stages/s00_bootstrap/__init__.py b/tests/fast/stages/s00_bootstrap/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/__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/fast/stages/s00_bootstrap/fixture/main.tf b/tests/fast/stages/s00_bootstrap/fixture/main.tf new file mode 100644 index 00000000..d6130655 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/fixture/main.tf @@ -0,0 +1,29 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/00-bootstrap" + prefix = "fast" + organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" + } + billing_account = { + id = "000000-111111-222222" + organization_id = 123456789012 + } +} diff --git a/tests/fast/stages/s00_bootstrap/test_plan.py b/tests/fast/stages/s00_bootstrap/test_plan.py new file mode 100644 index 00000000..1af5d507 --- /dev/null +++ b/tests/fast/stages/s00_bootstrap/test_plan.py @@ -0,0 +1,33 @@ +# 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. + +# _RESOURCE_COUNT = { +# 'module.organization': 28, +# 'module.automation-project': 23, +# 'module.automation-tf-bootstrap-gcs': 1, +# 'module.automation-tf-bootstrap-sa': 1, +# 'module.automation-tf-resman-gcs': 2, +# 'module.automation-tf-resman-sa': 1, +# 'module.billing-export-dataset': 1, +# 'module.billing-export-project': 7, +# 'module.log-export-dataset': 1, +# 'module.log-export-project': 7, +# } + + +def test_counts(e2e_plan_runner): + "Test stage." + # TODO: to re-enable per-module resource count check print _, then test + num_modules, num_resources, _ = e2e_plan_runner() + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s01_resman/__init__.py b/tests/fast/stages/s01_resman/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s01_resman/__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/fast/stages/s01_resman/fixture/main.tf b/tests/fast/stages/s01_resman/fixture/main.tf new file mode 100644 index 00000000..06ab534c --- /dev/null +++ b/tests/fast/stages/s01_resman/fixture/main.tf @@ -0,0 +1,42 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/01-resman" + automation_project_id = "fast-prod-automation" + billing_account = { + id = "000000-111111-222222" + organization_id = 123456789012 + } + custom_roles = { + "organizationIamAdmin" : "organizations/123456789012/roles/organizationIamAdmin", + "xpnServiceAdmin" : "organizations/123456789012/roles/xpnServiceAdmin" + } + groups = { + gcp-billing-admins = "gcp-billing-admins", + gcp-devops = "gcp-devops", + gcp-network-admins = "gcp-network-admins", + gcp-organization-admins = "gcp-organization-admins", + gcp-security-admins = "gcp-security-admins", + gcp-support = "gcp-support" + } + organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" + } + prefix = "fast2" +} diff --git a/tests/fast/stages/s01_resman/test_plan.py b/tests/fast/stages/s01_resman/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s01_resman/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_networking/__init__.py b/tests/fast/stages/s02_networking/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s02_networking/__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/fast/stages/s02_networking/fixture/data b/tests/fast/stages/s02_networking/fixture/data new file mode 120000 index 00000000..423e31f5 --- /dev/null +++ b/tests/fast/stages/s02_networking/fixture/data @@ -0,0 +1 @@ +../../../../stages/02-networking/data \ No newline at end of file diff --git a/tests/fast/stages/s02_networking/fixture/main.tf b/tests/fast/stages/s02_networking/fixture/main.tf new file mode 100644 index 00000000..b3fda3d4 --- /dev/null +++ b/tests/fast/stages/s02_networking/fixture/main.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/02-networking" + billing_account_id = "000000-111111-222222" + organization = { + domain = "gcp-pso-italy.net" + id = 856933387836 + customer_id = "C01lmug8b" + } + prefix = "fast" + project_factory_sa = { + dev = "foo@iam" + prod = "bar@iam" + } +} diff --git a/tests/fast/stages/s02_networking/test_plan.py b/tests/fast/stages/s02_networking/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s02_networking/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_security/__init__.py b/tests/fast/stages/s02_security/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s02_security/__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/fast/stages/s02_security/fixture/data b/tests/fast/stages/s02_security/fixture/data new file mode 120000 index 00000000..423e31f5 --- /dev/null +++ b/tests/fast/stages/s02_security/fixture/data @@ -0,0 +1 @@ +../../../../stages/02-networking/data \ No newline at end of file diff --git a/tests/fast/stages/s02_security/fixture/main.tf b/tests/fast/stages/s02_security/fixture/main.tf new file mode 100644 index 00000000..39e1de82 --- /dev/null +++ b/tests/fast/stages/s02_security/fixture/main.tf @@ -0,0 +1,109 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "stage" { + source = "../../../../stages/02-security" + billing_account_id = "000000-111111-222222" + folder_id = "folders/12345678" + organization = { + domain = "gcp-pso-italy.net" + id = 856933387836 + customer_id = "C01lmug8b" + } + prefix = "fast" + kms_restricted_admins = { + "dev" : [ + "serviceAccount:fast-dev-resman-pf-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ], + "prod" : [ + "serviceAccount:fast-prod-resman-pf-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + kms_keys = { + compute = { + iam = { + "roles/cloudkms.admin" = ["user:user1@example.com"] + } + labels = { service = "compute" } + locations = null + rotation_period = null + } + } + vpc_sc_ingress_policies = { + iac = { + ingress_from = { + identities = [ + "serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ], + source_access_levels = ["*"], identity_type = null, source_resources = null + } + ingress_to = { + operations = [{ method_selectors = [], service_name = "*" }] + resources = ["*"] + } + } + } + vpc_sc_perimeter_ingress_policies = { + dev = ["iac"] + landing = null + prod = ["iac"] + } + vpc_sc_perimeter_projects = { + dev = [ + "projects/345678912", # ludo-dev-sec-core-0 + ] + landing = [] + prod = [ + "projects/234567891", # ludo-prod-sec-core-0 + ] + } + + vpc_sc_access_levels = { + all = { + combining_function = null + conditions = [{ + members = [ + "serviceAccount:quota-monitor@foobar.iam.gserviceaccount.com", + ], + ip_subnetworks = null, negate = null, regions = null, + required_access_levels = null + }] + } + } + + vpc_sc_perimeter_access_levels = { + dev = ["all"] + landing = null + prod = ["all"] + } + + vpc_sc_egress_policies = { + iac-gcs = { + egress_from = { + identity_type = null + identities = [ + "serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com" + ] + } + egress_to = { + operations = [{ + method_selectors = ["*"], service_name = "storage.googleapis.com" + }] + resources = ["projects/123456789"] + } + } + } +} diff --git a/tests/fast/stages/s02_security/test_plan.py b/tests/fast/stages/s02_security/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s02_security/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s03_project_factory/__init__.py b/tests/fast/stages/s03_project_factory/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/__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/fast/stages/s03_project_factory/fixture/data/defaults.yaml b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml new file mode 100644 index 00000000..ab35a310 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml @@ -0,0 +1,22 @@ +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Setup for billing alerts +billing_alert: + amount: 1000 + thresholds: + current: [0.5, 0.8] + forecasted: [0.5, 0.8] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: ["team-contacts@example.com"] + +# [opt] Labels set for all projects +labels: + environment: prod + department: accounting + application: example-app + foo: bar + +# [opt] Additional notification channels for billing +notification_channels: [] diff --git a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml new file mode 100644 index 00000000..a8b92d60 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml @@ -0,0 +1,98 @@ +# [opt] Billing account id - overrides default if set +billing_account_id: 012345-67890A-BCDEF0 + +# [opt] Billing alerts config - overrides default if set +billing_alert: + amount: 10 + thresholds: + current: + - 0.5 + - 0.8 + forecasted: [] + credit_treatment: INCLUDE_ALL_CREDITS + +# [opt] DNS zones to be created as children of the environment_dns_zone defined in defaults +dns_zones: + - lorem + - ipsum + +# [opt] Contacts for billing alerts and important notifications +essential_contacts: + - team-a-contacts@example.com + +# Folder the project will be created as children of +folder_id: folders/012345678901 + +# [opt] Authoritative IAM bindings in group => [roles] format +group_iam: + test-team-foobar@fast-lab-0.gcp-pso-italy.net: + - roles/compute.admin + +# [opt] Authoritative IAM bindings in role => [principals] format +# Generally used to grant roles to service accounts external to the project +iam: + roles/compute.admin: + - serviceAccount:service-account + +# [opt] Service robots and keys they will be assigned as cryptoKeyEncrypterDecrypter +# in service => [keys] format +kms_service_agents: + compute: [key1, key2] + storage: [key1, key2] + +# [opt] Labels for the project - merged with the ones defined in defaults +labels: + environment: prod + +# [opt] Org policy overrides defined at project level +org_policies: + policy_boolean: + constraints/compute.disableGuestAttributesAccess: true + policy_list: + constraints/compute.trustedImageProjects: + inherit_from_parent: null + status: true + suggested_value: null + values: + - projects/fast-prod-iac-core-0 + +# [opt] Service account to create for the project and their roles on the project +# in name => [roles] format +service_accounts: + another-service-account: + - roles/compute.admin + my-service-account: + - roles/compute.admin + +# [opt] APIs to enable on the project. +services: + - storage.googleapis.com + - stackdriver.googleapis.com + - compute.googleapis.com + +# [opt] Roles to assign to the robots service accounts in robot => [roles] format +services_iam: + compute: + - roles/storage.objectViewer + + # [opt] VPC setup. + # If set enables the `compute.googleapis.com` service and configures + # service project attachment +vpc: + # [opt] If set, enables the container API + gke_setup: + # Grants "roles/container.hostServiceAgentUser" to the container robot if set + enable_host_service_agent: false + + # Grants "roles/compute.securityAdmin" to the container robot if set + enable_security_admin: true + + # Host project the project will be service project of + host_project: fast-prod-net-spoke-0 + + # [opt] Subnets in the host project where principals will be granted networkUser + # in region/subnet-name => [principals] + subnets_iam: + europe-west1/prod-default-ew1: + - user:foobar@example.com + - serviceAccount:service-account1 diff --git a/tests/fast/stages/s03_project_factory/fixture/main.tf b/tests/fast/stages/s03_project_factory/fixture/main.tf new file mode 100644 index 00000000..e1c05e51 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Project factory. + + +locals { + _defaults = yamldecode(file(var.defaults_file)) + _defaults_net = { + billing_account_id = var.billing_account_id + environment_dns_zone = var.environment_dns_zone + shared_vpc_self_link = var.shared_vpc_self_link + vpc_host_project = var.vpc_host_project + } + defaults = merge(local._defaults, local._defaults_net) + projects = { + for f in fileset("${var.data_dir}", "**/*.yaml") : + trimsuffix(f, ".yaml") => yamldecode(file("${var.data_dir}/${f}")) + } +} + +module "projects" { + #TODO(sruffilli): Pin to release + source = "github.com/terraform-google-modules/cloud-foundation-fabric/examples/factories/project-factory" + for_each = local.projects + defaults = local.defaults + project_id = each.key + billing_account_id = try(each.value.billing_account_id, null) + billing_alert = try(each.value.billing_alert, null) + dns_zones = try(each.value.dns_zones, []) + essential_contacts = try(each.value.essential_contacts, []) + folder_id = each.value.folder_id + group_iam = try(each.value.group_iam, {}) + iam = try(each.value.iam, {}) + kms_service_agents = try(each.value.kms, {}) + labels = try(each.value.labels, {}) + org_policies = try(each.value.org_policies, null) + service_accounts = try(each.value.service_accounts, {}) + services = try(each.value.services, []) + services_iam = try(each.value.services_iam, {}) + vpc = try(each.value.vpc, null) +} + + diff --git a/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json b/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json new file mode 100644 index 00000000..d446d643 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/terraform-bootstrap.auto.tfvars.json @@ -0,0 +1,4 @@ +{ + "billing_account_id": "012345-67890A-BCDEF0", + "prefix": "fast" +} \ No newline at end of file diff --git a/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json b/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json new file mode 100644 index 00000000..56cfa3de --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/terraform-networking.auto.tfvars.json @@ -0,0 +1,5 @@ +{ + "environment_dns_zone": "prod.gcp.example.com.", + "shared_vpc_self_link": "https://www.googleapis.com/compute/v1/projects/fast-example/global/networks/prod-spoke-0", + "vpc_host_project": "fast-example" +} \ No newline at end of file diff --git a/tests/fast/stages/s03_project_factory/fixture/variables.tf b/tests/fast/stages/s03_project_factory/fixture/variables.tf new file mode 100644 index 00000000..b52ebd6c --- /dev/null +++ b/tests/fast/stages/s03_project_factory/fixture/variables.tf @@ -0,0 +1,61 @@ +/** + * 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. + */ + +#TODO: tfdoc annotations + +variable "billing_account_id" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id." + type = string +} + +variable "data_dir" { + description = "Relative path for the folder storing configuration data." + type = string + default = "data/projects" +} + +variable "environment_dns_zone" { + # tfdoc:variable:source 02-networking + description = "DNS zone suffix for environment." + type = string + default = null +} + +variable "defaults_file" { + description = "Relative path for the file storing the project factory configuration." + type = string + default = "data/defaults.yaml" +} + +#TODO(sruffilli): is this really required? +variable "environment" { + description = "Environment where projects will be created (e.g. prod, dev, ...)." + type = string + default = "prod" +} + +variable "shared_vpc_self_link" { + # tfdoc:variable:source 02-networking + description = "Self link for the shared VPC." + type = string +} + +variable "vpc_host_project" { + # tfdoc:variable:source 02-networking + description = "Host project for the shared VPC." + type = string +} diff --git a/tests/fast/stages/s03_project_factory/test_plan.py b/tests/fast/stages/s03_project_factory/test_plan.py new file mode 100644 index 00000000..f8495b39 --- /dev/null +++ b/tests/fast/stages/s03_project_factory/test_plan.py @@ -0,0 +1,20 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 From de34956f47fa4434245172e237d68e19121aaa4c Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:46:24 +0100 Subject: [PATCH 030/132] Copy FAST top level README --- tests/fast/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/fast/README.md diff --git a/tests/fast/README.md b/tests/fast/README.md new file mode 100644 index 00000000..01ea3e69 --- /dev/null +++ b/tests/fast/README.md @@ -0,0 +1,34 @@ +# Fabric FAST + +Setting up a production-ready GCP organization is often a time-consuming process. Fabric FAST aims to speed up this process via two complementary goals. On the one hand, FAST provides a design of a GCP organization that includes the typical elements required by enterprise customers. Secondly, we provide a reference implementation of the FAST design using Terraform. + +Note that while our implementation is necessarily influenced (and constrained) by the way Terraform works, the design we put forward only refers to GCP constructs and features. In other words, while we use Terraform for our reference implementation, in theory, the FAST design can be implemented using any other tool (e.g., Pulumi, bash scripts, or even calling the relevant APIs directly). + +Fabric FAST comes from engineers in Google Cloud's Professional Services Organization, with a combined experience of decades solving the typical technical problems faced by GCP customers. While every GCP user has specific requirements, many common issues arise repeatedly. Solving those issues correctly from the beginning is key to a robust and scalable GCP setup. It's those common issues and their solutions that Fabric FAST aims to collect and present coherently. + +Fabric FAST was initially conceived to help enterprises quickly set up a GCP organization following battle-tested and widely-used patterns. Despite its origin in enterprise environments, FAST includes many customization points making it an ideal blueprint for organizations of all sizes, ranging from startups to the largest companies. + + +## Guiding principles +### Contracts and stages +FAST uses the concept of stages, which individually perform precise tasks but, taken together, build a functional, ready-to-use GCP organization. More importantly, stages are modeled around the security boundaries that typically appear in mature organizations. This arrangement allows delegating ownership of each stage to the team responsible for the types of resources it manages. For example, as its name suggests, the networking stage sets up all the networking elements and is usually the responsibility of a dedicated networking team within the organization. + +From the perspective of FAST's overall design, stages also work as contacts or interfaces, defining a set of pre-requisites and inputs required to perform their designed task and generating outputs needed by other stages lower in the chain. + +### Security-first design +Security was, from the beginning, one of the most critical elements in the design of Fabric FAST. Many of FAST's design decisions aim to build the foundations of a secure organization. In fact, the first two stages deal mainly with the organization-wide security setup. + +FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. + +### Extensive use of factories +A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. + +FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. + +## High level design + +TBD + +## Implementation + +TBD From 65902c459cfec3b5cc6da7a69de5d891bb18c717 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 17 Jan 2022 10:46:24 +0100 Subject: [PATCH 031/132] Copy FAST top level README --- fast/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 fast/README.md diff --git a/fast/README.md b/fast/README.md new file mode 100644 index 00000000..01ea3e69 --- /dev/null +++ b/fast/README.md @@ -0,0 +1,34 @@ +# Fabric FAST + +Setting up a production-ready GCP organization is often a time-consuming process. Fabric FAST aims to speed up this process via two complementary goals. On the one hand, FAST provides a design of a GCP organization that includes the typical elements required by enterprise customers. Secondly, we provide a reference implementation of the FAST design using Terraform. + +Note that while our implementation is necessarily influenced (and constrained) by the way Terraform works, the design we put forward only refers to GCP constructs and features. In other words, while we use Terraform for our reference implementation, in theory, the FAST design can be implemented using any other tool (e.g., Pulumi, bash scripts, or even calling the relevant APIs directly). + +Fabric FAST comes from engineers in Google Cloud's Professional Services Organization, with a combined experience of decades solving the typical technical problems faced by GCP customers. While every GCP user has specific requirements, many common issues arise repeatedly. Solving those issues correctly from the beginning is key to a robust and scalable GCP setup. It's those common issues and their solutions that Fabric FAST aims to collect and present coherently. + +Fabric FAST was initially conceived to help enterprises quickly set up a GCP organization following battle-tested and widely-used patterns. Despite its origin in enterprise environments, FAST includes many customization points making it an ideal blueprint for organizations of all sizes, ranging from startups to the largest companies. + + +## Guiding principles +### Contracts and stages +FAST uses the concept of stages, which individually perform precise tasks but, taken together, build a functional, ready-to-use GCP organization. More importantly, stages are modeled around the security boundaries that typically appear in mature organizations. This arrangement allows delegating ownership of each stage to the team responsible for the types of resources it manages. For example, as its name suggests, the networking stage sets up all the networking elements and is usually the responsibility of a dedicated networking team within the organization. + +From the perspective of FAST's overall design, stages also work as contacts or interfaces, defining a set of pre-requisites and inputs required to perform their designed task and generating outputs needed by other stages lower in the chain. + +### Security-first design +Security was, from the beginning, one of the most critical elements in the design of Fabric FAST. Many of FAST's design decisions aim to build the foundations of a secure organization. In fact, the first two stages deal mainly with the organization-wide security setup. + +FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. + +### Extensive use of factories +A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. + +FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. + +## High level design + +TBD + +## Implementation + +TBD From f4ca029314237b6f91125bce6863cba044382de9 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:50:34 +0100 Subject: [PATCH 032/132] TODO list --- fast/TODO.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 fast/TODO.txt diff --git a/fast/TODO.txt b/fast/TODO.txt new file mode 100644 index 00000000..439af117 --- /dev/null +++ b/fast/TODO.txt @@ -0,0 +1,10 @@ +TODO before merging + +- [ ] fix tests +- [ ] fix linting errors +- [ ] fast-specific .gitignore +- [ ] YAML samples thingy +- [ ] stages README +- [ ] proper docstring on new tools +- [ ] modify github actions for different fast tfdoc usage +- [ ] add roadmap to top-level fast README From c082ba4b39586e1f280df1897d54834c8a31e8f6 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:51:22 +0100 Subject: [PATCH 033/132] TODO list --- fast/TODO.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/fast/TODO.txt b/fast/TODO.txt index 439af117..aecf14c5 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -8,3 +8,4 @@ TODO before merging - [ ] proper docstring on new tools - [ ] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README +- [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) From 2838b30cc877056af54398f4bae8ad711540483d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 10:59:39 +0100 Subject: [PATCH 034/132] fix linting action to account for fast --- .github/workflows/linting.yml | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index e3919451..2e27c6dc 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -51,23 +51,22 @@ jobs: run: | terraform fmt -recursive -check -diff $GITHUB_WORKSPACE - - name: Check documentation - id: documentation + - name: Check documentation (fabric) + id: documentation-fabric run: | - python3 tools/check_documentation.py \ - cloud-operations \ - data-solutions \ - factories \ - foundations \ - modules \ - networking + python3 tools/check_documentation.py examples modules + + - name: Check documentation (fast) + id: documentation-fast + run: | + python3 tools/check_documentation.py --files --show-extra fast markdown-link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: 'yes' - use-verbose-mode: 'yes' - config-file: '.github/workflows/markdown-link-check.json' + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: "yes" + use-verbose-mode: "yes" + config-file: ".github/workflows/markdown-link-check.json" From 61e8148a2eae8e8883f4de0d571f483be3cfad12 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:02:24 +0100 Subject: [PATCH 035/132] remove providers file --- fast/stages/01-resman/providers.tf | 1 - 1 file changed, 1 deletion(-) delete mode 120000 fast/stages/01-resman/providers.tf diff --git a/fast/stages/01-resman/providers.tf b/fast/stages/01-resman/providers.tf deleted file mode 120000 index 49577549..00000000 --- a/fast/stages/01-resman/providers.tf +++ /dev/null @@ -1 +0,0 @@ -../../configs/jccb/01-resman/providers.tf \ No newline at end of file From 85a401a18f3d4a8d617a1d0c63742076ec2be43e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:05:42 +0100 Subject: [PATCH 036/132] add missing boilerplate --- fast/assets/schemas/firewall_rules.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/hierarchical_rules.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/project.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/project_defaults.schema.yaml | 14 ++++++++++++++ fast/assets/schemas/subnet.schema.yaml | 14 ++++++++++++++ .../s03_project_factory/fixture/data/defaults.yaml | 14 ++++++++++++++ .../fixture/data/projects/project.yaml | 14 ++++++++++++++ 7 files changed, 98 insertions(+) diff --git a/fast/assets/schemas/firewall_rules.schema.yaml b/fast/assets/schemas/firewall_rules.schema.yaml index ebb62772..1fd96caf 100644 --- a/fast/assets/schemas/firewall_rules.schema.yaml +++ b/fast/assets/schemas/firewall_rules.schema.yaml @@ -1,3 +1,17 @@ +# 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. + map(include('firewall_rule')) --- firewall_rule: diff --git a/fast/assets/schemas/hierarchical_rules.schema.yaml b/fast/assets/schemas/hierarchical_rules.schema.yaml index d8c72b1d..0e0f7b66 100644 --- a/fast/assets/schemas/hierarchical_rules.schema.yaml +++ b/fast/assets/schemas/hierarchical_rules.schema.yaml @@ -1,3 +1,17 @@ +# 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. + map(include('hierarchical_rule')) --- hierarchical_rule: diff --git a/fast/assets/schemas/project.schema.yaml b/fast/assets/schemas/project.schema.yaml index e914e5a7..f7f89730 100644 --- a/fast/assets/schemas/project.schema.yaml +++ b/fast/assets/schemas/project.schema.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) billing_alert: any(include('billing_alert'), null(), required=False) # If set to null, use defaults dns_zones: list(str(), required=False) diff --git a/fast/assets/schemas/project_defaults.schema.yaml b/fast/assets/schemas/project_defaults.schema.yaml index 52676baa..113fe26b 100644 --- a/fast/assets/schemas/project_defaults.schema.yaml +++ b/fast/assets/schemas/project_defaults.schema.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: str(matches='[A-F0-9]{6}-[A-F0-9]{6}-[A-F0-9]{6}', required=False) billing_alert: any(include('billing_alert'), null(), required=False) essential_contacts: list(str(), required=False) diff --git a/fast/assets/schemas/subnet.schema.yaml b/fast/assets/schemas/subnet.schema.yaml index 1bf10ee8..add0d74b 100644 --- a/fast/assets/schemas/subnet.schema.yaml +++ b/fast/assets/schemas/subnet.schema.yaml @@ -1,3 +1,17 @@ +# 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. + region: str() description: str() ip_cidr_range: str() diff --git a/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml index ab35a310..b050583f 100644 --- a/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml +++ b/tests/fast/stages/s03_project_factory/fixture/data/defaults.yaml @@ -1,3 +1,17 @@ +# 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. + billing_account_id: 012345-67890A-BCDEF0 # [opt] Setup for billing alerts diff --git a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml index a8b92d60..d988d9d5 100644 --- a/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml +++ b/tests/fast/stages/s03_project_factory/fixture/data/projects/project.yaml @@ -1,3 +1,17 @@ +# 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. + # [opt] Billing account id - overrides default if set billing_account_id: 012345-67890A-BCDEF0 From 0af1133a81492ccaf8f456e5b4c91c3640ff163d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:08:26 +0100 Subject: [PATCH 037/132] update factory README --- .../factories/net-vpc-firewall-yaml/README.md | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/factories/net-vpc-firewall-yaml/README.md b/examples/factories/net-vpc-firewall-yaml/README.md index 064ee30c..89af153e 100644 --- a/examples/factories/net-vpc-firewall-yaml/README.md +++ b/examples/factories/net-vpc-firewall-yaml/README.md @@ -136,25 +136,27 @@ web-app-a-ingress: ``` + ## Variables -| name | description | type | required | default | -| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------: | :------: | :---------------: | -| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | -| network | Name of the network this set of firewall rules applies to. | string | ✓ | | -| project_id | Project Id. | string | ✓ | | -| log_config | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({…}) | | null | +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| config_directories | List of paths to folders where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | list(string) | ✓ | | +| network | Name of the network this set of firewall rules applies to. | string | ✓ | | +| project_id | Project Id. | string | ✓ | | +| log_config | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({…}) | | null | ## Outputs -| name | description | sensitive | -| ------------------- | -------------------------------- | :-------: | -| egress_allow_rules | Egress rules with allow blocks. | | -| egress_deny_rules | Egress rules with allow blocks. | | -| ingress_allow_rules | Ingress rules with allow blocks. | | -| ingress_deny_rules | Ingress rules with deny blocks. | | +| name | description | sensitive | +|---|---|:---:| +| egress_allow_rules | Egress rules with allow blocks. | | +| egress_deny_rules | Egress rules with allow blocks. | | +| ingress_allow_rules | Ingress rules with allow blocks. | | +| ingress_deny_rules | Ingress rules with deny blocks. | | + From 4f82723a7131d9784217c2d2178fd750788d39d4 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:10:47 +0100 Subject: [PATCH 038/132] align examples tfdoc --- .../gcs-to-bq-with-dataflow/README.md | 4 ++- .../decentralized-firewall/README.md | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/data-solutions/gcs-to-bq-with-dataflow/README.md b/examples/data-solutions/gcs-to-bq-with-dataflow/README.md index aa6a6eb7..055f93ef 100644 --- a/examples/data-solutions/gcs-to-bq-with-dataflow/README.md +++ b/examples/data-solutions/gcs-to-bq-with-dataflow/README.md @@ -113,14 +113,15 @@ You can check data imported into Google BigQuery from the Google Cloud Console U + ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| prefix | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | ✓ | | | project_id | Project id, references existing project if `project_create` is null. | string | ✓ | | +| prefix | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | | null | | project_create | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format | object({…}) | | null | | region | The region where resources will be deployed. | string | | "europe-west1" | | vpc_subnet_range | Ip range used for the VPC subnet created for the example. | string | | "10.0.0.0/20" | @@ -139,3 +140,4 @@ You can check data imported into Google BigQuery from the Google Cloud Console U + diff --git a/examples/networking/decentralized-firewall/README.md b/examples/networking/decentralized-firewall/README.md index 8bf40135..96c5ac2f 100644 --- a/examples/networking/decentralized-firewall/README.md +++ b/examples/networking/decentralized-firewall/README.md @@ -21,26 +21,28 @@ the two). There is an example of a YAML-based validator using [Yamale](https://g in the [`validator/`](validator/) subdirectory, which can be integrated as part of a CI/CD pipeline. + ## Variables -| name | description | type | required | default | -| ------------------ | --------------------------------------------------------------------------------------------- | :-------------------------------: | :------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | -| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | -| region | Region used. | string | | "europe-west1" | +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| ip_ranges | Subnet IP CIDR ranges. | map(string) | | {…} | +| project_services | Service APIs enabled by default in new projects. | list(string) | | […] | +| region | Region used. | string | | "europe-west1" | ## Outputs -| name | description | sensitive | -| -------- | --------------- | :-------: | -| fw_rules | Firewall rules. | | -| projects | Project ids. | | -| vpc | Shared VPCs. | | +| name | description | sensitive | +|---|---|:---:| +| fw_rules | Firewall rules. | | +| projects | Project ids. | | +| vpc | Shared VPCs. | | + From 6259f7592a7e4978b9236b3dec4cb54049ed921c Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:11:52 +0100 Subject: [PATCH 039/132] fast readmes tfdoc --- fast/stages/02-networking/README.md | 86 ++++++++++--------- fast/stages/03-project-factory/prod/README.md | 34 ++++---- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md index 4df304a8..b4d82351 100644 --- a/fast/stages/02-networking/README.md +++ b/fast/stages/02-networking/README.md @@ -279,58 +279,59 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res + ## Files -| name | description | modules | resources | -| ---------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | -| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | -| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | -| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | -| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | -| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | -| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | -| [variables.tf](./variables.tf) | Module variables. | | | -| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | -| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | -| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | -| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | +| name | description | modules | resources | +|---|---|---|---| +| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | +| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | +| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | +| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | +| [outputs.tf](./outputs.tf) | Module outputs. | | local_file | +| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpc-landing.tf](./vpc-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-dev.tf](./vpc-spoke-dev.tf) | Dev spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpc-spoke-prod.tf](./vpc-spoke-prod.tf) | Production spoke VPC and related resources. | net-address · net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | +| [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | +| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | ## Variables -| name | description | type | required | default | producer | -| ------------------ | -------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------: | -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | -| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | -| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | -| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | -| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | -| gke | | map(object({…})) | | {} | 01-resman | -| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | -| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | -| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | -| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | -| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | -| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | +| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | +| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | +| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | +| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | +| gke | | map(object({…})) | | {} | 01-resman | +| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | +| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | +| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | +| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | +| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | +| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | ## Outputs -| name | description | sensitive | consumers | -| ------------------------ | ----------------------------------------------- | :-------: | --------- | -| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | -| project_ids | Network project ids. | | | -| project_numbers | Network project numbers. | | | -| shared_vpc_host_projects | Shared VPC host projects. | | | -| shared_vpc_self_links | Shared VPC host projects. | | | -| tfvars | Network-related variables used in other stages. | ✓ | | -| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | +| name | description | sensitive | consumers | +|---|---|:---:|---| +| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | +| project_ids | Network project ids. | | | +| project_numbers | Network project numbers. | | | +| shared_vpc_host_projects | Shared VPC host projects. | | | +| shared_vpc_self_links | Shared VPC host projects. | | | +| tfvars | Network-related variables used in other stages. | ✓ | | +| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | @@ -351,3 +352,4 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res + diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md index 3cc9ac7e..97cd20cc 100644 --- a/fast/stages/03-project-factory/prod/README.md +++ b/fast/stages/03-project-factory/prod/README.md @@ -97,34 +97,36 @@ terraform apply + ## Files -| name | description | modules | resources | -| ------------------------------ | ----------------- | ---------------------------- | --------- | -| [main.tf](./main.tf) | Project factory. | project-factory | | -| [outputs.tf](./outputs.tf) | Module outputs. | | | -| [variables.tf](./variables.tf) | Module variables. | | | +| name | description | modules | resources | +|---|---|---|---| +| [main.tf](./main.tf) | Project factory. | project-factory | | +| [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables.tf](./variables.tf) | Module variables. | | | ## Variables -| name | description | type | required | default | producer | -| -------------------- | --------------------------------------------------------------------- | :-----------------: | :------: | :-------------------------------------------: | :------------------------: | -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | -| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | -| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | -| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | -| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | +| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | +| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | +| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | +| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | +| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | ## Outputs -| name | description | sensitive | consumers | -| -------- | -------------------------------------- | :-------: | --------- | -| projects | Created projects and service accounts. | | | +| name | description | sensitive | consumers | +|---|---|:---:|---| +| projects | Created projects and service accounts. | | | + From 644151b574dfe2773feecb90ff53f4d3708b0279 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:19:14 +0100 Subject: [PATCH 040/132] disable markdown link check --- .../{markdown-link-check.json => markdown-link-check.json_} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{markdown-link-check.json => markdown-link-check.json_} (100%) diff --git a/.github/workflows/markdown-link-check.json b/.github/workflows/markdown-link-check.json_ similarity index 100% rename from .github/workflows/markdown-link-check.json rename to .github/workflows/markdown-link-check.json_ From 618d0278a8c231736c5641e2dd235a54b4abbf3e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:19:54 +0100 Subject: [PATCH 041/132] really disable markdown link check --- .github/workflows/linting.yml | 18 +++++++++--------- ...nk-check.json_ => markdown-link-check.json} | 0 2 files changed, 9 insertions(+), 9 deletions(-) rename .github/workflows/{markdown-link-check.json_ => markdown-link-check.json} (100%) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 2e27c6dc..be0d9b84 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -61,12 +61,12 @@ jobs: run: | python3 tools/check_documentation.py --files --show-extra fast - markdown-link-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: "yes" - use-verbose-mode: "yes" - config-file: ".github/workflows/markdown-link-check.json" + # markdown-link-check: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@master + # - uses: gaurav-nelson/github-action-markdown-link-check@v1 + # with: + # use-quiet-mode: "yes" + # use-verbose-mode: "yes" + # config-file: ".github/workflows/markdown-link-check.json" diff --git a/.github/workflows/markdown-link-check.json_ b/.github/workflows/markdown-link-check.json similarity index 100% rename from .github/workflows/markdown-link-check.json_ rename to .github/workflows/markdown-link-check.json From 941d862cfa456f0860bf10cfedb42d539be914a4 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:22:08 +0100 Subject: [PATCH 042/132] update TODO --- fast/TODO.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fast/TODO.txt b/fast/TODO.txt index aecf14c5..f94d2e64 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -1,11 +1,11 @@ TODO before merging - [ ] fix tests -- [ ] fix linting errors +- [x] fix linting errors - [ ] fast-specific .gitignore - [ ] YAML samples thingy - [ ] stages README - [ ] proper docstring on new tools -- [ ] modify github actions for different fast tfdoc usage +- [x] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README - [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) From bc50e9368af250da1088119765aa42ca854ed968 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:25:53 +0100 Subject: [PATCH 043/132] switch to local module refs in stage0 --- fast/stages/00-bootstrap/automation.tf | 10 +++++----- fast/stages/00-bootstrap/billing.tf | 6 +++--- fast/stages/00-bootstrap/log-export.tf | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fast/stages/00-bootstrap/automation.tf b/fast/stages/00-bootstrap/automation.tf index 8498df00..f55cfdc5 100644 --- a/fast/stages/00-bootstrap/automation.tf +++ b/fast/stages/00-bootstrap/automation.tf @@ -17,7 +17,7 @@ # tfdoc:file:description Automation project and resources. module "automation-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" billing_account = var.billing_account.id name = "iac-core-0" parent = "organizations/${var.organization.id}" @@ -68,7 +68,7 @@ module "automation-project" { # this stage's bucket and service account module "automation-tf-bootstrap-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" project_id = module.automation-project.project_id name = "iac-core-bootstrap-0" prefix = local.prefix @@ -77,7 +77,7 @@ module "automation-tf-bootstrap-gcs" { } module "automation-tf-bootstrap-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "bootstrap-0" description = "Terraform organization bootstrap service account." @@ -87,7 +87,7 @@ module "automation-tf-bootstrap-sa" { # resource hierarchy stage's bucket and service account module "automation-tf-resman-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" project_id = module.automation-project.project_id name = "iac-core-resman-0" prefix = local.prefix @@ -99,7 +99,7 @@ module "automation-tf-resman-gcs" { } module "automation-tf-resman-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" + source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "resman-0" description = "Terraform organization bootstrap service account." diff --git a/fast/stages/00-bootstrap/billing.tf b/fast/stages/00-bootstrap/billing.tf index 7d5bcf09..0b161011 100644 --- a/fast/stages/00-bootstrap/billing.tf +++ b/fast/stages/00-bootstrap/billing.tf @@ -33,7 +33,7 @@ moved { } module "billing-export-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" count = local.billing_org ? 1 : 0 billing_account = var.billing_account.id name = "billing-export-0" @@ -58,7 +58,7 @@ moved { } module "billing-export-dataset" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + source = "../../../modules/bigquery-dataset" count = local.billing_org ? 1 : 0 project_id = module.billing-export-project.0.project_id id = "billing_export" @@ -68,7 +68,7 @@ module "billing-export-dataset" { # billing account in a different org module "billing-organization-ext" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/organization?ref=v12.0.0" + source = "../../../modules/organization" count = local.billing_org_ext ? 1 : 0 organization_id = "organizations/${var.billing_account.organization_id}" iam_additive = { diff --git a/fast/stages/00-bootstrap/log-export.tf b/fast/stages/00-bootstrap/log-export.tf index f6fb51a5..682d473d 100644 --- a/fast/stages/00-bootstrap/log-export.tf +++ b/fast/stages/00-bootstrap/log-export.tf @@ -21,7 +21,7 @@ locals { } module "log-export-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "audit-logs-0" parent = "organizations/${var.organization.id}" prefix = local.prefix @@ -42,7 +42,7 @@ module "log-export-project" { # one log export per type, with conditionals to skip those not needed module "log-export-dataset" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/bigquery-dataset?ref=v12.0.0" + source = "../../../modules/bigquery-dataset" count = contains(local.log_types, "bigquery") ? 1 : 0 project_id = module.log-export-project.project_id id = "audit_export" @@ -50,7 +50,7 @@ module "log-export-dataset" { } module "log-export-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" + source = "../../../modules/gcs" count = contains(local.log_types, "storage") ? 1 : 0 project_id = module.log-export-project.project_id name = "audit-logs-0" @@ -58,7 +58,7 @@ module "log-export-gcs" { } module "log-export-logbucket" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/logging-bucket?ref=v12.0.0" + source = "../../../modules/logging-bucket" count = contains(local.log_types, "logging") ? 1 : 0 parent_type = "project" parent = module.log-export-project.project_id @@ -66,7 +66,7 @@ module "log-export-logbucket" { } module "log-export-pubsub" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/pubsub?ref=v12.0.0" + source = "../../../modules/pubsub" for_each = toset([for k, v in var.log_sinks : k if v == "pubsub"]) project_id = module.log-export-project.project_id name = "audit-logs-${each.key}" From c7a884f65851ccf8d070b1b7fb260b97d26a33a1 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 17 Jan 2022 11:29:01 +0100 Subject: [PATCH 044/132] replace module refs in 02-sec --- fast/TODO.txt | 7 ++++++- fast/stages/02-security/core-dev.tf | 4 ++-- fast/stages/02-security/core-prod.tf | 4 ++-- fast/stages/02-security/vpc-sc.tf | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fast/TODO.txt b/fast/TODO.txt index f94d2e64..3c99a403 100644 --- a/fast/TODO.txt +++ b/fast/TODO.txt @@ -8,4 +8,9 @@ TODO before merging - [ ] proper docstring on new tools - [x] modify github actions for different fast tfdoc usage - [ ] add roadmap to top-level fast README -- [ ] update modules references to local paths (ludo:0+2sec, julio:1, simo:2net+3) +- [ ] update modules references to local paths + - [x] stage 00 (ludo) + - [ ] stage 01 (julio) + - [ ] stage 02-net (simo) + - [x] stage 02-sec (ludo) + - [ ] stage 03-pf (simo) diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf index b0c78485..4862ef52 100644 --- a/fast/stages/02-security/core-dev.tf +++ b/fast/stages/02-security/core-dev.tf @@ -15,7 +15,7 @@ */ module "dev-sec-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "dev-sec-core-0" parent = var.folder_id prefix = var.prefix @@ -29,7 +29,7 @@ module "dev-sec-project" { module "dev-sec-kms" { for_each = toset(local.kms_locations) - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + source = "../../../modules/kms" project_id = module.dev-sec-project.project_id keyring = { location = each.key diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf index 0524788d..f259a488 100644 --- a/fast/stages/02-security/core-prod.tf +++ b/fast/stages/02-security/core-prod.tf @@ -15,7 +15,7 @@ */ module "prod-sec-project" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/project?ref=v12.0.0" + source = "../../../modules/project" name = "prod-sec-core-0" parent = var.folder_id prefix = var.prefix @@ -29,7 +29,7 @@ module "prod-sec-project" { module "prod-sec-kms" { for_each = toset(local.kms_locations) - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/kms?ref=v12.0.0" + source = "../../../modules/kms" project_id = module.prod-sec-project.project_id keyring = { location = each.key diff --git a/fast/stages/02-security/vpc-sc.tf b/fast/stages/02-security/vpc-sc.tf index f22added..855dc1dd 100644 --- a/fast/stages/02-security/vpc-sc.tf +++ b/fast/stages/02-security/vpc-sc.tf @@ -44,7 +44,7 @@ locals { } module "vpc-sc" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/vpc-sc?ref=ea17e65" + source = "../../../modules/vpc-sc" # only enable if we have projects defined for perimeters count = anytrue([for k, v in local.vpc_sc_counts : v > 0]) ? 1 : 0 access_policy = null From e4d1db7def3f300c5fb93e167b6dc2db18425872 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 18 Jan 2022 08:58:14 +0100 Subject: [PATCH 045/132] Move first draft to fast branch --- .../dp-foundation/01-lnd-datastorage.tf | 78 +++++++++ .../dp-foundation/01-lnd-main.tf.tf | 92 ++++++++++ .../dp-foundation/02-lod-datastorage.tf | 42 +++++ .../dp-foundation/02-lod-main.tf | 75 ++++++++ .../dp-foundation/02-lod-vpc.tf | 49 ++++++ .../dp-foundation/03-orc-composer.tf | 129 ++++++++++++++ .../dp-foundation/03-orc-datastorage.tf | 27 +++ .../dp-foundation/03-orc-main.tf | 90 ++++++++++ .../dp-foundation/03-orc-vpc.tf | 53 ++++++ .../dp-foundation/04-trf-datastorage.tf | 61 +++++++ .../dp-foundation/04-trf-main.tf | 72 ++++++++ .../dp-foundation/04-trf-vpc.tf | 49 ++++++ .../dp-foundation/05-dtl-datastorage.tf | 97 +++++++++++ .../dp-foundation/05-dtl-main.tf | 160 ++++++++++++++++++ .../dp-foundation/99-kms-main.tf | 77 +++++++++ .../data-solutions/dp-foundation/README.md | 15 ++ .../dp-foundation/backend.tf.sample | 30 ++++ examples/data-solutions/dp-foundation/main.tf | 18 ++ .../data-solutions/dp-foundation/variables.tf | 132 +++++++++++++++ 19 files changed, 1346 insertions(+) create mode 100644 examples/data-solutions/dp-foundation/01-lnd-datastorage.tf create mode 100644 examples/data-solutions/dp-foundation/01-lnd-main.tf.tf create mode 100644 examples/data-solutions/dp-foundation/02-lod-datastorage.tf create mode 100644 examples/data-solutions/dp-foundation/02-lod-main.tf create mode 100644 examples/data-solutions/dp-foundation/02-lod-vpc.tf create mode 100644 examples/data-solutions/dp-foundation/03-orc-composer.tf create mode 100644 examples/data-solutions/dp-foundation/03-orc-datastorage.tf create mode 100644 examples/data-solutions/dp-foundation/03-orc-main.tf create mode 100644 examples/data-solutions/dp-foundation/03-orc-vpc.tf create mode 100644 examples/data-solutions/dp-foundation/04-trf-datastorage.tf create mode 100644 examples/data-solutions/dp-foundation/04-trf-main.tf create mode 100644 examples/data-solutions/dp-foundation/04-trf-vpc.tf create mode 100644 examples/data-solutions/dp-foundation/05-dtl-datastorage.tf create mode 100644 examples/data-solutions/dp-foundation/05-dtl-main.tf create mode 100644 examples/data-solutions/dp-foundation/99-kms-main.tf create mode 100644 examples/data-solutions/dp-foundation/README.md create mode 100644 examples/data-solutions/dp-foundation/backend.tf.sample create mode 100644 examples/data-solutions/dp-foundation/main.tf create mode 100644 examples/data-solutions/dp-foundation/variables.tf diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf new file mode 100644 index 00000000..0ea78075 --- /dev/null +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -0,0 +1,78 @@ +# 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. + +locals { + lnd_bucket_retention_policy = { + retention_period = 7776000 # 90 * 24 * 60 * 60 + is_locked = false + } +} +############################################################################### +# GCS # +############################################################################### + +module "lnd-sa-cs-0" { + source = "../../../modules/iam-service-account" + project_id = module.lnd-prj.project_id + name = "cs-0" + prefix = local.prefix_lnd +} + +module "lnd-cs-0" { + source = "../../../modules/gcs" + project_id = module.lnd-prj.project_id + name = "cs-0" + prefix = local.prefix_lnd + location = var.region + storage_class = "REGIONAL" + retention_policy = local.lnd_bucket_retention_policy + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy +} + +############################################################################### +# PubSub # +############################################################################### + +module "lnd-sa-ps-0" { + source = "../../../modules/iam-service-account" + project_id = module.lnd-prj.project_id + name = "ps-0" + prefix = local.prefix_lnd +} + +module "lnd-ps-0" { + source = "../../../modules/pubsub" + project_id = module.lnd-prj.project_id + name = "${local.prefix_lnd}-ps-0" +} + +############################################################################### +# BigQuery # +############################################################################### + +module "lnd-sa-bq-0" { + source = "../../../modules/iam-service-account" + project_id = module.lnd-prj.project_id + name = "bq-0" + prefix = local.prefix_lnd +} + +module "lnd-bq-0" { + source = "../../../modules/bigquery-dataset" + project_id = module.lnd-prj.project_id + id = "${replace(local.prefix_lnd, "-", "_")}_bq_0" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null +} diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf new file mode 100644 index 00000000..13c87f17 --- /dev/null +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -0,0 +1,92 @@ +# 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. + +locals { + group_iam_lnd = { + "${local.groups.data-engineers}" = [ + "roles/bigquery.dataEditor", + "roles/pubsub.editor", + "roles/storage.admin", + "roles/storage.objectViewer", + "roles/viewer", + ], + # "${local.groups.data-scientists}" = [ + # "roles/bigquery.dataViewer", + # "roles/bigquery.jobUser", + # "roles/bigquery.user", + # "roles/pubsub.viewer", + # ] + } + iam_lnd = { + "roles/bigquery.dataEditor" = [ + module.lnd-sa-bq-0.iam_email, + ] + "roles/bigquery.dataViewer" = [ + module.lod-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/bigquery.jobUser" = [ + module.orc-sa-cmp-0.iam_email + ] + "roles/bigquery.user" = [ + module.lod-sa-df-0.iam_email + ] + "roles/pubsub.publisher" = [ + module.lnd-sa-ps-0.iam_email + ] + "roles/pubsub.subscriber" = [ + module.lod-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email + ] + "roles/storage.objectAdmin" = [ + module.lod-sa-df-0.iam_email, + ] + "roles/storage.objectCreator" = [ + module.lnd-sa-cs-0.iam_email, + ] + "roles/storage.objectViewer" = [ + module.orc-sa-cmp-0.iam_email, + ] + "roles/storage.admin" = [ + module.lod-sa-df-0.iam_email, + ] + } + prefix_lnd = "${var.prefix}-lnd" +} + +############################################################################### +# Project # +############################################################################### + +module "lnd-prj" { + source = "../../../modules/project" + name = var.project_id["landing"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_lnd : {} + iam_additive = var.project_create == null ? local.iam_lnd : {} + # group_iam = local.group_iam_lnd + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "pubsub.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com", + ]) +} diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf new file mode 100644 index 00000000..39d2d888 --- /dev/null +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -0,0 +1,42 @@ +# 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. + +############################################################################### +# GCS # +############################################################################### + +module "lod-sa-df-0" { + source = "../../../modules/iam-service-account" + project_id = module.lod-prj.project_id + name = "lod-df-0" + prefix = local.prefix_lod + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers + ], + "roles/iam.serviceAccountUser" = [ + module.orc-sa-cmp-0.iam_email, + ] + } +} + +module "lod-cs-df-0" { + source = "../../../modules/gcs" + project_id = module.lod-prj.project_id + name = "lod-cs-0" + prefix = local.prefix_lod + storage_class = "REGIONAL" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null +} diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf new file mode 100644 index 00000000..a8c3e93a --- /dev/null +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -0,0 +1,75 @@ +# 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. + +locals { + group_iam_lod = { + "${local.groups.data-engineers}" = [ + "roles/compute.viewer", + "roles/dataflow.admin", + "roles/dataflow.developer", + "roles/viewer", + ] + } + iam_lod = { + "roles/bigquery.jobUser" = [ + module.lod-sa-df-0.iam_email + ] + "roles/compute.serviceAgent" = [ + "serviceAccount:${module.lod-prj.service_accounts.robots.compute}" + ] + "roles/dataflow.admin" = [ + module.orc-sa-cmp-0.iam_email, + module.lod-sa-df-0.iam_email + ] + "roles/dataflow.worker" = [ + module.lod-sa-df-0.iam_email + ] + "roles/dataflow.serviceAgent" = [ + "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}" + ] + "roles/storage.objectAdmin" = [ + "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}" + ] + } + prefix_lod = "${var.prefix}-lod" +} + +############################################################################### +# Project # +############################################################################### + +module "lod-prj" { + source = "../../../modules/project" + name = var.project_id["load"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_lod : {} + iam_additive = var.project_create == null ? local.iam_lod : {} + # group_iam = local.group_iam_lod + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf new file mode 100644 index 00000000..e46b0a63 --- /dev/null +++ b/examples/data-solutions/dp-foundation/02-lod-vpc.tf @@ -0,0 +1,49 @@ +# 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. + +############################################################################### +# Network # +############################################################################### + +module "lod-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.lod-prj.project_id + name = "${local.prefix_lod}-lod-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.load + name = "subnet" + region = var.region + secondary_ip_range = {} + } + ] +} + +module "lod-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.lod-prj.project_id + network = module.lod-vpc[0].name + admin_ranges = values(module.lod-vpc[0].subnet_ips) +} + +module "lod-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.lod-prj.project_id + region = var.region + name = "${local.prefix_lod}-default" + router_network = module.lod-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf new file mode 100644 index 00000000..6c02fba8 --- /dev/null +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -0,0 +1,129 @@ +# 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. + +############################################################################### +# Composer # +############################################################################### + +module "orc-sa-cmp-0" { + source = "../../../modules/iam-service-account" + project_id = module.orc-prj.project_id + name = "cmp-0" + prefix = local.prefix_orc + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers + ], + "roles/iam.serviceAccountUser" = [ + module.orc-sa-cmp-0.iam_email, + ] + } +} + +resource "google_composer_environment" "orc-cmp-0" { + name = "${local.prefix_orc}-cmp-0" + region = var.composer_config.region + provider = google-beta + project = module.orc-prj.project_id + config { + node_count = 3 + node_config { + zone = "${var.composer_config.region}-b" + service_account = module.orc-sa-cmp-0.email + network = module.orc-vpc[0].self_link + subnetwork = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/subnet"] + tags = ["composer-worker", "http-server", "https-server"] + ip_allocation_policy { + use_ip_aliases = "true" + cluster_secondary_range_name = "pods" + services_secondary_range_name = "services" + } + } + software_config { + env_variables = { + DTL_L0_BQ_DATASET = module.dtl-0-bq-0.dataset_id + DTL_L1_BQ_DATASET = module.dtl-1-bq-0.dataset_id + DTL_L2_BQ_DATASET = module.dtl-2-bq-0.dataset_id + DTL_EXP_BQ_DATASET = module.dtl-exp-bq-0.dataset_id + DTL_L0_GCS = module.dtl-0-cs-0.url + DTL_L1_GCS = module.dtl-1-cs-0.url + DTL_L2_GCS = module.dtl-2-cs-0.url + DTL_EXP_GCS = module.dtl-exp-cs-0.url + LND_GCS = module.lnd-cs-0.url + LND_BQ = module.lnd-bq-0.dataset_id + LND_PS = module.lnd-ps-0.id + LOD_GCS_STAGING = module.lod-cs-df-0.url + TRF_GCS_STAGING = module.trf-cs-df-0.url + GCP_REGION = var.composer_config.region + NET_VPC = module.orc-vpc[0].self_link + NET_SUBNET = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/subnet"] + DTL_L0_PRJ = module.dtl-0-prj.project_id + DTL_L1_PRJ = module.dtl-1-prj.project_id + DTL_L2_PRJ = module.dtl-2-prj.project_id + LND_PRJ = module.lnd-prj.project_id + LOD_PRJ = module.lod-prj.project_id + ORC_PRJ = module.orc-prj.project_id + TRF_PRJ = module.trf-prj.project_id + LOD_SA_DF = module.lod-sa-df-0.email + TRF_SA_DF = module.lod-sa-df-0.email + } + } + private_environment_config { + enable_private_endpoint = "true" + master_ipv4_cidr_block = var.composer_config.ip_range_gke_master + cloud_sql_ipv4_cidr_block = var.composer_config.ip_range_cloudsql + web_server_ipv4_cidr_block = var.composer_config.ip_range_web_server + } + + dynamic "encryption_config" { + for_each = can(module.kms[0].keys.key-cmp.id) ? { 1 = 1 } : {} + content { + kms_key_name = var.cmek_encryption ? try(module.kms[0].keys.key-cmp.id, null) : null + } + } + + # web_server_network_access_control { + # allowed_ip_range { + # value = "172.16.0.0/12" + # description = "Allowed ip range" + # } + # } + } + depends_on = [ + module.dtl-0-bq-0, + module.dtl-1-bq-0, + module.dtl-2-bq-0, + module.dtl-exp-bq-0, + module.dtl-0-cs-0, + module.dtl-1-cs-0, + module.dtl-2-cs-0, + module.dtl-exp-cs-0, + module.lnd-cs-0, + module.lnd-bq-0, + module.lnd-ps-0, + module.lod-cs-df-0, + module.trf-cs-df-0, + module.orc-vpc, + module.orc-vpc, + module.dtl-0-prj, + module.dtl-1-prj, + module.dtl-2-prj, + module.lnd-prj, + module.lod-prj, + module.orc-prj, + module.trf-prj, + module.lod-sa-df-0, + module.lod-sa-df-0, + ] +} diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf new file mode 100644 index 00000000..1a2afe32 --- /dev/null +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.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 +# +# 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. + +############################################################################### +# GCS # +############################################################################### + +module "orc-cs-0" { + source = "../../../modules/gcs" + project_id = module.orc-prj.project_id + name = "orc-cs-0" + prefix = local.prefix_orc + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null +} diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf new file mode 100644 index 00000000..b888b1a7 --- /dev/null +++ b/examples/data-solutions/dp-foundation/03-orc-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. + +locals { + iam_orc = { + "roles/bigquery.dataEditor" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/bigquery.jobUser" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/composer.worker" = [ + module.orc-sa-cmp-0.iam_email + ] + "roles/compute.networkUser" = [ + module.orc-sa-cmp-0.iam_email, + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", + "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", + "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", + "serviceAccount:${module.orc-prj.service_accounts.cloud_services}" + ] + "roles/storage.objectAdmin" = [ + module.lod-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/storage.admin" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email + ] + } + group_iam_orc = { + "${local.groups.data-engineers}" = [ + "roles/bigquery.dataEditor", + "roles/bigquery.jobUser", + "roles/cloudbuild.builds.editor", + "roles/composer.admin", + "roles/composer.environmentAndStorageObjectAdmin", + "roles/iap.httpsResourceAccessor", + "roles/compute.networkUser", + "roles/storage.objectAdmin", + "roles/storage.admin", + "roles/compute.networkUser" + ] + } + prefix_orc = "${var.prefix}-orc" +} + +module "orc-prj" { + source = "../../../modules/project" + name = var.project_id["orchestration"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_orc : {} + iam_additive = var.project_create == null ? local.iam_orc : {} + group_iam = local.group_iam_orc + services = concat(var.project_services, [ + "artifactregistry.googleapis.com", + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "composer.googleapis.com", + "container.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf new file mode 100644 index 00000000..c962edb9 --- /dev/null +++ b/examples/data-solutions/dp-foundation/03-orc-vpc.tf @@ -0,0 +1,53 @@ +# 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. + +############################################################################### +# Network # +############################################################################### + +module "orc-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.orc-prj.project_id + name = "${local.prefix_orc}-orc-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.orchestration + name = "subnet" + region = var.region + secondary_ip_range = {} + secondary_ip_range = { + pods = var.composer_config.secondary_ip_range.pods + services = var.composer_config.secondary_ip_range.services + } + } + ] +} + +module "orc-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.orc-prj.project_id + network = module.orc-vpc[0].name + admin_ranges = values(module.orc-vpc[0].subnet_ips) +} + +module "orc-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.orc-prj.project_id + region = var.region + name = "${local.prefix_orc}-default" + router_network = module.orc-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf new file mode 100644 index 00000000..b5c2b75b --- /dev/null +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -0,0 +1,61 @@ +# 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. + +############################################################################### +# GCS # +############################################################################### + +module "trf-sa-df-0" { + source = "../../../modules/iam-service-account" + project_id = module.trf-prj.project_id + name = "trf-df-0" + prefix = local.prefix_trf + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers, + ], + "roles/iam.serviceAccountUser" = [ + module.orc-sa-cmp-0.iam_email, + ] + } +} + +module "trf-cs-df-0" { + source = "../../../modules/gcs" + project_id = module.trf-prj.project_id + name = "trf-cs-0" + prefix = local.prefix_trf + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null +} + +############################################################################### +# BQ # +############################################################################### + +module "trf-sa-bq-0" { + source = "../../../modules/iam-service-account" + project_id = module.trf-prj.project_id + name = "trf-bq-0" + prefix = local.prefix_trf + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers, + ], + "roles/iam.serviceAccountUser" = [ + module.orc-sa-cmp-0.iam_email, + ] + } +} diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf new file mode 100644 index 00000000..c38a6a69 --- /dev/null +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -0,0 +1,72 @@ +# 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. + +locals { + group_iam_trf = { + "${local.groups.data-engineers}" = [ + "roles/bigquery.jobUser", + "roles/dataflow.admin", + "roles/viewer", + ] + } + iam_trf = { + "roles/bigquery.dataViewer" = [ + module.orc-sa-cmp-0.iam_email + ] + "roles/bigquery.jobUser" = [ + module.trf-sa-bq-0.iam_email, + ] + "roles/dataflow.admin" = [ + module.orc-sa-cmp-0.iam_email, + ] + "roles/dataflow.worker" = [ + module.trf-sa-df-0.iam_email + ] + "roles/storage.objectAdmin" = [ + module.trf-sa-df-0.iam_email, + module.orc-sa-cmp-0.iam_email, + "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}" + ] + } + prefix_trf = "${var.prefix}-trf" +} + +############################################################################### +# Project # +############################################################################### + +module "trf-prj" { + source = "../../../modules/project" + name = var.project_id["trasformation"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_trf : {} + iam_additive = var.project_create == null ? local.iam_trf : {} + group_iam = local.group_iam_trf + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf new file mode 100644 index 00000000..874b8f49 --- /dev/null +++ b/examples/data-solutions/dp-foundation/04-trf-vpc.tf @@ -0,0 +1,49 @@ +# 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. + +############################################################################### +# Network # +############################################################################### + +module "trf-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.trf-prj.project_id + name = "${local.prefix_trf}-trf-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.transformation + name = "subnet" + region = var.region + secondary_ip_range = {} + } + ] +} + +module "trf-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.trf-prj.project_id + network = module.trf-vpc[0].name + admin_ranges = values(module.orc-vpc[0].subnet_ips) +} + +module "trf-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.trf-prj.project_id + region = var.region + name = "${local.prefix_trf}-default" + router_network = module.trf-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf new file mode 100644 index 00000000..3c20e17d --- /dev/null +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -0,0 +1,97 @@ +# 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. + +############################################################################### +# BQ # +############################################################################### + +module "dtl-0-bq-0" { + source = "../../../modules/bigquery-dataset" + project_id = module.dtl-0-prj.project_id + id = "${replace(local.prefix_lnd, "-", "_")}_0_bq_0" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null +} + +module "dtl-1-bq-0" { + source = "../../../modules/bigquery-dataset" + project_id = module.dtl-1-prj.project_id + id = "${replace(local.prefix_lnd, "-", "_")}_1_bq_0" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null +} + +module "dtl-2-bq-0" { + source = "../../../modules/bigquery-dataset" + project_id = module.dtl-2-prj.project_id + id = "${replace(local.prefix_lnd, "-", "_")}_2_bq_0" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null +} + +module "dtl-exp-bq-0" { + source = "../../../modules/bigquery-dataset" + project_id = module.dtl-exp-prj.project_id + id = "${replace(local.prefix_lnd, "-", "_")}_exp_bq_0" + location = var.region + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null +} + +############################################################################### +# GCS # +############################################################################### + +module "dtl-0-cs-0" { + source = "../../../modules/gcs" + project_id = module.dtl-0-prj.project_id + name = "0-cs-0" + prefix = local.prefix_dtl + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy +} + +module "dtl-1-cs-0" { + source = "../../../modules/gcs" + project_id = module.dtl-1-prj.project_id + name = "1-cs-0" + prefix = local.prefix_dtl + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy +} + +module "dtl-2-cs-0" { + source = "../../../modules/gcs" + project_id = module.dtl-2-prj.project_id + name = "2-cs-0" + prefix = local.prefix_dtl + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy +} + +module "dtl-exp-cs-0" { + source = "../../../modules/gcs" + project_id = module.dtl-exp-prj.project_id + name = "exp-cs-0" + prefix = local.prefix_dtl + location = var.region + storage_class = "REGIONAL" + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy +} diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf new file mode 100644 index 00000000..e4913f13 --- /dev/null +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -0,0 +1,160 @@ +# 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. + +locals { + group_iam_dtl = { + "${local.groups.data-engineers}" = [ + "roles/bigquery.dataEditor", + "roles/storage.admin", + "roles/storage.objectCreator", + "roles/storage.objectViewer", + "roles/viewer", + ], + # "${local.groups.data-scientists}" = [ + # "roles/bigquery.jobUser", + # ] + } + iam_dtl = { + "roles/bigquery.dataEditor" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + module.trf-sa-bq-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/bigquery.jobUser" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + ] + "roles/storage.admin" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + ] + "roles/storage.objectCreator" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email, + module.trf-sa-bq-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + "roles/storage.objectViewer" = [ + module.trf-sa-df-0.iam_email, + module.trf-sa-bq-0.iam_email, + module.orc-sa-cmp-0.iam_email, + ] + } + prefix_dtl = "${var.prefix}-dtl" +} + +############################################################################### +# Project # +############################################################################### + +module "dtl-0-prj" { + source = "../../../modules/project" + name = "${var.project_id["datalake"]}-0" + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_dtl : {} + iam_additive = var.project_create == null ? local.iam_dtl : {} + group_iam = local.group_iam_dtl + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} + +module "dtl-1-prj" { + source = "../../../modules/project" + name = "${var.project_id["datalake"]}-1" + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_dtl : {} + iam_additive = var.project_create == null ? local.iam_dtl : {} + group_iam = local.group_iam_dtl + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} + +module "dtl-2-prj" { + source = "../../../modules/project" + name = "${var.project_id["datalake"]}-2" + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_dtl : {} + iam_additive = var.project_create == null ? local.iam_dtl : {} + group_iam = local.group_iam_dtl + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} + +module "dtl-exp-prj" { + source = "../../../modules/project" + name = "${var.project_id["datalake"]}-exp" + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_dtl : {} + iam_additive = var.project_create == null ? local.iam_dtl : {} + group_iam = local.group_iam_dtl + services = concat(var.project_services, [ + "bigquery.googleapis.com", + "bigqueryreservation.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudkms.googleapis.com", + "compute.googleapis.com", + "dataflow.googleapis.com", + "pubsub.googleapis.com", + "servicenetworking.googleapis.com", + "storage.googleapis.com", + "storage-component.googleapis.com" + ]) +} diff --git a/examples/data-solutions/dp-foundation/99-kms-main.tf b/examples/data-solutions/dp-foundation/99-kms-main.tf new file mode 100644 index 00000000..63dc109e --- /dev/null +++ b/examples/data-solutions/dp-foundation/99-kms-main.tf @@ -0,0 +1,77 @@ +# 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 "kms" { + count = var.cmek_encryption ? 1 : 0 + source = "../../../modules/kms" + project_id = module.lnd-prj.project_id + keyring = { + name = "${var.prefix}-keyring", + location = var.region + } + keys = { + key-bq = null + key-cmp = null + key-df = null + key-gcs = null + key-ps = null + } + key_iam = { + key-bq = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", + "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", + "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", + "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", + "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", + ] + }, + key-cmp = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", + "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", + "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", + "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", + "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", + "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", + + ] + }, + key-df = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", + "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", + "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", + "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", + ] + } + key-gcs = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", + "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", + "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", + "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", + "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", + "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", + "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", + "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", + ] + }, + key-ps = { + "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ + "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" + ] + } + } +} diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md new file mode 100644 index 00000000..97aaadee --- /dev/null +++ b/examples/data-solutions/dp-foundation/README.md @@ -0,0 +1,15 @@ + - GCS and BQ regional + - KMS: Regional keyring, one key per product + - Composer require "Require OS Login" not enforced + - Groups: gcp-data-scientists, gcp-data-engineers + + #TODO KMS: support key per product + #TODO Write README + #TODO Run a working test + #TODO Write a working e2e test + #TODO Column level access on BQ + #TODO DataCatalog + #TODO DLP + #TODO DataLake layers: Tables, views and Authorized views + #TODO ShareVPC Role: roles/composer.sharedVpcAgent, roles/container.hostServiceAgentUser + #TODO Composer require "Require OS Login" not enforced \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/backend.tf.sample b/examples/data-solutions/dp-foundation/backend.tf.sample new file mode 100644 index 00000000..49a0883d --- /dev/null +++ b/examples/data-solutions/dp-foundation/backend.tf.sample @@ -0,0 +1,30 @@ +# 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. + +# The `impersonate_service_account` option require the identity launching terraform +# role `roles/iam.serviceAccountTokenCreator` on the Service Account specified. + +terraform { + backend "gcs" { + bucket = "BUCKET_NAME" + prefix = "PREFIX" + impersonate_service_account = "SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com" + } +} +provider "google" { + impersonate_service_account = "SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com" +} +provider "google-beta" { + impersonate_service_account = "SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com" +} \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf new file mode 100644 index 00000000..063f0a77 --- /dev/null +++ b/examples/data-solutions/dp-foundation/main.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. + +locals { + groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } + groups_iam = { for k, v in local.groups : k => "group:${v}" } +} diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf new file mode 100644 index 00000000..47b223cd --- /dev/null +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -0,0 +1,132 @@ +# 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 "cmek_encryption" { + description = "Flag to enable CMEK on GCP resources created." + type = bool + default = false +} + +variable "composer_config" { + type = object({ + ip_range_cloudsql = string + ip_range_gke_master = string + ip_range_web_server = string + region = string + secondary_ip_range = object({ + pods = string + services = string + }) + }) + default = { + ip_range_cloudsql = "10.20.10.0/24" + ip_range_gke_master = "10.20.11.0/28" + ip_range_web_server = "10.20.11.16/28" + region = "europe-west1" + secondary_ip_range = { + pods = "10.10.8.0/22" + services = "10.10.12.0/24" + } + } +} + +variable "data_force_destroy" { + description = "Flag to set 'force_destroy' on data services like biguqery or cloud storage." + type = bool + default = false +} + +variable "groups" { + description = "Groups." + type = map(string) + default = { + data-engineers = "gcp-data-engineers" + data-scientists = "gcp-data-scientists" + } +} + +variable "network_config" { + description = "Shared VPC to use. If not null networks will be created in projects." + type = object({ + network = string + vpc_subnet_range = object({ + load = string + transformation = string + orchestration = string + }) + }) + default = { + network = null + vpc_subnet_range = { + load = "10.10.0.0/24" + transformation = "10.10.0.0/24" + orchestration = "10.10.0.0/24" + } + } +} + +variable "organization" { + description = "Organization details." + type = object({ + domain = string + }) +} + +variable "prefix" { + description = "Unique prefix used for resource names. Not used for project if 'project_create' is null." + type = string +} + +variable "project_create" { + description = "Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format" + type = object({ + billing_account_id = string + parent = string + }) + default = null +} + +variable "project_id" { + description = "Project id, references existing project if `project_create` is null." + type = object({ + landing = string + load = string + orchestration = string + trasformation = string + datalake = string + }) + default = { + landing = "lnd" + load = "lod" + orchestration = "orc" + trasformation = "trf" + datalake = "dtl" + } +} + +variable "project_services" { + type = list(string) + default = [ + "cloudresourcemanager.googleapis.com", + "iam.googleapis.com", + "serviceusage.googleapis.com", + "stackdriver.googleapis.com" + ] +} + +variable "region" { + description = "The region where resources will be deployed." + type = string + default = "europe-west1" +} From c8a4836c75060e119ab0aaa1f3c9d288d2974b3a Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 19 Jan 2022 00:53:52 +0100 Subject: [PATCH 046/132] Fix roles and variables. Add e2e DAG example! --- .../dp-foundation/01-lnd-datastorage.tf | 33 +++- .../dp-foundation/02-lod-datastorage.tf | 4 +- .../dp-foundation/02-lod-main.tf | 3 +- .../dp-foundation/03-orc-composer.tf | 4 +- .../dp-foundation/03-orc-datastorage.tf | 2 +- .../dp-foundation/03-orc-main.tf | 6 + .../dp-foundation/04-trf-datastorage.tf | 8 +- .../dp-foundation/05-dtl-datastorage.tf | 8 +- .../dp-foundation/demo/README.md | 0 .../demo/data/customer_purchase.json | 50 ++++++ .../dp-foundation/demo/data/customers.csv | 12 ++ .../dp-foundation/demo/data/customers.json | 26 +++ .../demo/data/customers_schema.json | 28 ++++ .../dp-foundation/demo/data/customers_udf.js | 12 ++ .../dp-foundation/demo/data/purchases.csv | 20 +++ .../dp-foundation/demo/data/purchases.json | 32 ++++ .../demo/data/purchases_schema.json | 34 ++++ .../dp-foundation/demo/data/purchases_udf.js | 13 ++ .../dp-foundation/demo/gcs2bq.py | 156 ++++++++++++++++++ .../data-solutions/dp-foundation/output.tf | 73 ++++++++ 20 files changed, 503 insertions(+), 21 deletions(-) create mode 100644 examples/data-solutions/dp-foundation/demo/README.md create mode 100644 examples/data-solutions/dp-foundation/demo/data/customer_purchase.json create mode 100644 examples/data-solutions/dp-foundation/demo/data/customers.csv create mode 100644 examples/data-solutions/dp-foundation/demo/data/customers.json create mode 100644 examples/data-solutions/dp-foundation/demo/data/customers_schema.json create mode 100644 examples/data-solutions/dp-foundation/demo/data/customers_udf.js create mode 100644 examples/data-solutions/dp-foundation/demo/data/purchases.csv create mode 100644 examples/data-solutions/dp-foundation/demo/data/purchases.json create mode 100644 examples/data-solutions/dp-foundation/demo/data/purchases_schema.json create mode 100644 examples/data-solutions/dp-foundation/demo/data/purchases_udf.js create mode 100644 examples/data-solutions/dp-foundation/demo/gcs2bq.py create mode 100644 examples/data-solutions/dp-foundation/output.tf diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index 0ea78075..76e62d26 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -27,18 +27,23 @@ module "lnd-sa-cs-0" { project_id = module.lnd-prj.project_id name = "cs-0" prefix = local.prefix_lnd + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers + ] + } } module "lnd-cs-0" { - source = "../../../modules/gcs" - project_id = module.lnd-prj.project_id - name = "cs-0" - prefix = local.prefix_lnd - location = var.region - storage_class = "REGIONAL" - retention_policy = local.lnd_bucket_retention_policy - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null - force_destroy = var.data_force_destroy + source = "../../../modules/gcs" + project_id = module.lnd-prj.project_id + name = "cs-0" + prefix = local.prefix_lnd + location = var.region + storage_class = "REGIONAL" + # retention_policy = local.lnd_bucket_retention_policy + encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + force_destroy = var.data_force_destroy } ############################################################################### @@ -50,6 +55,11 @@ module "lnd-sa-ps-0" { project_id = module.lnd-prj.project_id name = "ps-0" prefix = local.prefix_lnd + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers + ] + } } module "lnd-ps-0" { @@ -67,6 +77,11 @@ module "lnd-sa-bq-0" { project_id = module.lnd-prj.project_id name = "bq-0" prefix = local.prefix_lnd + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + local.groups_iam.data-engineers + ] + } } module "lnd-bq-0" { diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index 39d2d888..3c2199a5 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -19,7 +19,7 @@ module "lod-sa-df-0" { source = "../../../modules/iam-service-account" project_id = module.lod-prj.project_id - name = "lod-df-0" + name = "df-0" prefix = local.prefix_lod iam = { "roles/iam.serviceAccountTokenCreator" = [ @@ -34,7 +34,7 @@ module "lod-sa-df-0" { module "lod-cs-df-0" { source = "../../../modules/gcs" project_id = module.lod-prj.project_id - name = "lod-cs-0" + name = "cs-0" prefix = local.prefix_lod storage_class = "REGIONAL" location = var.region diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index a8c3e93a..c3eab37a 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -39,7 +39,8 @@ locals { "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}" ] "roles/storage.objectAdmin" = [ - "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}" + "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", + module.lod-sa-df-0.iam_email ] } prefix_lod = "${var.prefix}-lod" diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index 6c02fba8..b0d6c514 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -51,6 +51,7 @@ resource "google_composer_environment" "orc-cmp-0" { } } software_config { + image_version = "composer-1.17.5-airflow-2.1.4" env_variables = { DTL_L0_BQ_DATASET = module.dtl-0-bq-0.dataset_id DTL_L1_BQ_DATASET = module.dtl-1-bq-0.dataset_id @@ -76,7 +77,8 @@ resource "google_composer_environment" "orc-cmp-0" { ORC_PRJ = module.orc-prj.project_id TRF_PRJ = module.trf-prj.project_id LOD_SA_DF = module.lod-sa-df-0.email - TRF_SA_DF = module.lod-sa-df-0.email + TRF_SA_DF = module.trf-sa-df-0.email + TRF_SA_BQ = module.trf-sa-bq-0.email } } private_environment_config { diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 1a2afe32..f39d74a1 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -19,7 +19,7 @@ module "orc-cs-0" { source = "../../../modules/gcs" project_id = module.orc-prj.project_id - name = "orc-cs-0" + name = "cs-0" prefix = local.prefix_orc location = var.region storage_class = "REGIONAL" diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index b888b1a7..3efaabdb 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -36,9 +36,13 @@ locals { "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", "serviceAccount:${module.orc-prj.service_accounts.cloud_services}" ] + "roles/iam.serviceAccountUser" = [ + module.orc-sa-cmp-0.iam_email, + ] "roles/storage.objectAdmin" = [ module.lod-sa-df-0.iam_email, module.orc-sa-cmp-0.iam_email, + "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", ] "roles/storage.admin" = [ module.lod-sa-df-0.iam_email, @@ -53,6 +57,7 @@ locals { "roles/composer.admin", "roles/composer.environmentAndStorageObjectAdmin", "roles/iap.httpsResourceAccessor", + "roles/iam.serviceAccountUser", "roles/compute.networkUser", "roles/storage.objectAdmin", "roles/storage.admin", @@ -73,6 +78,7 @@ module "orc-prj" { iam = var.project_create != null ? local.iam_orc : {} iam_additive = var.project_create == null ? local.iam_orc : {} group_iam = local.group_iam_orc + oslogin = false services = concat(var.project_services, [ "artifactregistry.googleapis.com", "bigquery.googleapis.com", diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index b5c2b75b..555f41ed 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -19,11 +19,12 @@ module "trf-sa-df-0" { source = "../../../modules/iam-service-account" project_id = module.trf-prj.project_id - name = "trf-df-0" + name = "df-0" prefix = local.prefix_trf iam = { "roles/iam.serviceAccountTokenCreator" = [ local.groups_iam.data-engineers, + module.orc-sa-cmp-0.iam_email, ], "roles/iam.serviceAccountUser" = [ module.orc-sa-cmp-0.iam_email, @@ -34,7 +35,7 @@ module "trf-sa-df-0" { module "trf-cs-df-0" { source = "../../../modules/gcs" project_id = module.trf-prj.project_id - name = "trf-cs-0" + name = "cs-0" prefix = local.prefix_trf location = var.region storage_class = "REGIONAL" @@ -48,11 +49,12 @@ module "trf-cs-df-0" { module "trf-sa-bq-0" { source = "../../../modules/iam-service-account" project_id = module.trf-prj.project_id - name = "trf-bq-0" + name = "bq-0" prefix = local.prefix_trf iam = { "roles/iam.serviceAccountTokenCreator" = [ local.groups_iam.data-engineers, + module.orc-sa-cmp-0.iam_email, ], "roles/iam.serviceAccountUser" = [ module.orc-sa-cmp-0.iam_email, diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index 3c20e17d..b1018abb 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -19,7 +19,7 @@ module "dtl-0-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-0-prj.project_id - id = "${replace(local.prefix_lnd, "-", "_")}_0_bq_0" + id = "${replace(local.prefix_dtl, "-", "_")}_0_bq_0" location = var.region encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null } @@ -27,7 +27,7 @@ module "dtl-0-bq-0" { module "dtl-1-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-1-prj.project_id - id = "${replace(local.prefix_lnd, "-", "_")}_1_bq_0" + id = "${replace(local.prefix_dtl, "-", "_")}_1_bq_0" location = var.region encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null } @@ -35,7 +35,7 @@ module "dtl-1-bq-0" { module "dtl-2-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-2-prj.project_id - id = "${replace(local.prefix_lnd, "-", "_")}_2_bq_0" + id = "${replace(local.prefix_dtl, "-", "_")}_2_bq_0" location = var.region encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null } @@ -43,7 +43,7 @@ module "dtl-2-bq-0" { module "dtl-exp-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-exp-prj.project_id - id = "${replace(local.prefix_lnd, "-", "_")}_exp_bq_0" + id = "${replace(local.prefix_dtl, "-", "_")}_exp_bq_0" location = var.region encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null } diff --git a/examples/data-solutions/dp-foundation/demo/README.md b/examples/data-solutions/dp-foundation/demo/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/data-solutions/dp-foundation/demo/data/customer_purchase.json b/examples/data-solutions/dp-foundation/demo/data/customer_purchase.json new file mode 100644 index 00000000..d97e228f --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/customer_purchase.json @@ -0,0 +1,50 @@ +[ + { + "mode": "REQUIRED", + "name": "id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "customer_id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "purchase_id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "customer_name", + "type": "STRING", + "description": "Name" + }, + { + "mode": "REQUIRED", + "name": "customer_surname", + "type": "STRING", + "description": "Surname" + }, + { + "mode": "REQUIRED", + "name": "purchase_item", + "type": "STRING", + "description": "Item Name" + }, + { + "mode": "REQUIRED", + "name": "price", + "type": "FLOAT", + "description": "Item Price" + }, + { + "mode": "REQUIRED", + "name": "purchase_timestamp", + "type": "TIMESTAMP", + "description": "Timestamp" + } +] \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/customers.csv b/examples/data-solutions/dp-foundation/demo/data/customers.csv new file mode 100644 index 00000000..ea6aa753 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/customers.csv @@ -0,0 +1,12 @@ +1,Name1,Surname1,1636972001 +2,Name2,Surname2,1636972002 +3,Name3,Surname3,1636972003 +4,Name4,Surname4,1636972004 +5,Name5,Surname5,1636972005 +6,Name6,Surname6,1636972006 +7,Name7,Surname7,1636972007 +8,Name8,Surname8,1636972008 +9,Name9,Surname9,1636972009 +10,Name11,Surname11,1636972010 +11,Name12,Surname12,1636972011 +12,Name13,Surname13,1636972012 \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/customers.json b/examples/data-solutions/dp-foundation/demo/data/customers.json new file mode 100644 index 00000000..c685279d --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/customers.json @@ -0,0 +1,26 @@ +[ + { + "mode": "REQUIRED", + "name": "id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "name", + "type": "STRING", + "description": "Name" + }, + { + "mode": "REQUIRED", + "name": "surname", + "type": "STRING", + "description": "Surname" + }, + { + "mode": "REQUIRED", + "name": "timestamp", + "type": "TIMESTAMP", + "description": "Timestamp" + } +] \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/customers_schema.json b/examples/data-solutions/dp-foundation/demo/data/customers_schema.json new file mode 100644 index 00000000..b751a511 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/customers_schema.json @@ -0,0 +1,28 @@ +{ + "BigQuery Schema": [ + { + "mode": "REQUIRED", + "name": "id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "name", + "type": "STRING", + "description": "Name" + }, + { + "mode": "REQUIRED", + "name": "surname", + "type": "STRING", + "description": "Surname" + }, + { + "mode": "REQUIRED", + "name": "timestamp", + "type": "TIMESTAMP", + "description": "Timestamp" + } + ] +} \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/customers_udf.js b/examples/data-solutions/dp-foundation/demo/data/customers_udf.js new file mode 100644 index 00000000..11e1cfe4 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/customers_udf.js @@ -0,0 +1,12 @@ +function transform(line) { + var values = line.split(','); + + var obj = new Object(); + obj.id = values[0] + obj.name = values[1]; + obj.surname = values[2]; + obj.timestamp = values[3]; + var jsonString = JSON.stringify(obj); + + return jsonString; +} \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/purchases.csv b/examples/data-solutions/dp-foundation/demo/data/purchases.csv new file mode 100644 index 00000000..0b0de75c --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/purchases.csv @@ -0,0 +1,20 @@ +1,1,Car1,5000,1636972012 +1,1,Car1,7000,1636972045 +1,2,Car1,6000,1636972088 +1,2,Car1,8000,16369720099 +1,3,Car1,10000,1636972102 +1,3,Car1,50000,1636972180 +1,4,Car1,13000,1636972260 +1,4,Car1,5000,1636972302 +1,5,Car1,2000,1636972408 +1,1,Car1,77000,1636972501 +1,1,Car1,64000,1636975001 +1,8,Car1,2000,1636976001 +1,9,Car1,4000,1636977001 +1,10,Car1,18000,1636982001 +1,11,Car1,21000,1636992001 +1,11,Car1,33000,1636932001 +1,11,Car1,37000,1636872001 +1,11,Car1,26000,1636772001 +1,12,Car1,22000,1636672001 +1,4,Car1,11000,1636952001 \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/purchases.json b/examples/data-solutions/dp-foundation/demo/data/purchases.json new file mode 100644 index 00000000..78eb024c --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/purchases.json @@ -0,0 +1,32 @@ +[ + { + "mode": "REQUIRED", + "name": "id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "customer_id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "item", + "type": "STRING", + "description": "Item Name" + }, + { + "mode": "REQUIRED", + "name": "price", + "type": "FLOAT", + "description": "Item Price" + }, + { + "mode": "REQUIRED", + "name": "timestamp", + "type": "TIMESTAMP", + "description": "Timestamp" + } +] \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/purchases_schema.json b/examples/data-solutions/dp-foundation/demo/data/purchases_schema.json new file mode 100644 index 00000000..68731743 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/purchases_schema.json @@ -0,0 +1,34 @@ +{ + "BigQuery Schema": [ + { + "mode": "REQUIRED", + "name": "id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "customer_id", + "type": "INTEGER", + "description": "ID" + }, + { + "mode": "REQUIRED", + "name": "item", + "type": "STRING", + "description": "Item Name" + }, + { + "mode": "REQUIRED", + "name": "price", + "type": "FLOAT", + "description": "Item Price" + }, + { + "mode": "REQUIRED", + "name": "timestamp", + "type": "TIMESTAMP", + "description": "Timestamp" + } + ] +} \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/data/purchases_udf.js b/examples/data-solutions/dp-foundation/demo/data/purchases_udf.js new file mode 100644 index 00000000..d6ffde53 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/data/purchases_udf.js @@ -0,0 +1,13 @@ +function transform(line) { + var values = line.split(','); + + var obj = new Object(); + obj.id = values[0]; + obj.customer_id = values[1]; + obj.item = values[2]; + obj.price = values[3]; + obj.timestamp = values[4]; + var jsonString = JSON.stringify(obj); + + return jsonString; +} \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/gcs2bq.py b/examples/data-solutions/dp-foundation/demo/gcs2bq.py new file mode 100644 index 00000000..dea19b52 --- /dev/null +++ b/examples/data-solutions/dp-foundation/demo/gcs2bq.py @@ -0,0 +1,156 @@ +# 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. + +# -------------------------------------------------------------------------------- +# Load The Dependencies +# -------------------------------------------------------------------------------- + +import csv +import datetime +import io +import logging +import os + +from airflow import models +from airflow.contrib.operators.dataflow_operator import DataflowTemplateOperator +from airflow.operators import dummy +from airflow.providers.google.cloud.operators.bigquery import BigQueryInsertJobOperator + +# -------------------------------------------------------------------------------- +# Set variables +# -------------------------------------------------------------------------------- + +LND_GCS = os.environ.get("LND_GCS") +LOD_GCS_STAGING = os.environ.get("LOD_GCS_STAGING") +DTL_L0_BQ_DATASET = os.environ.get("DTL_L0_BQ_DATASET") +DTL_L0_PRJ = os.environ.get("DTL_L0_PRJ") +DTL_L1_BQ_DATASET = os.environ.get("DTL_L1_BQ_DATASET") +DTL_L1_PRJ = os.environ.get("DTL_L1_PRJ") +LOD_PRJ = os.environ.get("LOD_PRJ") +DF_ZONE = os.environ.get("GCP_REGION") + "-b" +DF_REGION = BQ_REGION = os.environ.get("GCP_REGION") +NET_VPC = os.environ.get("NET_VPC") +NET_SUBNET = "https://www.googleapis.com/compute/v1/projects/lc-04-lod/regions/europe-west1/subnetworks/subnet" #"https://www.googleapis.com/compute/v1/" + os.environ.get("NET_SUBNET") +LOD_SA_DF = os.environ.get("LOD_SA_DF") +TRF_SA_DF = os.environ.get("TRF_SA_DF") +TRF_SA_BQ = os.environ.get("TRF_SA_BQ") +TRF_PRJ = os.environ.get("TRF_PRJ") + +# -------------------------------------------------------------------------------- +# Set default arguments +# -------------------------------------------------------------------------------- + +# If you are running Airflow in more than one time zone +# see https://airflow.apache.org/docs/apache-airflow/stable/timezone.html +# for best practices +yesterday = datetime.datetime.now() - datetime.timedelta(days=1) + +default_args = { + 'owner': 'airflow', + 'start_date': yesterday, + 'depends_on_past': False, + 'email': [''], + 'email_on_failure': False, + 'email_on_retry': False, + 'retries': 1, + 'retry_delay': datetime.timedelta(minutes=5), + 'dataflow_default_options': { + 'project': LOD_PRJ, + 'location': DF_REGION, + 'zone': DF_ZONE, + 'stagingLocation': LOD_GCS_STAGING, + 'tempLocation': LOD_GCS_STAGING + "/tmp", + 'serviceAccountEmail': LOD_SA_DF, + 'subnetwork': NET_SUBNET, + 'ipConfiguration': "WORKER_IP_PRIVATE" + }, +} + +# -------------------------------------------------------------------------------- +# Main DAG +# -------------------------------------------------------------------------------- + +with models.DAG( + 'data_pipeline_dag', + default_args=default_args, + schedule_interval=None) as dag: + start = dummy.DummyOperator( + task_id='start', + trigger_rule='all_success' + ) + + end = dummy.DummyOperator( + task_id='end', + trigger_rule='all_success' + ) + + customers_import = DataflowTemplateOperator( + task_id="dataflow_customer_import", + template="gs://dataflow-templates/latest/GCS_Text_to_BigQuery", + parameters={ + "javascriptTextTransformFunctionName": "transform", + "JSONPath": LND_GCS + "/customers_schema.json", + "javascriptTextTransformGcsPath": LND_GCS + "/customers_udf.js", + "inputFilePattern": LND_GCS + "/customers.csv", + "outputTable": DTL_L0_PRJ + ":"+DTL_L0_BQ_DATASET+".customers", + "bigQueryLoadingTemporaryDirectory": LOD_GCS_STAGING + "/tmp/bq/", + }, + ) + + purchases_import = DataflowTemplateOperator( + task_id="dataflow_purchases_import", + template="gs://dataflow-templates/latest/GCS_Text_to_BigQuery", + parameters={ + "javascriptTextTransformFunctionName": "transform", + "JSONPath": LND_GCS + "/purchases_schema.json", + "javascriptTextTransformGcsPath": LND_GCS + "/purchases_udf.js", + "inputFilePattern": LND_GCS + "/purchases.csv", + "outputTable": DTL_L0_PRJ + ":"+DTL_L0_BQ_DATASET+".purchases", + "bigQueryLoadingTemporaryDirectory": LOD_GCS_STAGING + "/tmp/bq/", + }, + ) + + join_customer_purchase = BigQueryInsertJobOperator( + task_id='bq_join_customer_purchase', + gcp_conn_id='bigquery_default', + project_id=TRF_PRJ, + location=BQ_REGION, + configuration={ + 'jobType':'QUERY', + 'writeDisposition':'WRITE_TRUNCATE', + 'query':{ + 'query':"""SELECT + c.id as customer_id, + p.id as purchase_id, + c.name as name, + c.surname as surname, + p.item as item, + p.price as price, + p.timestamp as timestamp + FROM `lc-04-dtl-0.lc_04_dtl_0_bq_0.customers` c + JOIN `lc-04-dtl-0.lc_04_dtl_0_bq_0.purchases` p ON c.id = p.customer_id + """, + 'destinationTable':{ + 'projectId': DTL_L1_PRJ, + 'datasetId': DTL_L1_BQ_DATASET, + 'tableId': 'customer_purchase' + }, + "useLegacySql": False + } + }, + impersonation_chain=[TRF_SA_BQ] + ) + + start >> [customers_import, purchases_import] >> join_customer_purchase >> end + #start >> join_customer_purchase >> end diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf new file mode 100644 index 00000000..52e993c8 --- /dev/null +++ b/examples/data-solutions/dp-foundation/output.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. + +output "bigquery-datasets" { + description = "BigQuery datasets." + value = { + lnd-bq-0 = module.lnd-bq-0.dataset_id, + dtl-0-bq-0 = module.dtl-0-bq-0.dataset_id, + dtl-1-bq-0 = module.dtl-1-bq-0.dataset_id, + dtl-2-bq-0 = module.dtl-2-bq-0.dataset_id, + dtl-exp-bq-0 = module.dtl-exp-bq-0.dataset_id, + } +} + +output "gcs-buckets" { + description = "GCS buckets." + value = { + dtl-0-cs-0 = module.dtl-0-cs-0.name, + dtl-1-cs-0 = module.dtl-1-cs-0.name, + dtl-2-cs-0 = module.dtl-2-cs-0.name, + dtl-exp-cs-0 = module.dtl-exp-cs-0.name, + lnd-cs-0 = module.lnd-cs-0.name, + lod-cs-df = module.lod-cs-df-0.name, + orc-cs-0 = module.orc-cs-0.name, + trf-cs-df = module.trf-cs-df-0.name, + } +} + +output "projects" { + description = "GCP Projects." + value = { + lnd-prj = module.lnd-prj.project_id, + lod-prj = module.lod-prj.project_id, + orc-prj = module.orc-prj.project_id, + trf-prj = module.trf-prj.project_id, + dtl-0-prj = module.dtl-0-prj.project_id, + dtl-1-prj = module.dtl-1-prj.project_id, + dtl-2-prj = module.dtl-2-prj.project_id, + dtl-exp-prj = module.dtl-exp-prj.project_id, + } +} + +output "VPC" { + description = "VPC networks." + value = { + lod-vpc = try(module.lod-vpc[0].name, var.network_config.load) + orc-vpc = try(module.orc-vpc[0].name, var.network_config.orchestrator) + trf-vpc = try(module.trf-vpc[0].name, var.network_config.transformation) + } +} + +output "ZZZ_demo_commands" { + description = "Demo commands" + value = { + 01 = "gsutil -i ${module.lod-sa-df-0.email} cp demo/data/*.csv gs://${module.lnd-cs-0.name}" + 02 = "gsutil cp demo/data/*.j* gs://${module.orc-cs-0.name}" + 03 = "gsutil cp demo/gcs2bq.py ${google_composer_environment.orc-cmp-0.config[0].dag_gcs_prefix}/" + 04 = "bq mk --table --description 'Customers table' ${module.dtl-0-prj.project_id}:${module.dtl-0-bq-0.dataset_id}.customers demo/data/customers.json" + 05 = "bq mk --table --description 'Purchases table' ${module.dtl-0-prj.project_id}:${module.dtl-0-bq-0.dataset_id}.purchases demo/data/purchases.json" + 06 = "bq mk --table --description 'Customer_Purchase table' ${module.dtl-1-prj.project_id}:${module.dtl-1-bq-0.dataset_id}.customers demo/data/customer_purchase.json" + } +} From a9212fb3b57d749a0bd3008aa7e91fedf8ab96c3 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 19 Jan 2022 12:01:33 +0100 Subject: [PATCH 047/132] Fix example --- examples/data-solutions/dp-foundation/demo/gcs2bq.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/data-solutions/dp-foundation/demo/gcs2bq.py b/examples/data-solutions/dp-foundation/demo/gcs2bq.py index dea19b52..50c34f06 100644 --- a/examples/data-solutions/dp-foundation/demo/gcs2bq.py +++ b/examples/data-solutions/dp-foundation/demo/gcs2bq.py @@ -153,4 +153,3 @@ with models.DAG( ) start >> [customers_import, purchases_import] >> join_customer_purchase >> end - #start >> join_customer_purchase >> end From c28d756d280d3b852ac76aba880d1d02babe8358 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 19 Jan 2022 21:33:24 +0100 Subject: [PATCH 048/132] Fix KMS --- .../dp-foundation/01-lnd-datastorage.tf | 4 +- .../dp-foundation/01-lnd-main.tf.tf | 7 +- .../dp-foundation/02-lod-datastorage.tf | 2 +- .../dp-foundation/02-lod-main.tf | 5 + .../dp-foundation/03-orc-composer.tf | 4 +- .../dp-foundation/03-orc-datastorage.tf | 2 +- .../dp-foundation/03-orc-main.tf | 4 + .../dp-foundation/04-trf-datastorage.tf | 2 +- .../dp-foundation/04-trf-main.tf | 4 + .../dp-foundation/05-dtl-datastorage.tf | 16 +-- .../dp-foundation/05-dtl-main.tf | 16 +++ .../dp-foundation/99-kms-main.tf | 126 +++++++++--------- .../dp-foundation/demo/README.md | 1 + .../data-solutions/dp-foundation/variables.tf | 14 +- 14 files changed, 125 insertions(+), 82 deletions(-) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index 76e62d26..8d4a4ec7 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -42,7 +42,7 @@ module "lnd-cs-0" { location = var.region storage_class = "REGIONAL" # retention_policy = local.lnd_bucket_retention_policy - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -89,5 +89,5 @@ module "lnd-bq-0" { project_id = module.lnd-prj.project_id id = "${replace(local.prefix_lnd, "-", "_")}_bq_0" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null } diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 13c87f17..0c4a59e8 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -79,7 +79,7 @@ module "lnd-prj" { # additive IAM bindings avoid disrupting bindings in existing project iam = var.project_create != null ? local.iam_lnd : {} iam_additive = var.project_create == null ? local.iam_lnd : {} - # group_iam = local.group_iam_lnd + group_iam = local.group_iam_lnd services = concat(var.project_services, [ "bigquery.googleapis.com", "bigqueryreservation.googleapis.com", @@ -89,4 +89,9 @@ module "lnd-prj" { "storage.googleapis.com", "storage-component.googleapis.com", ]) + service_encryption_key_ids = { + bq = [try(var.service_encryption_keys.bq, null)] + pubsub = [try(var.service_encryption_keys.pubsub, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index 3c2199a5..5f1f0b38 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -38,5 +38,5 @@ module "lod-cs-df-0" { prefix = local.prefix_lod storage_class = "REGIONAL" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null } diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index c3eab37a..fa5b318f 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -73,4 +73,9 @@ module "lod-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + pubsub = [try(var.service_encryption_keys.pubsub, null)] + dataflow = [try(var.service_encryption_keys.dataflow, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index b0d6c514..3be02bc3 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -89,9 +89,9 @@ resource "google_composer_environment" "orc-cmp-0" { } dynamic "encryption_config" { - for_each = can(module.kms[0].keys.key-cmp.id) ? { 1 = 1 } : {} + for_each = var.service_encryption_keys != null ? { 1 = 1 } : {} content { - kms_key_name = var.cmek_encryption ? try(module.kms[0].keys.key-cmp.id, null) : null + kms_key_name = var.service_encryption_keys != null ? try(var.service_encryption_keys.composer, null) : null } } diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index f39d74a1..5f271ad3 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -23,5 +23,5 @@ module "orc-cs-0" { prefix = local.prefix_orc location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null } diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index 3efaabdb..34ced946 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -93,4 +93,8 @@ module "orc-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + composer = [try(var.service_encryption_keys.composer, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index 555f41ed..d079fb39 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -39,7 +39,7 @@ module "trf-cs-df-0" { prefix = local.prefix_trf location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null } ############################################################################### diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index c38a6a69..f5ae2ab1 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -69,4 +69,8 @@ module "trf-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + dataflow = [try(var.service_encryption_keys.dataflow, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index b1018abb..8fc66ffe 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -21,7 +21,7 @@ module "dtl-0-bq-0" { project_id = module.dtl-0-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_0_bq_0" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null } module "dtl-1-bq-0" { @@ -29,7 +29,7 @@ module "dtl-1-bq-0" { project_id = module.dtl-1-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_1_bq_0" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null } module "dtl-2-bq-0" { @@ -37,7 +37,7 @@ module "dtl-2-bq-0" { project_id = module.dtl-2-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_2_bq_0" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null } module "dtl-exp-bq-0" { @@ -45,7 +45,7 @@ module "dtl-exp-bq-0" { project_id = module.dtl-exp-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_exp_bq_0" location = var.region - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null } ############################################################################### @@ -59,7 +59,7 @@ module "dtl-0-cs-0" { prefix = local.prefix_dtl location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -70,7 +70,7 @@ module "dtl-1-cs-0" { prefix = local.prefix_dtl location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -81,7 +81,7 @@ module "dtl-2-cs-0" { prefix = local.prefix_dtl location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -92,6 +92,6 @@ module "dtl-exp-cs-0" { prefix = local.prefix_dtl location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf index e4913f13..882e0e08 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-main.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -82,6 +82,10 @@ module "dtl-0-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + bq = [try(var.service_encryption_keys.bq, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } module "dtl-1-prj" { @@ -107,6 +111,10 @@ module "dtl-1-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + bq = [try(var.service_encryption_keys.bq, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } module "dtl-2-prj" { @@ -132,6 +140,10 @@ module "dtl-2-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + bq = [try(var.service_encryption_keys.bq, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } module "dtl-exp-prj" { @@ -157,4 +169,8 @@ module "dtl-exp-prj" { "storage.googleapis.com", "storage-component.googleapis.com" ]) + service_encryption_key_ids = { + bq = [try(var.service_encryption_keys.bq, null)] + storage = [try(var.service_encryption_keys.storage, null)] + } } diff --git a/examples/data-solutions/dp-foundation/99-kms-main.tf b/examples/data-solutions/dp-foundation/99-kms-main.tf index 63dc109e..34d05e35 100644 --- a/examples/data-solutions/dp-foundation/99-kms-main.tf +++ b/examples/data-solutions/dp-foundation/99-kms-main.tf @@ -12,66 +12,68 @@ # See the License for the specific language governing permissions and # limitations under the License. -module "kms" { - count = var.cmek_encryption ? 1 : 0 - source = "../../../modules/kms" - project_id = module.lnd-prj.project_id - keyring = { - name = "${var.prefix}-keyring", - location = var.region - } - keys = { - key-bq = null - key-cmp = null - key-df = null - key-gcs = null - key-ps = null - } - key_iam = { - key-bq = { - "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ - "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", - "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", - "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", - "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", - "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", - ] - }, - key-cmp = { - "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ - "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", - "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", - "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", - "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", - "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", - "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", +# Uncomment this section and assigne key links accondingly if you want +# to create a project with KMS and KMS keys +# module "kms" { +# count = var.service_encryption_keys? 1 : 0 +# source = "../../../modules/kms" +# project_id = module.lnd-prj.project_id +# keyring = { +# name = "${var.prefix}-keyring", +# location = var.region +# } +# keys = { +# key-bq = null +# key-cmp = null +# key-df = null +# key-gcs = null +# key-ps = null +# } +# key_iam = { +# key-bq = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", +# ] +# }, +# key-cmp = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", - ] - }, - key-df = { - "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ - "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", - "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", - "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", - "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", - ] - } - key-gcs = { - "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ - "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", - "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", - "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", - "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", - "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", - "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", - "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", - "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", - ] - }, - key-ps = { - "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ - "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" - ] - } - } -} +# ] +# }, +# key-df = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", +# "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", +# ] +# } +# key-gcs = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", +# ] +# }, +# key-ps = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" +# ] +# } +# } +# } diff --git a/examples/data-solutions/dp-foundation/demo/README.md b/examples/data-solutions/dp-foundation/demo/README.md index e69de29b..86a96f8f 100644 --- a/examples/data-solutions/dp-foundation/demo/README.md +++ b/examples/data-solutions/dp-foundation/demo/README.md @@ -0,0 +1 @@ +gsutil \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 47b223cd..3d2efb60 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -12,10 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -variable "cmek_encryption" { - description = "Flag to enable CMEK on GCP resources created." - type = bool - default = false +variable "service_encryption_keys" { # service encription key + description = "Cloud KMS to use to encrypt different services. Key location should match service region." + type = object({ + bq = string + composer = string + dataflow = string + storage = string + pubsub = string + }) + default = null } variable "composer_config" { From 6d00ff8d0c92a7876c3d47d3287e6d6b3bdf0a86 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 20 Jan 2022 12:35:42 +0100 Subject: [PATCH 049/132] First draft: README --- .../dp-foundation/03-orc-composer.tf | 36 ++++---- .../dp-foundation/99-kms-main.tf | 79 ------------------ .../data-solutions/dp-foundation/README.md | 59 +++++++++++++ .../dp-foundation/demo/gcs2bq.py | 7 +- .../dp-foundation/images/kms_diagram.png | Bin 0 -> 20271 bytes 5 files changed, 82 insertions(+), 99 deletions(-) delete mode 100644 examples/data-solutions/dp-foundation/99-kms-main.tf create mode 100644 examples/data-solutions/dp-foundation/images/kms_diagram.png diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index 3be02bc3..ce3338f7 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -53,30 +53,32 @@ resource "google_composer_environment" "orc-cmp-0" { software_config { image_version = "composer-1.17.5-airflow-2.1.4" env_variables = { - DTL_L0_BQ_DATASET = module.dtl-0-bq-0.dataset_id - DTL_L1_BQ_DATASET = module.dtl-1-bq-0.dataset_id - DTL_L2_BQ_DATASET = module.dtl-2-bq-0.dataset_id + DTL_EXP_PRJ = module.dtl-0-prj.project_id DTL_EXP_BQ_DATASET = module.dtl-exp-bq-0.dataset_id - DTL_L0_GCS = module.dtl-0-cs-0.url - DTL_L1_GCS = module.dtl-1-cs-0.url - DTL_L2_GCS = module.dtl-2-cs-0.url DTL_EXP_GCS = module.dtl-exp-cs-0.url - LND_GCS = module.lnd-cs-0.url - LND_BQ = module.lnd-bq-0.dataset_id - LND_PS = module.lnd-ps-0.id - LOD_GCS_STAGING = module.lod-cs-df-0.url - TRF_GCS_STAGING = module.trf-cs-df-0.url + DTL_L0_PRJ = module.dtl-0-prj.project_id + DTL_L0_BQ_DATASET = module.dtl-0-bq-0.dataset_id + DTL_L0_GCS = module.dtl-0-cs-0.url + DTL_L1_PRJ = module.dtl-1-prj.project_id + DTL_L1_BQ_DATASET = module.dtl-1-bq-0.dataset_id + DTL_L1_GCS = module.dtl-1-cs-0.url + DTL_L2_PRJ = module.dtl-2-prj.project_id + DTL_L2_BQ_DATASET = module.dtl-2-bq-0.dataset_id + DTL_L2_GCS = module.dtl-2-cs-0.url GCP_REGION = var.composer_config.region + LND_PRJ = module.lnd-prj.project_id + LND_BQ = module.lnd-bq-0.dataset_id + LND_GCS = module.lnd-cs-0.url + LND_PS = module.lnd-ps-0.id + LOD_PRJ = module.lod-prj.project_id + LOD_GCS_STAGING = module.lod-cs-df-0.url + LOD_SA_DF = module.lod-sa-df-0.email NET_VPC = module.orc-vpc[0].self_link NET_SUBNET = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/subnet"] - DTL_L0_PRJ = module.dtl-0-prj.project_id - DTL_L1_PRJ = module.dtl-1-prj.project_id - DTL_L2_PRJ = module.dtl-2-prj.project_id - LND_PRJ = module.lnd-prj.project_id - LOD_PRJ = module.lod-prj.project_id ORC_PRJ = module.orc-prj.project_id + ORC_GCS = module.orc-cs-0.url TRF_PRJ = module.trf-prj.project_id - LOD_SA_DF = module.lod-sa-df-0.email + TRF_GCS_STAGING = module.trf-cs-df-0.url TRF_SA_DF = module.trf-sa-df-0.email TRF_SA_BQ = module.trf-sa-bq-0.email } diff --git a/examples/data-solutions/dp-foundation/99-kms-main.tf b/examples/data-solutions/dp-foundation/99-kms-main.tf deleted file mode 100644 index 34d05e35..00000000 --- a/examples/data-solutions/dp-foundation/99-kms-main.tf +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Uncomment this section and assigne key links accondingly if you want -# to create a project with KMS and KMS keys -# module "kms" { -# count = var.service_encryption_keys? 1 : 0 -# source = "../../../modules/kms" -# project_id = module.lnd-prj.project_id -# keyring = { -# name = "${var.prefix}-keyring", -# location = var.region -# } -# keys = { -# key-bq = null -# key-cmp = null -# key-df = null -# key-gcs = null -# key-ps = null -# } -# key_iam = { -# key-bq = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", -# ] -# }, -# key-cmp = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", - -# ] -# }, -# key-df = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", -# "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", -# ] -# } -# key-gcs = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", -# ] -# }, -# key-ps = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" -# ] -# } -# } -# } diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 97aaadee..11a0d26d 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -1,3 +1,62 @@ +# Data Platform + +This module implement an opinionated Data Platform (DP) that create and set up projects (and related resources) to be used for your workloads. + +# Design overview and choices #TODO +Diagram and introduction +## Project structure #TODO +One prj per data stage. + +## Roles +Assigned at PRJ level +## Service accounts #TODO +- Service account with minimal roles +## Groups #TODO +Describe here groups to configure and their role: +- Data Eng +- Data Analyst +## VPC design #TODO +Internal: one VPC per prj, where neede (lod, trf, ) +## IP ranges, subnetting #TODO +List subnets and ranges. +How to rely on Shared-VPC + +## Resource naming convention #TODO + +## Encryption +We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. + +![Centralized Cloud KMS high level diagram](diagram.png "GCS to Biquery High-level diagram") + +To configure the use of Cloud KMS on resources you have to specify key URL on the 'service_encryption_keys'. Key location should match the resource location. Example: + +``` +service_encryption_keys = { + bq = "KEY_URL_MULTIREGIONAL" + composer = "KEY_URL_REGIONAL" + dataflow = "KEY_URL_REGIONAL" + storage = "KEY_URL_MULTIREGIONAL" + pubsub = "KEY_URL_MULTIREGIONAL" +``` + +We consider this step optional, it depend on customer policy and security best practices. + +# How to run this script #TODO +The Data Prlatform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: +* **Org level** + * TODO +* **Cloud KMS Keys** (if Cloud KMS keys are configured) + * TODO +* **Network** (if DP needs to rely on an existing Shared-VPC) + * TODO + +# Variable configuration #TODO + +# Customizations #TODO +Add internal KMS? +Parallel workstream + +# RAW notes, TO BE delete - GCS and BQ regional - KMS: Regional keyring, one key per product - Composer require "Require OS Login" not enforced diff --git a/examples/data-solutions/dp-foundation/demo/gcs2bq.py b/examples/data-solutions/dp-foundation/demo/gcs2bq.py index 50c34f06..b959f3e0 100644 --- a/examples/data-solutions/dp-foundation/demo/gcs2bq.py +++ b/examples/data-solutions/dp-foundation/demo/gcs2bq.py @@ -32,6 +32,7 @@ from airflow.providers.google.cloud.operators.bigquery import BigQueryInsertJob # -------------------------------------------------------------------------------- LND_GCS = os.environ.get("LND_GCS") +ORC_GCS = os.environ.get("ORC_GCS") LOD_GCS_STAGING = os.environ.get("LOD_GCS_STAGING") DTL_L0_BQ_DATASET = os.environ.get("DTL_L0_BQ_DATASET") DTL_L0_PRJ = os.environ.get("DTL_L0_PRJ") @@ -100,7 +101,7 @@ with models.DAG( template="gs://dataflow-templates/latest/GCS_Text_to_BigQuery", parameters={ "javascriptTextTransformFunctionName": "transform", - "JSONPath": LND_GCS + "/customers_schema.json", + "JSONPath": ORC_GCS + "/customers_schema.json", "javascriptTextTransformGcsPath": LND_GCS + "/customers_udf.js", "inputFilePattern": LND_GCS + "/customers.csv", "outputTable": DTL_L0_PRJ + ":"+DTL_L0_BQ_DATASET+".customers", @@ -113,8 +114,8 @@ with models.DAG( template="gs://dataflow-templates/latest/GCS_Text_to_BigQuery", parameters={ "javascriptTextTransformFunctionName": "transform", - "JSONPath": LND_GCS + "/purchases_schema.json", - "javascriptTextTransformGcsPath": LND_GCS + "/purchases_udf.js", + "JSONPath": ORC_GCS + "/purchases_schema.json", + "javascriptTextTransformGcsPath": ORC_GCS + "/purchases_udf.js", "inputFilePattern": LND_GCS + "/purchases.csv", "outputTable": DTL_L0_PRJ + ":"+DTL_L0_BQ_DATASET+".purchases", "bigQueryLoadingTemporaryDirectory": LOD_GCS_STAGING + "/tmp/bq/", diff --git a/examples/data-solutions/dp-foundation/images/kms_diagram.png b/examples/data-solutions/dp-foundation/images/kms_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ef9a6e4b4415985b46157e63a3db0655f209a0 GIT binary patch literal 20271 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 M27iL^`j2D(1rQ;eQ~&?~ literal 0 HcmV?d00001 From d2b752f21520caaac70a994dfa8777c73ac32018 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 21 Jan 2022 17:27:06 +0100 Subject: [PATCH 050/132] Update README --- .../data-solutions/dp-foundation/README.md | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 11a0d26d..9a00189b 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -4,11 +4,26 @@ This module implement an opinionated Data Platform (DP) that create and set up p # Design overview and choices #TODO Diagram and introduction -## Project structure #TODO -One prj per data stage. +## Project structure +The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different +stages of the data journey and rely on project level roles. + +The following projects will be created: +* **Landing** This Project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. + +* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or a later stage. + +* **Data Lake** This project is intended to store your data. It reppresents where data will be persisted within 3 Layers. These layers reppresent different stages where data is processed and progressivly refined + * **L0 - Raw data** Structured Data, stored in adeguate format: structured data stored in bigquery, unstructured data stored on Cloud Storage with additional metadata stored in Bigquery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in Bigquery). + * **L1 - Cleansed, aggregated and standardized data** + * **L2 - Curated layer** + * **Experimental** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers +* **Orchestration** This project is inteded to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. +* **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest to rely on BigQuery engine to perform transformation. If Bigquery do not have the feature needed to perform your transformation you suggest to use Clud Dataflow. +* **Exposure** This project is intended to host resources to expose your data. To expose Bigquery data, we strongly suggest to rely on Authorized views. Other resources may better fit on particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed on use case where you need low latency to access data. ## Roles -Assigned at PRJ level +Roles will be granted at Project level. ## Service accounts #TODO - Service account with minimal roles ## Groups #TODO @@ -64,8 +79,6 @@ Parallel workstream #TODO KMS: support key per product #TODO Write README - #TODO Run a working test - #TODO Write a working e2e test #TODO Column level access on BQ #TODO DataCatalog #TODO DLP From 53a6809d21fcb6b1a1a56b55fd3904c5d5ce5e20 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 21 Jan 2022 18:40:11 +0100 Subject: [PATCH 051/132] Add DLP, update README --- .../dp-foundation/02-lod-main.tf | 1 + .../dp-foundation/04-trf-main.tf | 1 + .../dp-foundation/06-sec-main.tf | 120 ++++++++++++++++++ .../data-solutions/dp-foundation/README.md | 14 +- .../dp-foundation/images/dlp_diagram.png | Bin 0 -> 16995 bytes .../data-solutions/dp-foundation/variables.tf | 3 + 6 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 examples/data-solutions/dp-foundation/06-sec-main.tf create mode 100644 examples/data-solutions/dp-foundation/images/dlp_diagram.png diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index fa5b318f..c413430a 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -68,6 +68,7 @@ module "lod-prj" { "cloudkms.googleapis.com", "compute.googleapis.com", "dataflow.googleapis.com", + "dlp.googleapis.com", "pubsub.googleapis.com", "servicenetworking.googleapis.com", "storage.googleapis.com", diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index f5ae2ab1..9170665f 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -64,6 +64,7 @@ module "trf-prj" { "cloudkms.googleapis.com", "compute.googleapis.com", "dataflow.googleapis.com", + "dlp.googleapis.com", "pubsub.googleapis.com", "servicenetworking.googleapis.com", "storage.googleapis.com", diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf new file mode 100644 index 00000000..d3707aba --- /dev/null +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -0,0 +1,120 @@ +# 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. + +locals { + group_iam_sec = { + "${local.groups.data-engineers}" = [ + "roles/dlp.reader", + "roles/dlp.user", + "roles/dlp.estimatesAdmin", + ], + "${local.groups.data-security}" = [ + "roles/dlp.admin", + ], + } + iam_sec = { + "roles/dlp.user" = [ + module.lod-sa-df-0.iam_email, + module.trf-sa-df-0.iam_email + ] + } + prefix_sec = "${var.prefix}-sec" +} + +############################################################################### +# Project # +############################################################################### + +module "sec-prj" { + source = "../../../modules/project" + name = var.project_id["security"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_trf : {} + iam_additive = var.project_create == null ? local.iam_trf : {} + group_iam = local.group_iam_trf + services = concat(var.project_services, [ + "dlp.googleapis.com", + ]) +} + +# Uncomment this section and assigne key links accondingly if you want +# to create a project with KMS and KMS keys + +# module "kms" { +# count = var.service_encryption_keys? 1 : 0 +# source = "../../../modules/kms" +# project_id = module.lnd-prj.project_id +# keyring = { +# name = "${var.prefix}-keyring", +# location = var.region +# } +# keys = { +# key-bq = null +# key-cmp = null +# key-df = null +# key-gcs = null +# key-ps = null +# } +# key_iam = { +# key-bq = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", +# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", +# ] +# }, +# key-cmp = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", + +# ] +# }, +# key-df = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", +# "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", +# ] +# } +# key-gcs = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", +# "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", +# ] +# }, +# key-ps = { +# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ +# "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" +# ] +# } +# } +# } diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 9a00189b..34e598d8 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -30,6 +30,7 @@ Roles will be granted at Project level. Describe here groups to configure and their role: - Data Eng - Data Analyst +- Data Security ## VPC design #TODO Internal: one VPC per prj, where neede (lod, trf, ) ## IP ranges, subnetting #TODO @@ -41,7 +42,7 @@ How to rely on Shared-VPC ## Encryption We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. -![Centralized Cloud KMS high level diagram](diagram.png "GCS to Biquery High-level diagram") +![Centralized Cloud KMS high level diagram](.images/kms_diagram.png "Centralized Cloud KMS high level diagram") To configure the use of Cloud KMS on resources you have to specify key URL on the 'service_encryption_keys'. Key location should match the resource location. Example: @@ -56,6 +57,15 @@ service_encryption_keys = { We consider this step optional, it depend on customer policy and security best practices. +# Data Anonymization +We suggest the use of Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. The implementation of the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to let you implement the DLP strategy. We expect you will use DLP templates in one of the following way: +- During the ingestion phase, from Dataflow +- During the transformation phase, from BigQuery or Dataflow + +We implemented a centralized model for Data Loss Prevention material. Templates will be stored in the security project: + +![Centralized Cloud DLP high level diagram](./images/dlp_diagram.png "Centralized Cloud DLP high level diagram") + # How to run this script #TODO The Data Prlatform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: * **Org level** @@ -75,7 +85,7 @@ Parallel workstream - GCS and BQ regional - KMS: Regional keyring, one key per product - Composer require "Require OS Login" not enforced - - Groups: gcp-data-scientists, gcp-data-engineers + - Groups: gcp-data-scientists, gcp-data-engineers, gcp-data-security #TODO KMS: support key per product #TODO Write README diff --git a/examples/data-solutions/dp-foundation/images/dlp_diagram.png b/examples/data-solutions/dp-foundation/images/dlp_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e2b229b9d6222f147c25021e0c7e9187eb3c04ce GIT binary patch literal 16995 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 DX+x(s literal 0 HcmV?d00001 diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 3d2efb60..9d18b86d 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -59,6 +59,7 @@ variable "groups" { default = { data-engineers = "gcp-data-engineers" data-scientists = "gcp-data-scientists" + data-security = "gcp-data-security" } } @@ -111,6 +112,7 @@ variable "project_id" { orchestration = string trasformation = string datalake = string + security = string }) default = { landing = "lnd" @@ -118,6 +120,7 @@ variable "project_id" { orchestration = "orc" trasformation = "trf" datalake = "dtl" + security = "sec" } } From 4ec5860caf82db54d4888e9c4500e97f5d742557 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 26 Jan 2022 14:51:01 +0100 Subject: [PATCH 052/132] Update Readme --- .../dp-foundation/01-lnd-main.tf.tf | 13 +++--- .../data-solutions/dp-foundation/README.md | 37 +++++++++++++++--- .../dp-foundation/images/dlp_diagram.png | Bin 16995 -> 28056 bytes .../dp-foundation/images/overview_diagram.png | Bin 0 -> 64758 bytes 4 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 examples/data-solutions/dp-foundation/images/overview_diagram.png diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 0c4a59e8..16155cfa 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -19,14 +19,13 @@ locals { "roles/pubsub.editor", "roles/storage.admin", "roles/storage.objectViewer", - "roles/viewer", ], - # "${local.groups.data-scientists}" = [ - # "roles/bigquery.dataViewer", - # "roles/bigquery.jobUser", - # "roles/bigquery.user", - # "roles/pubsub.viewer", - # ] + "${local.groups.data-scientists}" = [ + "roles/bigquery.dataViewer", + "roles/bigquery.jobUser", + "roles/bigquery.user", + "roles/pubsub.viewer", + ] } iam_lnd = { "roles/bigquery.dataEditor" = [ diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 34e598d8..5349c63f 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -3,10 +3,12 @@ This module implement an opinionated Data Platform (DP) that create and set up projects (and related resources) to be used for your workloads. # Design overview and choices #TODO -Diagram and introduction +This is the Data Platform architecture we are going to deploy. + +![Data Platform Architecture overview](./images/overview_diagram.png "Data Platform Architecture overview") + ## Project structure -The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different -stages of the data journey and rely on project level roles. +The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different stages of the data journey and rely on project level roles. The following projects will be created: * **Landing** This Project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. @@ -23,8 +25,33 @@ The following projects will be created: * **Exposure** This project is intended to host resources to expose your data. To expose Bigquery data, we strongly suggest to rely on Authorized views. Other resources may better fit on particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed on use case where you need low latency to access data. ## Roles -Roles will be granted at Project level. +We assigned roles on resources at Project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. + +The following roles where assigned to different groups: + +| Group | Landing | Load | Data Lake - L0 | Data Lake - L1 | Data Lake - L2 | Data Lake - Exposure | +| :----- | ------- | ---- | -------------- | -------------- | -------------- | -------------------- | +| gcp-data-scientists | | | | | | | | +| gcp-data-engineers | | | | | | | | +| gcp-data-security | | | | | | | + + +The following roles where assigned to different Service Accounts: + + ## Service accounts #TODO +Service Account creation follow the following principals: +- Each service account perform a single task aving access to the minimun number of projects (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) +- Each Service Account has least privilage on each project. + +### Service Account Keys +Service Account Keys (SAK) are out of scope for this example. The example implemented rely on Service Account Impersonification avoiding the creation of SAK. + +The use of SAK within a data pipeline incurs several security risks, as these are physical credentials, matched to an automated system, that can be distributed without oversight or control. + +Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identify a structured process to mitigate risks associated with the use of service account keys. + + - Service account with minimal roles ## Groups #TODO Describe here groups to configure and their role: @@ -42,7 +69,7 @@ How to rely on Shared-VPC ## Encryption We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. -![Centralized Cloud KMS high level diagram](.images/kms_diagram.png "Centralized Cloud KMS high level diagram") +![Centralized Cloud KMS high level diagram](./images/kms_diagram.png "Centralized Cloud KMS high level diagram") To configure the use of Cloud KMS on resources you have to specify key URL on the 'service_encryption_keys'. Key location should match the resource location. Example: diff --git a/examples/data-solutions/dp-foundation/images/dlp_diagram.png b/examples/data-solutions/dp-foundation/images/dlp_diagram.png index e2b229b9d6222f147c25021e0c7e9187eb3c04ce..65467b2bed4fa2e4b73cbd95363a0228eadb52b4 100644 GIT binary patch literal 28056 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 DX+x(s diff --git a/examples/data-solutions/dp-foundation/images/overview_diagram.png b/examples/data-solutions/dp-foundation/images/overview_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..093343f50c5d30e1da48b4be7754646590ec4a4b GIT binary patch literal 64758 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 Xaa`Qf#Xg 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 Date: Wed, 26 Jan 2022 17:58:17 +0100 Subject: [PATCH 053/132] README --- .../data-solutions/dp-foundation/README.md | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 5349c63f..ccdc4a97 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -1,6 +1,6 @@ # Data Platform -This module implement an opinionated Data Platform (DP) that create and set up projects (and related resources) to be used for your workloads. +This module implement an opinionated Data Platform (DP) Architecture that create and set up projects (and related resources) to be used for your data workloads. # Design overview and choices #TODO This is the Data Platform architecture we are going to deploy. @@ -13,9 +13,9 @@ The DP is designed to rely on several projects, one prj per data stage. This is The following projects will be created: * **Landing** This Project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. -* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or a later stage. +* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transofmation stage depending on your requirements. -* **Data Lake** This project is intended to store your data. It reppresents where data will be persisted within 3 Layers. These layers reppresent different stages where data is processed and progressivly refined +* **Data Lake** Those projects is intended to store your data. It reppresents where data will be persisted within 3 Layers. These layers reppresent different stages where data is processed and progressivly refined * **L0 - Raw data** Structured Data, stored in adeguate format: structured data stored in bigquery, unstructured data stored on Cloud Storage with additional metadata stored in Bigquery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in Bigquery). * **L1 - Cleansed, aggregated and standardized data** * **L2 - Curated layer** @@ -27,22 +27,12 @@ The following projects will be created: ## Roles We assigned roles on resources at Project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. -The following roles where assigned to different groups: - -| Group | Landing | Load | Data Lake - L0 | Data Lake - L1 | Data Lake - L2 | Data Lake - Exposure | -| :----- | ------- | ---- | -------------- | -------------- | -------------- | -------------------- | -| gcp-data-scientists | | | | | | | | -| gcp-data-engineers | | | | | | | | -| gcp-data-security | | | | | | | - - -The following roles where assigned to different Service Accounts: - +The only exception is for BigQuery dataset. We let you configure IAM at dataset level to host in the same infrastructure dataset that need access level segregation. ## Service accounts #TODO Service Account creation follow the following principals: -- Each service account perform a single task aving access to the minimun number of projects (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) -- Each Service Account has least privilage on each project. +- Each service account perform a single task aving access to the minimun number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) +- Each Service Account has least privilage on each project. ### Service Account Keys Service Account Keys (SAK) are out of scope for this example. The example implemented rely on Service Account Impersonification avoiding the creation of SAK. @@ -52,17 +42,24 @@ The use of SAK within a data pipeline incurs several security risks, as these ar Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identify a structured process to mitigate risks associated with the use of service account keys. -- Service account with minimal roles ## Groups #TODO -Describe here groups to configure and their role: -- Data Eng -- Data Analyst -- Data Security +As default groups, we identified the following actors: +- *Data Engineers*: the group that handle and run the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issue with the pipeline. The team has also the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. +- *Data Analyst*: the group that perform analysis on the dataset. The group has Read access to the Data Lake L2 project and Bigquery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` +- *Data Security*: the project that handle security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` ## VPC design #TODO -Internal: one VPC per prj, where neede (lod, trf, ) +The DP except as input an existing Shared-VPC to run resources. You can configure subsets for DP resource specifying the link to the subnet in the `` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. + +If no VPC configuration, the project will create a VPC on each project that require a VPC: *laod* project, *trasformation* project and *orchestration* project. ## IP ranges, subnetting #TODO -List subnets and ranges. -How to rely on Shared-VPC +To run your DP resources you need the following ranges: +- Load project VPC for Dataflow. Range: '/24'. +- Transformation VPC for Dataflow. Range: '/24'. +- Orchestration VPC for Cloud Composer: + - Cloud SQL. Range: '/24' + - GKE Master. Range: '/28' + - Web Server: Range: '/28' + - Secondary ip ranges. Pods range: '/22', Services range: '/24' ## Resource naming convention #TODO From a0311affa89693a6092fea5e401209ad9332acf7 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 28 Jan 2022 16:16:25 +0100 Subject: [PATCH 054/132] Add todos --- examples/data-solutions/dp-foundation/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index ccdc4a97..fcbbe988 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -2,11 +2,16 @@ This module implement an opinionated Data Platform (DP) Architecture that create and set up projects (and related resources) to be used for your data workloads. -# Design overview and choices #TODO -This is the Data Platform architecture we are going to deploy. - ![Data Platform Architecture overview](./images/overview_diagram.png "Data Platform Architecture overview") +# Design overview and choices #TODO +This is the Data Platform architecture we are going to deploy. +#TODO Add introduction +- boundaries for each step +- help identify actors +- help assign minimal roles + +#TODO Rename secutiry to Core services ## Project structure The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different stages of the data journey and rely on project level roles. @@ -41,7 +46,6 @@ The use of SAK within a data pipeline incurs several security risks, as these ar Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identify a structured process to mitigate risks associated with the use of service account keys. - ## Groups #TODO As default groups, we identified the following actors: - *Data Engineers*: the group that handle and run the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issue with the pipeline. The team has also the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. @@ -102,6 +106,7 @@ The Data Prlatform is meant to be executed by a Service Account (or a regular us # Variable configuration #TODO # Customizations #TODO +Variables with default Add internal KMS? Parallel workstream From 1f8ad06b8d1bf8cc8c8072602b50b16e287f03bf Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 28 Jan 2022 17:06:36 +0100 Subject: [PATCH 055/132] Merge master --- fast/stages/01-resman/README.md | 46 --------- .../data/subnets/dev/dev-default-ew1.yaml | 4 - .../data/subnets/prod/prod-default-ew1.yaml | 4 - fast/stages/03-project-factory/README.md | 7 -- fast/stages/03-project-factory/prod/README.md | 79 --------------- .../prod/data/defaults.yaml.sample | 22 ----- .../prod/data/projects/project.yaml.sample | 99 ------------------- tests/fast/README.md | 34 ------- tests/fast/stages/s00_bootstrap/test_plan.py | 7 -- tests/fast/stages/s01_resman/test_plan.py | 6 -- tests/fast/stages/s02_networking/test_plan.py | 6 -- tests/fast/stages/s02_security/test_plan.py | 6 -- .../stages/s03_project_factory/test_plan.py | 6 -- 13 files changed, 326 deletions(-) delete mode 100644 fast/stages/03-project-factory/prod/data/defaults.yaml.sample delete mode 100644 fast/stages/03-project-factory/prod/data/projects/project.yaml.sample delete mode 100644 tests/fast/README.md diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md index a4543cb9..098c6506 100644 --- a/fast/stages/01-resman/README.md +++ b/fast/stages/01-resman/README.md @@ -9,13 +9,9 @@ The code is intentionally simple, as it's intended to provide a generic initial The following diagram is a high level reference of the resources created and managed here: -<<<<<<< HEAD -![Resource-management diagram](diagram.png) -=======

Resource-management diagram

->>>>>>> master ## Design overview and choices @@ -142,13 +138,7 @@ IAM roles can be easily edited in the relevant `branch-xxx.tf` file, following t Due to its simplicity, this stage lends itself easily to customizations: adding a new top-level branch (e.g. for shared GKE clusters) is as easy as cloning one of the `branch-xxx.tf` files, and changing names. -<<<<<<< HEAD - - - -======= ->>>>>>> master ## Files @@ -156,10 +146,6 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | name | description | modules | resources | |---|---|---|---| | [billing.tf](./billing.tf) | Billing resources for external billing use cases. | organization | google_billing_account_iam_member | -<<<<<<< HEAD -| [branch-gke.tf](./branch-gke.tf) | GKE stage resources. | folder · gcs · iam-service-account | | -======= ->>>>>>> master | [branch-networking.tf](./branch-networking.tf) | Networking stage resources. | folder · gcs · iam-service-account | | | [branch-sandbox.tf](./branch-sandbox.tf) | Sandbox stage resources. | folder · gcs · iam-service-account | | | [branch-security.tf](./branch-security.tf) | Security stage resources. | folder · gcs · iam-service-account | | @@ -173,17 +159,6 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -<<<<<<< HEAD -| automation_project_id | Project id for the automation project created by the bootstrap stage. | string | ✓ | | 00-bootstrap | -| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | -| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | -| custom_roles | Custom roles defined at the org level, in key => id format. | map(string) | | {} | 00-bootstrap | -| groups | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | -| organization_policy_configs | Organization policies customization. | object({…}) | | null | | -| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| team_folders | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | -======= | [automation_project_id](variables.tf#L29) | Project id for the automation project created by the bootstrap stage. | string | ✓ | | 00-bootstrap | | [billing_account](variables.tf#L20) | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | | [organization](variables.tf#L57) | Organization details. | object({…}) | ✓ | | 00-bootstrap | @@ -193,31 +168,11 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | [organization_policy_configs](variables.tf#L67) | Organization policies customization. | object({…}) | | null | | | [outputs_location](variables.tf#L75) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | | [team_folders](variables.tf#L87) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | ->>>>>>> master ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -<<<<<<< HEAD -| networking | Data for the networking stage. | | 02-networking | -| project_factories | Data for the project factories stage. | | xx-teams | -| providers | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · xx-sandbox · xx-teams | -| sandbox | Data for the sandbox stage. | | xx-sandbox | -| security | Data for the networking stage. | | 02-security | -| teams | Data for the teams stage. | | | -| tfvars | Terraform variable files for the following stages. | ✓ | | - - - - - - - - - - -======= | [networking](outputs.tf#L79) | Data for the networking stage. | | 02-networking | | [project_factories](outputs.tf#L89) | Data for the project factories stage. | | xx-teams | | [providers](outputs.tf#L106) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · xx-sandbox · xx-teams | @@ -227,4 +182,3 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | [tfvars](outputs.tf#L146) | Terraform variable files for the following stages. | ✓ | | ->>>>>>> master diff --git a/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml index 859db0c3..be0d8f8b 100644 --- a/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml +++ b/fast/stages/02-networking/data/subnets/dev/dev-default-ew1.yaml @@ -1,9 +1,5 @@ # skip boilerplate check region: europe-west1 -<<<<<<< HEAD -ip_cidr_range: 10.144.0.0/24 -======= ip_cidr_range: 10.128.16.0/24 ->>>>>>> master description: Default subnet for dev diff --git a/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml index f1732554..4f0a8695 100644 --- a/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml +++ b/fast/stages/02-networking/data/subnets/prod/prod-default-ew1.yaml @@ -1,9 +1,5 @@ # skip boilerplate check region: europe-west1 -<<<<<<< HEAD -ip_cidr_range: 10.136.0.0/24 -======= ip_cidr_range: 10.128.128.0/24 ->>>>>>> master description: Default subnet for prod diff --git a/fast/stages/03-project-factory/README.md b/fast/stages/03-project-factory/README.md index 02b7bc7b..44b96b9a 100644 --- a/fast/stages/03-project-factory/README.md +++ b/fast/stages/03-project-factory/README.md @@ -1,13 +1,6 @@ # Project factory -<<<<<<< HEAD -The Project Factory (or PF) builds on top of your foundations to create and setup projects (and related resources) to be used for your workloads. -It is organized in folders representing enviroments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). - -This directory contains a single project factory ([`prod/`](./prod/)) as an example - in order to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). -======= The Project Factory (PF) builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). This directory contains a single project factory ([`prod/`](./prod/)) as an example - to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). ->>>>>>> master diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md index 8c0665ef..34f9be88 100644 --- a/fast/stages/03-project-factory/prod/README.md +++ b/fast/stages/03-project-factory/prod/README.md @@ -1,17 +1,6 @@ # Project factory The Project Factory (or PF) builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. -<<<<<<< HEAD -It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). - -## Design overview and choices - -![Diagram](diagram.png) - -A single factory creates projects in a well-defined context, according to your resource management structure. In the diagram above, each Team is structured to have specific folders projects for a given environment, such as Production and Development, per the resource management structure configured in stage `01-resman`. - -Projects for each environment across different teams are created by dedicated service accounts, as exemplified in the diagram above. While there's no intrinsic limitation regarding where the project factory can create a given project, the IAM bindings for the service account effectively enforce boundaries (e.g. the production service account shouldn't be able to create or have any access to the development projects, and vice versa). -======= It is organized in folders representing environments (e.g., "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). ## Design overview and choices @@ -23,7 +12,6 @@ It is organized in folders representing environments (e.g., "dev", "prod"), each A single factory creates projects in a well-defined context, according to your resource management structure. For example, in the diagram above, each Team is structured to have specific folders projects for a given environment, such as Production and Development, per the resource management structure configured in stage `01-resman`. Projects for each environment across different teams are created by dedicated service accounts, as exemplified in the diagram above. While there's no intrinsic limitation regarding where the project factory can create a projects, the IAM bindings for the service account effectively enforce boundaries (e.g., the production service account shouldn't be able to create or have any access to the development projects, and vice versa). ->>>>>>> master The project factory takes care of the following activities: @@ -41,15 +29,9 @@ The project factory takes care of the following activities: ## How to run this stage -<<<<<<< HEAD -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: -======= 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: ->>>>>>> master * One service account per environment, each with appropriate permissions * at the organization level a custom role for networking operations including the following permissions @@ -58,11 +40,7 @@ It's of course possible to run this stage in isolation, by making sure the archi * `"compute.subnetworks.setIamPolicy"`, * `"dns.networks.bindPrivateDNSZone"` * and role `"roles/orgpolicy.policyAdmin"` -<<<<<<< HEAD - * on each folder where projects will be created -======= * on each folder where projects are created ->>>>>>> master * `"roles/logging.admin"` * `"roles/owner"` * `"roles/resourcemanager.folderAdmin"` @@ -71,21 +49,12 @@ It's of course possible to run this stage in isolation, by making sure the archi * `"roles/browser"` * `"roles/compute.viewer"` * `"roles/dns.admin"` -<<<<<<< HEAD -* If networking is to be 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 provider file, and populate the required variables from the previous stage. -======= * 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. ->>>>>>> master ```bash # Variable `outputs_location` is set to `../../configs/example` in stage 01-resman @@ -110,19 +79,6 @@ ln -s ../../../configs/example/03-project-factory-prod/terraform-bootstrap.auto. ln -s ../../../configs/example/03-project-factory-prod/terraform-networking.auto.tfvars.json ``` -<<<<<<< HEAD -If you're not running on top of 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: - - -* `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 generate project `fast-prod-lab0`. - -Once the configuration is complete, the project factory can be run with the usual -======= 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: @@ -132,73 +88,38 @@ Besides the values above, a project factory takes 2 additional inputs: * `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 ->>>>>>> master ```bash terraform init terraform apply ``` -<<<<<<< HEAD - - - - -======= ->>>>>>> master ## Files -<<<<<<< HEAD -| name | description | modules | resources | -|---|---|---|---| -| [main.tf](./main.tf) | Project factory. | project-factory | | -| [outputs.tf](./outputs.tf) | Module outputs. | | | -| [variables.tf](./variables.tf) | Module variables. | | | -======= | name | description | modules | |---|---|---| | [main.tf](./main.tf) | Project factory. | project-factory | | [outputs.tf](./outputs.tf) | Module outputs. | | | [variables.tf](./variables.tf) | Module variables. | | ->>>>>>> master ## Variables | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -<<<<<<< HEAD -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| shared_vpc_self_link | Self link for the shared VPC. | string | ✓ | | 02-networking | -| vpc_host_project | Host project for the shared VPC. | string | ✓ | | 02-networking | -| data_dir | Relative path for the folder storing configuration data. | string | | "data/projects" | | -| defaults_file | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | -| environment_dns_zone | DNS zone suffix for environment. | string | | null | 02-networking | -======= | [billing_account_id](variables.tf#L19) | Billing account id. | string | ✓ | | 00-bootstrap | | [shared_vpc_self_link](variables.tf#L44) | Self link for the shared VPC. | string | ✓ | | 02-networking | | [vpc_host_project](variables.tf#L50) | Host project for the shared VPC. | string | ✓ | | 02-networking | | [data_dir](variables.tf#L25) | Relative path for the folder storing configuration data. | string | | "data/projects" | | | [defaults_file](variables.tf#L38) | Relative path for the file storing the project factory configuration. | string | | "data/defaults.yaml" | | | [environment_dns_zone](variables.tf#L31) | DNS zone suffix for environment. | string | | null | 02-networking | ->>>>>>> master ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -<<<<<<< HEAD -| projects | Created projects and service accounts. | | | - - - - - - -======= | [projects](outputs.tf#L17) | Created projects and service accounts. | | | ->>>>>>> master diff --git a/fast/stages/03-project-factory/prod/data/defaults.yaml.sample b/fast/stages/03-project-factory/prod/data/defaults.yaml.sample deleted file mode 100644 index 577c4bba..00000000 --- a/fast/stages/03-project-factory/prod/data/defaults.yaml.sample +++ /dev/null @@ -1,22 +0,0 @@ -billing_account_id: 012345-67890A-BCDEF0 - -# [opt] Setup for billing alerts -billing_alert: - amount: 1000 - thresholds: - current: [0.5, 0.8] - forecasted: [0.5, 0.8] - credit_treatment: INCLUDE_ALL_CREDITS - -# [opt] Contacts for billing alerts and important notifications -essential_contacts: ["team-contacts@example.com"] - -# [opt] Labels set for all projects -labels: - environment: prod - department: accounting - application: example-app - foo: bar - -# [opt] Additional notification channels for billing -notification_channels: [] \ No newline at end of file diff --git a/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample b/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample deleted file mode 100644 index 7994e7f4..00000000 --- a/fast/stages/03-project-factory/prod/data/projects/project.yaml.sample +++ /dev/null @@ -1,99 +0,0 @@ -# [opt] Billing account id - overrides default if set -billing_account_id: 012345-67890A-BCDEF0 - -# [opt] Billing alerts config - overrides default if set -billing_alert: - amount: 10 - thresholds: - current: - - 0.5 - - 0.8 - forecasted: [] - -# [opt] DNS zones to be created as children of the environment_dns_zone defined in defaults -dns_zones: - - lorem - - ipsum - -# [opt] Contacts for billing alerts and important notifications -essential_contacts: - - team-a-contacts@example.com - -# Folder the project will be created as children of -folder_id: folders/012345678901 - -# [opt] Authoritative IAM bindings in group => [roles] format -group_iam: - test-team-foobar@fast-lab-0.gcp-pso-italy.net: - - roles/compute.admin - -# [opt] Authoritative IAM bindings in role => [principals] format -# Generally used to grant roles to service accounts external to the project -iam: - roles/compute.admin: - - serviceAccount:service-account - -# [opt] Service robots and keys they will be assigned as cryptoKeyEncrypterDecrypter -# in service => [keys] format -kms_service_agents: - compute: [key1, key2] - storage: [key1, key2] - -# [opt] Labels for the project - merged with the ones defined in defaults -labels: - environment: prod - -# [opt] Org policy overrides defined at project level -org_policies: - policy_boolean: - constraints/compute.disableGuestAttributesAccess: true - policy_list: - constraints/compute.trustedImageProjects: - inherit_from_parent: null - status: true - suggested_value: null - values: - - projects/fast-prod-iac-core-0 - -# [opt] Service account to create for the project and their roles on the project -# in name => [roles] format -service_accounts: - another-service-account: - - roles/compute.admin - my-service-account: - - roles/compute.admin - -# [opt] APIs to enable on the project. -services: - - storage.googleapis.com - - stackdriver.googleapis.com - - compute.googleapis.com - -# [opt] Roles to assign to the robots service accounts in robot => [roles] format -services_iam: - compute: - - roles/storage.objectViewer - - # [opt] VPC setup. - # If set enables the `compute.googleapis.com` service and configures - # service project attachment -vpc: - - # [opt] If set, enables the container API - gke_setup: - - # Grants "roles/container.hostServiceAgentUser" to the container robot if set - enable_host_service_agent: false - - # Grants "roles/compute.securityAdmin" to the container robot if set - enable_security_admin: true - - # Host project the project will be service project of - host_project: fast-prod-net-spoke-0 - - # [opt] Subnets in the host project where principals will be granted networkUser - # in region/subnet-name => [principals] - subnets_iam: - europe-west1/prod-default-ew1: - - user:foobar@example.com - - serviceAccount:service-account1 \ No newline at end of file diff --git a/tests/fast/README.md b/tests/fast/README.md deleted file mode 100644 index 01ea3e69..00000000 --- a/tests/fast/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Fabric FAST - -Setting up a production-ready GCP organization is often a time-consuming process. Fabric FAST aims to speed up this process via two complementary goals. On the one hand, FAST provides a design of a GCP organization that includes the typical elements required by enterprise customers. Secondly, we provide a reference implementation of the FAST design using Terraform. - -Note that while our implementation is necessarily influenced (and constrained) by the way Terraform works, the design we put forward only refers to GCP constructs and features. In other words, while we use Terraform for our reference implementation, in theory, the FAST design can be implemented using any other tool (e.g., Pulumi, bash scripts, or even calling the relevant APIs directly). - -Fabric FAST comes from engineers in Google Cloud's Professional Services Organization, with a combined experience of decades solving the typical technical problems faced by GCP customers. While every GCP user has specific requirements, many common issues arise repeatedly. Solving those issues correctly from the beginning is key to a robust and scalable GCP setup. It's those common issues and their solutions that Fabric FAST aims to collect and present coherently. - -Fabric FAST was initially conceived to help enterprises quickly set up a GCP organization following battle-tested and widely-used patterns. Despite its origin in enterprise environments, FAST includes many customization points making it an ideal blueprint for organizations of all sizes, ranging from startups to the largest companies. - - -## Guiding principles -### Contracts and stages -FAST uses the concept of stages, which individually perform precise tasks but, taken together, build a functional, ready-to-use GCP organization. More importantly, stages are modeled around the security boundaries that typically appear in mature organizations. This arrangement allows delegating ownership of each stage to the team responsible for the types of resources it manages. For example, as its name suggests, the networking stage sets up all the networking elements and is usually the responsibility of a dedicated networking team within the organization. - -From the perspective of FAST's overall design, stages also work as contacts or interfaces, defining a set of pre-requisites and inputs required to perform their designed task and generating outputs needed by other stages lower in the chain. - -### Security-first design -Security was, from the beginning, one of the most critical elements in the design of Fabric FAST. Many of FAST's design decisions aim to build the foundations of a secure organization. In fact, the first two stages deal mainly with the organization-wide security setup. - -FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. - -### Extensive use of factories -A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. - -FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. - -## High level design - -TBD - -## Implementation - -TBD diff --git a/tests/fast/stages/s00_bootstrap/test_plan.py b/tests/fast/stages/s00_bootstrap/test_plan.py index 8bcbec7d..2201cfcc 100644 --- a/tests/fast/stages/s00_bootstrap/test_plan.py +++ b/tests/fast/stages/s00_bootstrap/test_plan.py @@ -26,15 +26,8 @@ # } -<<<<<<< HEAD -def test_counts(e2e_plan_runner): - "Test stage." - # TODO: to re-enable per-module resource count check print _, then test - num_modules, num_resources, _ = e2e_plan_runner() -======= def test_counts(fast_e2e_plan_runner): "Test stage." # TODO: to re-enable per-module resource count check print _, then test num_modules, num_resources, _ = fast_e2e_plan_runner() ->>>>>>> master assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s01_resman/test_plan.py b/tests/fast/stages/s01_resman/test_plan.py index cc261235..6189f62e 100644 --- a/tests/fast/stages/s01_resman/test_plan.py +++ b/tests/fast/stages/s01_resman/test_plan.py @@ -13,14 +13,8 @@ # limitations under the License. -<<<<<<< HEAD -def test_counts(e2e_plan_runner): - "Test stage." - num_modules, num_resources, _ = e2e_plan_runner() -======= def test_counts(fast_e2e_plan_runner): "Test stage." num_modules, num_resources, _ = fast_e2e_plan_runner() ->>>>>>> master # TODO: to re-enable per-module resource count check print _, then test assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_networking/test_plan.py b/tests/fast/stages/s02_networking/test_plan.py index cc261235..6189f62e 100644 --- a/tests/fast/stages/s02_networking/test_plan.py +++ b/tests/fast/stages/s02_networking/test_plan.py @@ -13,14 +13,8 @@ # limitations under the License. -<<<<<<< HEAD -def test_counts(e2e_plan_runner): - "Test stage." - num_modules, num_resources, _ = e2e_plan_runner() -======= def test_counts(fast_e2e_plan_runner): "Test stage." num_modules, num_resources, _ = fast_e2e_plan_runner() ->>>>>>> master # TODO: to re-enable per-module resource count check print _, then test assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s02_security/test_plan.py b/tests/fast/stages/s02_security/test_plan.py index cc261235..6189f62e 100644 --- a/tests/fast/stages/s02_security/test_plan.py +++ b/tests/fast/stages/s02_security/test_plan.py @@ -13,14 +13,8 @@ # limitations under the License. -<<<<<<< HEAD -def test_counts(e2e_plan_runner): - "Test stage." - num_modules, num_resources, _ = e2e_plan_runner() -======= def test_counts(fast_e2e_plan_runner): "Test stage." num_modules, num_resources, _ = fast_e2e_plan_runner() ->>>>>>> master # TODO: to re-enable per-module resource count check print _, then test assert num_modules > 0 and num_resources > 0 diff --git a/tests/fast/stages/s03_project_factory/test_plan.py b/tests/fast/stages/s03_project_factory/test_plan.py index cc261235..6189f62e 100644 --- a/tests/fast/stages/s03_project_factory/test_plan.py +++ b/tests/fast/stages/s03_project_factory/test_plan.py @@ -13,14 +13,8 @@ # limitations under the License. -<<<<<<< HEAD -def test_counts(e2e_plan_runner): - "Test stage." - num_modules, num_resources, _ = e2e_plan_runner() -======= def test_counts(fast_e2e_plan_runner): "Test stage." num_modules, num_resources, _ = fast_e2e_plan_runner() ->>>>>>> master # TODO: to re-enable per-module resource count check print _, then test assert num_modules > 0 and num_resources > 0 From b6086c8937e8749d6d35cee0a73f33425c17491b Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 28 Jan 2022 17:17:28 +0100 Subject: [PATCH 056/132] Merge master --- fast/README.md | 1 + fast/TODO.txt | 16 -- fast/stages/00-bootstrap/README.md | 139 ----------------- fast/stages/01-resman/branch-gke.tf | 144 ------------------ fast/stages/02-networking/README.md | 127 --------------- .../data/subnets/prod/prod-gke-ew1.yaml | 8 - fast/stages/02-security/README.md | 132 ---------------- tests/fast/stages/s02_networking/fixture/data | 1 - tests/fast/stages/s02_security/fixture/data | 1 - 9 files changed, 1 insertion(+), 568 deletions(-) delete mode 100644 fast/TODO.txt delete mode 100644 fast/stages/01-resman/branch-gke.tf delete mode 100644 fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml delete mode 120000 tests/fast/stages/s02_networking/fixture/data delete mode 120000 tests/fast/stages/s02_security/fixture/data diff --git a/fast/README.md b/fast/README.md index 2afcbe11..5d990adb 100644 --- a/fast/README.md +++ b/fast/README.md @@ -27,6 +27,7 @@ Security was, from the beginning, one of the most critical elements in the desig FAST also aims to minimize the number of permissions granted to principals according to the security-first approach previously mentioned. We achieve this through the meticulous use of groups, service accounts, custom roles, and [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview), among other things. ### Extensive use of factories + A resource factory consumes a simple representation of a resource (e.g., in YAML) and deploys it (e.g., using Terraform). Used correctly, factories can help decrease the management overhead of large-scale infrastructure deployments. See "[Resource Factories: A descriptive approach to Terraform](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c)" for more details and the rationale behind factories. FAST uses YAML-based factories to deploy subnets and firewall rules and, as its name suggests, in the [project factory](./stages/03-project-factory/) stage. diff --git a/fast/TODO.txt b/fast/TODO.txt deleted file mode 100644 index 3c99a403..00000000 --- a/fast/TODO.txt +++ /dev/null @@ -1,16 +0,0 @@ -TODO before merging - -- [ ] fix tests -- [x] fix linting errors -- [ ] fast-specific .gitignore -- [ ] YAML samples thingy -- [ ] stages README -- [ ] proper docstring on new tools -- [x] modify github actions for different fast tfdoc usage -- [ ] add roadmap to top-level fast README -- [ ] update modules references to local paths - - [x] stage 00 (ludo) - - [ ] stage 01 (julio) - - [ ] stage 02-net (simo) - - [x] stage 02-sec (ludo) - - [ ] stage 03-pf (simo) diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md index ccd1495f..52e6ef27 100644 --- a/fast/stages/00-bootstrap/README.md +++ b/fast/stages/00-bootstrap/README.md @@ -1,10 +1,6 @@ # Organization bootstrap -<<<<<<< HEAD -The main purpose of this stage is to enable critical organization-level functionality that depends on broad administrative permissions, and to prepare the prerequisites needed to enable automation in this and future stages. -======= The primary purpose of this stage is to enable critical organization-level functionality that depends on broad administrative permissions, and prepare the prerequisites needed to enable automation in this and future stages. ->>>>>>> master It is intentionally simple, to minimize usage of administrative-level permissions and enable simple auditing and troubleshooting, and only deals with three sets of resources: @@ -12,23 +8,6 @@ It is intentionally simple, to minimize usage of administrative-level permission - projects, BQ datasets, and sinks for audit log and billing exports - IAM bindings on the organization -<<<<<<< HEAD -The following diagram can be used as a simple high level reference for the following sections, which describe the stage and its possible customizations in detail. - -![Organization-level diagram](diagram.png) - -## Design overview and choices - -As mentioned above, this stage only does the bare minimum required to bootstrap automation, and to ensure that base audit and billing exports are in place from the start to provide some measure of accountability, even before the security configurations are applied in a later stage. - -It also sets up organization-level IAM bindings so the Organization Administrator role is only used here, trading off some design freedom for ease of auditing and troubleshooting, and reducing the risk of costly security mistakes down the line. The only exception to this rule is for the [Resource Management stage](../01-resman) service account, and is described below. - -### User groups - -User groups are particularly important, not only here but throughout the whole automation process, as they provide a stable frame of reference that allows decoupling the final set of permissions for each group, from the stage where entities and resources are created and their IAM bindings defined. As an example, the final set of roles for the networking group is contributed by this stage at the organization level (XPN Admin, Cloud Asset Viewer, etc.), and by the Resource Management stage at the folder level. - -To simplify adoption, we have standardized the initial set of groups on those outlined in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist), as they provide a comprehensive and flexible starting point that can suit most users. Adding new groups, or deviating from the initial setup is of course possible and reasonably simple, and it's briefly outlined in the customization section below. -======= Use the following diagram as a simple high level reference for the following sections, which describe the stage and its possible customizations in detail.

@@ -46,7 +25,6 @@ It also sets up organization-level IAM bindings so the Organization Administrato User groups are important, not only here but throughout the whole automation process. They provide a stable frame of reference that allows decoupling the final set of permissions for each group, from the stage where entities and resources are created and their IAM bindings defined. For example, the final set of roles for the networking group is contributed by this stage at the organization level (XPN Admin, Cloud Asset Viewer, etc.), and by the Resource Management stage at the folder level. We have standardized the initial set of groups on those outlined in the [GCP Enterprise Setup Checklist](https://cloud.google.com/docs/enterprise/setup-checklist) to simplify adoption. They provide a comprehensive and flexible starting point that can suit most users. Adding new groups, or deviating from the initial setup is possible and reasonably simple, and it's briefly outlined in the customization section below. ->>>>>>> master ### Organization-level IAM @@ -56,19 +34,11 @@ In order to be able to assign those roles without having the full authority of t In this way, the Resource Management service account can effectively act as an Organization Admin, but only to grant the roles it effectively needs to control. -<<<<<<< HEAD -One consequence of the above setup, is the need to configure IAM bindings as non-authoritative for the roles included in the IAM condition, since those same roles are effectively under control of two stages: this one, and Resource Management. Using authoritative bindings for these roles instead of non-authoritative ones, would generate potential conflicts where each stage tries to overwrite and negate the bindings applied by the other at each `apply` cycle. - -### Automation project and resources - -One other design choice worth mentioning here, is the use of a single automation project for all foundational stages, trading off some complexity on the API side (single source for usage quota, multiple service activation) for increased flexibility and simpler operations, while still effectively providing the same degree of separation via resource-level IAM. -======= One consequence of the above setup, is the need to configure IAM bindings as non-authoritative for the roles included in the IAM condition, since those same roles are effectively under the control of two stages: this one and Resource Management. Using authoritative bindings for these roles (instead of non-authoritative ones) would generate potential conflicts, where each stage could try to overwrite and negate the bindings applied by the other at each `apply` cycle. ### Automation project and resources One other design choice worth mentioning here is using a single automation project for all foundational stages. We trade off some complexity on the API side (single source for usage quota, multiple service activation) for increased flexibility and simpler operations, while still effectively providing the same degree of separation via resource-level IAM. ->>>>>>> master ### Billing account @@ -80,19 +50,11 @@ We support three use cases in regards to billing: For same-organization billing, we configure a custom organization role that can set IAM bindings, via a delegated role grant to limit its scope to the relevant roles. -<<<<<<< HEAD -For details on how to configure the different billing account modes, refer to the [How to run this stage](#how-to-run-this-stage) section below. - -### Naming - -We are intentionally not supporting random prefix/suffixes for names, as that is an antipattern that is typically only used in development, and does not map to the actual production usage we see at customers, who always adopt a fixed naming convention. -======= For details on configuring the different billing account modes, refer to the [How to run this stage](#how-to-run-this-stage) section below. ### Naming We are intentionally not supporting random prefix/suffixes for names, as that is an antipattern typically only used in development. It does not map to our customer's actual production usage, where they always adopt a fixed naming convention. ->>>>>>> master What is implemented here is a fairly common convention, composed of tokens ordered by relative importance: @@ -102,17 +64,6 @@ What is implemented here is a fairly common convention, composed of tokens order - a context identifier (e.g. `core` or `kms`) - an arbitrary identifier used to distinguish similar resources (e.g. `0`, `1`) -<<<<<<< HEAD -Tokens are joined by a `-` character, which makes it easy to visually separate the individual tokens, and allows to programmatically split them in billing exports to derive initial high-level groupings for cost attribution. - -The convention is used in its full form only for specific resources which have globally unique names (projects, GCS buckets), other resources adopt a shorter version for legibility, as the full context can always be derived from their project. - -The [Customizations](#names-and-naming-convention) section on names below explains how to configure tokens, or how to implement a different naming convention. - -## How to run this stage - -This stage has very simple initial requirements, as it is designed to work on newly created GCP organizations. Four steps are needed to bring up this stage: -======= Tokens are joined by a `-` character, making it easy to separate the individual tokens visually, and to programmatically split them in billing exports to derive initial high-level groupings for cost attribution. The convention is used in its full form only for specific resources with globally unique names (projects, GCS buckets). Other resources adopt a shorter version for legibility, as the full context can always be derived from their project. @@ -122,27 +73,18 @@ The [Customizations](#names-and-naming-convention) section on names below explai ## How to run this stage This stage has straightforward initial requirements, as it is designed to work on newly created GCP organizations. Four steps are needed to bring up this stage: ->>>>>>> master - an Organization Admin self-assigns the required roles listed below - the same administrator runs the first `init/apply` sequence passing a special variable to `apply` - the providers configuration file is derived from the Terraform output or linked from the generated file -<<<<<<< HEAD -- a second `init` is run to migrate state, and from then on the stage is run via impersonation -======= - a second `init` is run to migrate state, and from then on, the stage is run via impersonation ->>>>>>> master ### Prerequisites The roles that the Organization Admin used in the first `apply` needs to self-grant are: - Billing Account Administrator (`roles/billing.admin`) -<<<<<<< HEAD - either on the org (if the billing account has been moved to the org) or on the billing account -======= either on the organization or the billing account (see the following section for details) ->>>>>>> master - Logging Admin (`roles/logging.admin`) - Organization Role Administrator (`roles/iam.organizationRoleAdmin`) - Organization Administrator (`roles/resourcemanager.organizationAdmin`) @@ -164,15 +106,9 @@ done If you are using a billing account belonging to a different organization (e.g. in multiple organization setups), some initial configurations are needed to ensure the identities running this stage can assign billing-related roles. -<<<<<<< HEAD -If the billing organization is managed by another version of this stage, we leverage the `organizationIamAdmin` created there, to allow restricted granting of billing roles at the organization level. - -If that's not the case, an equivalent role needs to exist, or the predefined `resourcemanager.organizationAdmin` role can be used if it's not managed authoritatively. The role name then needs to be manually changed in the `billing.tf` file, in the `google_organization_iam_binding` resource. -======= If the billing organization is managed by another version of this stage, we leverage the `organizationIamAdmin` role created there, to allow restricted granting of billing roles at the organization level. If that's not the case, an equivalent role needs to exist, or the predefined `resourcemanager.organizationAdmin` role can be used if not managed authoritatively. The role name then needs to be manually changed in the `billing.tf` file, in the `google_organization_iam_binding` resource. ->>>>>>> master The identity applying this stage for the first time also needs two roles in billing organization, they can be removed after the first `apply` completes successfully: @@ -197,11 +133,7 @@ gcloud beta billing accounts add-iam-policy-binding $BILLING_ACCOUNT \ #### Groups -<<<<<<< HEAD -Before the first run the following IAM groups must exist to allow IAM bindings to be created (actual names are flexible, see the [Customization](#customizations) section): -======= Before the first run, the following IAM groups must exist to allow IAM bindings to be created (actual names are flexible, see the [Customization](#customizations) section): ->>>>>>> master - gcp-billing-admins - gcp-devops @@ -212,11 +144,7 @@ Before the first run, the following IAM groups must exist to allow IAM bindings #### Configure variables -<<<<<<< HEAD -Then make sure you have configured the correct values for the following variables, by editing the defaults in `variables.tf` or providing a `terraform.tfvars` file (preferred): -======= Then make sure you have configured the correct values for the following variables by editing providing a `terraform.tfvars` file: ->>>>>>> master - `billing_account` an object containing the id of your billing account, derived from the Cloud Console UI or by running `gcloud beta billing accounts list`, and the id of the organization owning it, or `null` to use the billing account in isolation @@ -229,29 +157,17 @@ Then make sure you have configured the correct values for the following variable ### Output files and cross-stage variables -<<<<<<< HEAD -At any time during the life of this stage, you can configure it to automatically generate providers configuration and variable files, to simplify exchanging inputs and outputs between stages and avoid having to edit files manually. - -This is disabled by default, to enable the mechanism just set the `outputs_location` variable to a valid path on a local filesystem, e.g. -======= At any time during the life of this stage, you can configure it to automatically generate provider configurations and variable files for the following, to simplify exchanging inputs and outputs between stages and avoid having to edit files manually. 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. ->>>>>>> master ```hcl outputs_location = "../../configs" ``` -<<<<<<< HEAD -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 simply link these files in the relevant stages, instead of having to manually transfer outputs from one stage, to Terraform variables in another. - -This is the outline of the output files generated by this stage: -======= 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. Below is the outline of the output files generated by this stage: ->>>>>>> master ```bash [path specified in outputs_location] @@ -266,15 +182,6 @@ Below is the outline of the output files generated by this stage: ├── 02-security │   ├── providers.tf │   ├── terraform-bootstrap.auto.tfvars.json -<<<<<<< HEAD -├── 03-gke-multitenant-dev -│   ├── providers.tf -│   ├── terraform-bootstrap.auto.tfvars.json -├── 03-gke-multitenant-prod -│   ├── providers.tf -│   ├── terraform-bootstrap.auto.tfvars.json -======= ->>>>>>> master ├── 03-project-factory-dev │   └── terraform-bootstrap.auto.tfvars.json ├── 03-project-factory-prod @@ -298,22 +205,14 @@ Once the initial `apply` completes successfully, configure a remote backend usin ln -s [path set in outputs_location]/00-bootstrap/* ./ # or from outputs if not using output files terraform output -json providers | jq -r '.["00-bootstrap"]' \ -<<<<<<< HEAD - | tee providers.tf -======= > providers.tf ->>>>>>> master # migrate state to GCS bucket configured in providers file terraform init -migrate-state ``` ## Customizations -<<<<<<< HEAD -Most of the variables (e.g. `billing_account` and `organization`) are only used to input actual values and should be self-explanatory. The only meaningful customizations that apply here are groups, and IAM roles. -======= Most variables (e.g. `billing_account` and `organization`) are only used to input actual values and should be self-explanatory. The only meaningful customizations that apply here are groups, and IAM roles. ->>>>>>> master ### Group names @@ -336,11 +235,7 @@ If your groups layout differs substantially from the checklist, define all relev ### IAM -<<<<<<< HEAD -One other area where we directly support customizations is IAM. The code here, as in all other stages, follows a simple pattern derived from best practices: -======= One other area where we directly support customizations is IAM. The code here, as in all stages, follows a simple pattern derived from best practices: ->>>>>>> master - operational roles for humans are assigned to groups - any other principal is a service account @@ -369,11 +264,7 @@ If a different convention is needed, identify names via search/grep (e.g. with ` Names used in internal references (e.g. `module.foo-prod.id`) are only used by Terraform and do not influence resource naming, so they are best left untouched to avoid having to debug complex errors. -<<<<<<< HEAD - -======= ->>>>>>> master ## Files @@ -384,11 +275,7 @@ Names used in internal references (e.g. `module.foo-prod.id`) are only used by T | [billing.tf](./billing.tf) | Billing export project and dataset. | bigquery-dataset · organization · project | google_billing_account_iam_member · google_organization_iam_binding | | [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | | [main.tf](./main.tf) | Module-level locals and resources. | | | -<<<<<<< HEAD -| [organization.tf](./organization.tf) | Organization-level IAM and org policies. | organization | google_organization_iam_binding | -======= | [organization.tf](./organization.tf) | Organization-level IAM. | organization | google_organization_iam_binding | ->>>>>>> master | [outputs.tf](./outputs.tf) | Module outputs. | | local_file | | [variables.tf](./variables.tf) | Module variables. | | | @@ -396,17 +283,6 @@ Names used in internal references (e.g. `module.foo-prod.id`) are only used by T | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -<<<<<<< HEAD -| billing_account | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | | -| organization | Organization details. | object({…}) | ✓ | | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | | -| bootstrap_user | Email of the nominal user running this stage for the first time. | string | | null | | -| groups | Group names to grant organization-level permissions. | map(string) | | {…} | | -| iam | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| iam_additive | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | -| log_sinks | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -======= | [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | | | [organization](variables.tf#L82) | Organization details. | object({…}) | ✓ | | | | [prefix](variables.tf#L97) | Prefix used for resources that need unique names. | string | ✓ | | | @@ -416,29 +292,14 @@ Names used in internal references (e.g. `module.foo-prod.id`) are only used by T | [iam_additive](variables.tf#L51) | Organization-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | | [log_sinks](variables.tf#L57) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | | [outputs_location](variables.tf#L91) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | ->>>>>>> master ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -<<<<<<< HEAD -| billing_dataset | BigQuery dataset prepared for billing export. | | | -| project_ids | Projects created by this stage. | | | -| providers | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| tfvars | Terraform variable files for the following stages. | ✓ | | - - - - - - - -======= | [billing_dataset](outputs.tf#L84) | BigQuery dataset prepared for billing export. | | | | [project_ids](outputs.tf#L89) | Projects created by this stage. | | | | [providers](outputs.tf#L100) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | | [tfvars](outputs.tf#L109) | Terraform variable files for the following stages. | ✓ | | ->>>>>>> master diff --git a/fast/stages/01-resman/branch-gke.tf b/fast/stages/01-resman/branch-gke.tf deleted file mode 100644 index d7681995..00000000 --- a/fast/stages/01-resman/branch-gke.tf +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description GKE stage resources. - -locals { - gke_branch_group_iam = { - (local.groups.gcp-devops) = [ - "roles/viewer", - # ... - ] - } -} - -# top-level GKE folder - -module "branch-gke-folder" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" - parent = "organizations/${var.organization.id}" - name = "GKE" -} - -# environment: development folder and automation resources - -moved { - from = module.branch-gke-env-folder["dev"] - to = module.branch-gke-dev-folder -} - -module "branch-gke-dev-folder" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" - # naming: environment descriptive name - name = "Development" - parent = module.branch-gke-folder.id - group_iam = local.gke_branch_group_iam - iam = { - "roles/logging.admin" = [ - module.branch-gke-dev-sa.iam_email - ] - "roles/owner" = [ - module.branch-gke-dev-sa.iam_email - ] - "roles/resourcemanager.projectCreator" = [ - module.branch-gke-dev-sa.iam_email - ] - } -} - -moved { - from = module.branch-gke-env-sa["dev"] - to = module.branch-gke-dev-sa -} - -module "branch-gke-dev-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" - name = "resman-gke-0" - project_id = var.automation_project_id - description = "Terraform GKE development service account." - prefix = local.prefixes.dev -} - -moved { - from = module.branch-gke-gcs["dev"] - to = module.branch-gke-dev-gcs -} - -module "branch-gke-dev-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" - name = "resman-gke-0" - project_id = var.automation_project_id - prefix = local.prefixes.dev - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.branch-gke-dev-sa.iam_email] - } -} - -# environment: production folder and automation resources - -moved { - from = module.branch-gke-env-folder["prod"] - to = module.branch-gke-prod-folder -} - -module "branch-gke-prod-folder" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/folder?ref=v12.0.0" - # naming: environment descriptive name - name = "Production" - parent = module.branch-gke-folder.id - group_iam = local.gke_branch_group_iam - iam = { - "roles/logging.admin" = [ - module.branch-gke-prod-sa.iam_email - ] - "roles/owner" = [ - module.branch-gke-prod-sa.iam_email - ] - "roles/resourcemanager.projectCreator" = [ - module.branch-gke-prod-sa.iam_email - ] - } -} - -moved { - from = module.branch-gke-env-sa["prod"] - to = module.branch-gke-prod-sa -} - -module "branch-gke-prod-sa" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/iam-service-account?ref=v12.0.0" - name = "resman-gke-0" - project_id = var.automation_project_id - description = "Terraform GKE production service account." - prefix = local.prefixes.prod -} - -moved { - from = module.branch-gke-gcs["prod"] - to = module.branch-gke-prod-gcs -} - -module "branch-gke-prod-gcs" { - source = "github.com/terraform-google-modules/cloud-foundation-fabric//modules/gcs?ref=v12.0.0" - name = "resman-gke-0" - project_id = var.automation_project_id - prefix = local.prefixes.prod - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.branch-gke-prod-sa.iam_email] - } -} diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md index 37d160f3..4369cb44 100644 --- a/fast/stages/02-networking/README.md +++ b/fast/stages/02-networking/README.md @@ -1,10 +1,6 @@ # Networking -<<<<<<< HEAD -This stage sets up the shared network infrastructure for the whole org. It adopts the common “hub and spoke” reference design, which is well suited to multiple scenarios, and offers several advantages versus other designs: -======= This stage sets up the shared network infrastructure for the whole organization. It adopts the common “hub and spoke” reference design, which is well suited to multiple scenarios, and offers several advantages versus other designs: ->>>>>>> master - the “hub” VPC centralizes external connectivity to on-prem or other cloud environments, and is ready to host cross-environment services like CI/CD, code repositories, and monitoring probes - the “spoke” VPCs allow partitioning workloads (e.g. by environment like in this setup), while still retaining controlled access to central connectivity and services @@ -15,13 +11,9 @@ Connectivity between hub and spokes is established here via [VPN HA](https://clo The following diagram illustrates the high-level design, and should be used as a reference for the following sections. The final number of subnets, and their IP addressing design will of course depend on customer-specific requirements, and can be easily changed via variables or external data files without having to edit the actual code. -<<<<<<< HEAD -![Networking diagram](diagram.png) -=======

Networking diagram

->>>>>>> master ## Design overview and choices @@ -99,11 +91,7 @@ Several other scenarios are possible of course, with varying degrees of complexi Future pluggable modules will allow to easily experiment, or deploy the above scenarios. -<<<<<<< HEAD -### Firewall -======= ### VPC and Hierarchical Firewall ->>>>>>> master The GCP Firewall is a stateful, distributed feature that allows the creation of L4 policies, either via VPC-level rules or more recently via hierarchical policies applied on the resource hierarchy (organization, folders). @@ -188,14 +176,9 @@ Each file contains the same resources, described in the following paragraphs. The **project** ([`project`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)". -<<<<<<< HEAD -The **VPC** ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc#subnet-factory). -Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are in variable `psa_ranges` - such variables are consumed by spoke VPCs. -======= The **VPC** ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets), and can be easily customised to fit your needs. Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs. ->>>>>>> master **Cloud NAT** ([`net-cloudnat`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress. @@ -213,32 +196,18 @@ VPNs ([`net-vpn`](https://github.com/terraform-google-modules/cloud-foundation-f Each VPC network ([`net-vpc`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc)) manages a separate routing table, which can define static routes (e.g. to private.googleapis.com) and receives dynamic routes from BGP sessions established with neighbor networks (e.g. landing receives routes from onprem and spokes, and spokes receive RFC1918 from landing). -<<<<<<< HEAD -Static routes are defined in `vpc-*.tf` files, in the `routes` section of each `net-vpc` module. -======= Static routes are defined in `vpc-*.tf` files, in the `routes` section of each `net-vpc` module. ->>>>>>> master BGP sessions for landing-spoke are configured through variable `vpn_spoke_configs`, while the ones for landing-onprem use variable `vpn_onprem_configs` ### Firewall -<<<<<<< HEAD -**VPC firewall rules** ([`net-vpc-firewall`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall)) are defined per-vpc on each `vpc-*.tf` file and leverage a resource factory to massively create rules. -To add a new firewall rule, create a new file or edit an existing one in the `data_folder` directory defined in the module `net-vpc-firewall`, following the examples of the "[Rules factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall#rules-factory)" section of the module documentation. - -**Hierarchical firewall policies** ([`folder`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/folder)) are defined in `main.tf`, and managed through a policy factory implemented by the `folder` module, which applies the defined hierarchical to the `Networking` folder, which contains all the core networking infrastructure. Policies are defined in the `rules_file` file - to define a new one simply use the instructions found on "[Firewall policy factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/organization#firewall-policy-factory)". - - -### DNS -======= **VPC firewall rules** ([`net-vpc-firewall`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall)) are defined per-vpc on each `vpc-*.tf` file and leverage a resource factory to massively create rules. To add a new firewall rule, create a new file or edit an existing one in the `data_folder` directory defined in the module `net-vpc-firewall`, following the examples of the "[Rules factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/net-vpc-firewall#rules-factory)" section of the module documentation. Sample firewall rules are shipped in [data/firewall-rules/landing](./data/firewall-rules/landing) and can be easily customised. **Hierarchical firewall policies** ([`folder`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/folder)) are defined in `main.tf`, and managed through a policy factory implemented by the `folder` module, which applies the defined hierarchical to the `Networking` folder, which contains all the core networking infrastructure. Policies are defined in the `rules_file` file - to define a new one simply use the instructions found on "[Firewall policy factory](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/organization#firewall-policy-factory)". Sample hierarchical firewall policies are shipped in [data/hierarchical-policy-rules.yaml](./data/hierarchical-policy-rules.yaml) and can be easily customised. ### DNS architecture ->>>>>>> master The DNS ([`dns`](https://github.com/terraform-google-modules/cloud-foundation-fabric/tree/master/modules/dns)) infrastructure is defined in [`dns.tf`](dns.tf). @@ -257,35 +226,14 @@ DNS queries sent to the on-premises infrastructure come from the `35.199.192.0/1 #### On-prem to cloud -<<<<<<< HEAD -The [Inbound DNS Policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) defined in module `landing-vpc` ([`landing.tf`](./landing.tf)) automatically reserves the first available IP address on each created subnet (typically the third one in a CIDR) to expose the Cloud DNS service so that it can be consumed from outside of GCP. - -### Private Google Access -======= The [Inbound DNS Policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) defined in module `landing-vpc` ([`landing.tf`](./landing.tf)) automatically reserves the first available IP address on each created subnet (typically the third one in a CIDR) to expose the Cloud DNS service so that it can be consumed from outside of GCP. ### Private Google Access ->>>>>>> master [Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment. For PGA to work: -<<<<<<< HEAD -* Private Google Access should be enabled on the subnet. \ -Subnets created by the `net-vpc` module are PGA-enabled by default. - -* 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \ -Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed. - -* A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain, as implemented in module `googleapis-private-zone` in [`dns.tf`](./dns.tf) - -### Preliminar activities - -Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` to your needs, to update all reference to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. - -If you're not using FAST, you'll also need to create a `providers.tf` file to configure the GCS backend and the service account to use to run the deployment. -======= - Private Google Access should be enabled on the subnet. \ Subnets created by the `net-vpc` module are PGA-enabled by default. @@ -299,35 +247,22 @@ Per variable `vpn_onprem_configs` such ranges are advertised to onprem - further Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` to your needs, to update all reference to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. If you're not using FAST, you'll also need to create a `providers.tf` file to configure the GCS backend and the service account to use to run the deployment. ->>>>>>> master You're now ready to run `terraform init` and `apply`. ### Post-deployment activities -<<<<<<< HEAD -* On-prem routers should be configured to advertise all relevant CIDRs to the GCP environments. To avoid hitting GCP quotas, we recomment aggregating routes as much as possible. -* On-prem routers should accept BGP sessions from their cloud peers. -* On-prem DNS servers should have forward zones for GCP-managed ones. -======= - On-prem routers should be configured to advertise all relevant CIDRs to the GCP environments. To avoid hitting GCP quotas, we recomment aggregating routes as much as possible. - On-prem routers should accept BGP sessions from their cloud peers. - On-prem DNS servers should have forward zones for GCP-managed ones. ->>>>>>> master ## Customizations ### Adding an environment -<<<<<<< HEAD -To create a new environment (e.g. `staging`), a few changes are required. - -Create a `vpc-spoke-staging.tf` file by copying `vpc-spoke-prod.tf` file, -======= To create a new environment (e.g. `staging`), a few changes are required. Create a `vpc-spoke-staging.tf` file by copying `vpc-spoke-prod.tf` file, ->>>>>>> master and adapt the new file by replacing the value "prod" with the value "staging". Running `diff vpc-spoke-dev.tf vpc-spoke-prod.tf` can help to see how environment files differ. @@ -339,18 +274,6 @@ Variables managing L7 Interal Load Balancers (`l7ilb_subnets`) and Private Servi VPN HA connectivity (see also [VPNs](#vpns)) to `landing` is managed by the `vpn-spoke-*.tf` files. Copy `vpn-spoke-prod.tf` to `vpn-spoke-staging.tf` - replace "prod" with "staging" where relevant. -<<<<<<< HEAD -VPN configuration also controls BGP advertisements, which requires the following variable changes: -* `router_configs` to configure the new routers (one per region) created for the `staging` VPC -* `vpn_onprem_configs` to configure the new advertisments to on-premises for the new CIDRs -* `vpn_spoke_configs` to configure the new advertisements to `landing` for the new VPC - new keys (one per region) should be added, such as e.g. `staging-ew1` and `staging-ew4` - -DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `staging.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy all the `prod-*` modules in the `dns.tf` file to `staging-*`, and update their content accordingly. Don't forget to add a peering zone from Landing to the newly created environment private zone. - - - - -======= VPN configuration also controls BGP advertisements, which requires the following variable changes: - `router_configs` to configure the new routers (one per region) created for the `staging` VPC @@ -360,7 +283,6 @@ VPN configuration also controls BGP advertisements, which requires the following DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `staging.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy all the `prod-*` modules in the `dns.tf` file to `staging-*`, and update their content accordingly. Don't forget to add a peering zone from Landing to the newly created environment private zone. ->>>>>>> master ## Files @@ -386,23 +308,6 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -<<<<<<< HEAD -| billing_account_id | Billing account id. | string | ✓ | | 00-bootstrap | -| organization | Organization details. | object({…}) | ✓ | | 00-bootstrap | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | -| custom_adv | Custom advertisement definitions in name => range format. | map(string) | | {…} | | -| data_dir | Relative path for the folder storing configuration data for network resources. | string | | "data" | | -| dns | Onprem DNS resolvers | map(list(string)) | | {…} | | -| folder_id | Folder to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | string | | null | 01-resman | -| gke | | map(object({…})) | | {} | 01-resman | -| l7ilb_subnets | Subnets used for L7 ILBs. | map(list(object({…}))) | | {…} | | -| outputs_location | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| project_factory_sa | IAM emails for project factory service accounts | map(string) | | {} | 01-resman | -| psa_ranges | IP ranges used for Private Service Access (e.g. CloudSQL). | map(map(string)) | | {…} | | -| router_configs | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | -| vpn_onprem_configs | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | -| vpn_spoke_configs | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | -======= | [billing_account_id](variables.tf#L17) | Billing account id. | string | ✓ | | 00-bootstrap | | [organization](variables.tf#L97) | Organization details. | object({…}) | ✓ | | 00-bootstrap | | [prefix](variables.tf#L113) | Prefix used for resources that need unique names. | string | ✓ | | 00-bootstrap | @@ -418,42 +323,11 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res | [router_configs](variables.tf#L141) | Configurations for CRs and onprem routers. | map(object({…})) | | {…} | | | [vpn_onprem_configs](variables.tf#L165) | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | | [vpn_spoke_configs](variables.tf#L218) | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | ->>>>>>> master ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -<<<<<<< HEAD -| cloud_dns_inbound_policy | IP Addresses for Cloud DNS inbound policy. | | | -| project_ids | Network project ids. | | | -| project_numbers | Network project numbers. | | | -| shared_vpc_host_projects | Shared VPC host projects. | | | -| shared_vpc_self_links | Shared VPC host projects. | | | -| tfvars | Network-related variables used in other stages. | ✓ | | -| vpn_gateway_endpoints | External IP Addresses for the GCP VPN gateways. | | | - - - - - - - - - - - - - - - - - - - - - -======= | [cloud_dns_inbound_policy](outputs.tf#L41) | IP Addresses for Cloud DNS inbound policy. | | | | [project_ids](outputs.tf#L46) | Network project ids. | | | | [project_numbers](outputs.tf#L55) | Network project numbers. | | | @@ -463,4 +337,3 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res | [vpn_gateway_endpoints](outputs.tf#L84) | External IP Addresses for the GCP VPN gateways. | | | ->>>>>>> master diff --git a/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml b/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml deleted file mode 100644 index 2512947e..00000000 --- a/fast/stages/02-networking/data/subnets/prod/prod-gke-ew1.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# skip boilerplate check - -region: europe-west1 -description: GKE subnet for prod -ip_cidr_range: 10.140.0.0/24 -secondary_ip_ranges: - - pods: 10.140.1.0/16 - - services: 10.140.2.0/24 diff --git a/fast/stages/02-security/README.md b/fast/stages/02-security/README.md index e1816cca..36797f2f 100644 --- a/fast/stages/02-security/README.md +++ b/fast/stages/02-security/README.md @@ -1,32 +1,5 @@ # Shared security resources -<<<<<<< HEAD -This stage sets up security resources and configurations which impact the whole org, or are shared across the hierarchy to other projects and teams. - -Its design is fairly general, and provides a reference example for [Cloud KMS](https://cloud.google.com/security-key-management) and a [VPC Service Controls](https://cloud.google.com/vpc-service-controls) configuration that sets up three perimeters (landing, development, production), the related bridge perimeters, and provides variables to configure their resources, access levels, and directional policies. - -Expanding this stage to include other security-related services like Secret Manager, is fairly simple by using the provided implementation for Cloud KMS, and leveraging the broad permissions on the top-level Security folder of the automation service account used. - -The following diagram illustrates the high level design of created resources, and a schema of the VPC SC design, which can be adapted to specific requirements via variables: - -![Security diagram](diagram.png) - -## Design overview and choices - -Project-level security resources are grouped into two separate projects, one per environment. This matches requirements we frequently observe in real life, and provides enough separation without needlessly complicating operations. - -Cloud KMS is configured and designed mainly to encrypt GCP resources with a [Customer-managed encryption key](https://cloud.google.com/kms/docs/cmek) but it may be used to create cryptokeys used to [encrypt application data](https://cloud.google.com/kms/docs/encrypting-application-data) too. - -IAM for management-related operations is already assigned at the folder level to the security team by the previous stage, but more granularity can of course be added here at the project level, to grant control of separate services across environments to different actors. - -### Cloud KMS - -A reference Cloud KMS implementation is part of this stage, to provide a simple way of managing centralized keys, that are then shared and consumed widely across the org to enable customer-managed encryption. The implementation is also easy to clone and modify to support other services like Secret Manager. - -The Cloud KMS configuration allows defining keys by name (typically matching the downstream service that uses them) in different locations, either based on a common default or a per-key setting, and then takes care internally of provisioning the relevant keyrings and creating keys in the appropriate location. - -IAM roles on keys can of course be configured at the logical level for all locations where a logical key is created, and their management can also be delegated via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) exposed through a simple variable, to allow other identities to set IAM policies on keys. This is particularly useful in setups like project factories, making it possible to configure IAM bindings during project creation for team groups or service agent accounts (compute, storage, etc.). -======= This stage sets up security resources and configurations which impact the whole organization, or are shared across the hierarchy to other projects and teams. The design of this stage is fairly general, and provides a reference example for [Cloud KMS](https://cloud.google.com/security-key-management) and a [VPC Service Controls](https://cloud.google.com/vpc-service-controls) configuration that sets up three perimeters (landing, development, production), their related bridge perimeters, and provides variables to configure their resources, access levels, and directional policies. @@ -54,7 +27,6 @@ A reference Cloud KMS implementation is part of this stage, to provide a simple The Cloud KMS configuration allows defining keys by name (typically matching the downstream service that uses them) in different locations, either based on a common default or a per-key setting. It then takes care internally of provisioning the relevant keyrings and creating keys in the appropriate location. IAM roles on keys can be configured at the logical level for all locations where a logical key is created. Their management can also be delegated via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) exposed through a simple variable, to allow other identities to set IAM policies on keys. This is particularly useful in setups like project factories, making it possible to configure IAM bindings during project creation for team groups or service agent accounts (compute, storage, etc.). ->>>>>>> master ### VPC Service Controls @@ -64,23 +36,6 @@ This stage also provisions the VPC Service Controls configuration on demand for - one perimeter for centralized services and the landing VPC - bridge perimeters to connect the landing perimeter to each environment -<<<<<<< HEAD -The VPC SC configuration is set to dry-run mode, but switching to enforced mode is a simple operation involving the modification of a few lines of code highlighted by ad-hoc comments. Variables are designed to enable easy centralized management of VPC Service Controls, including access levels and [ingress/egress rules](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules) as described below. - -Some care needs to be taken with project membership in perimeters, which can only be implemented here instead of being delegated (all or partially) to different stages, until the [Google Provider feature request](https://github.com/hashicorp/terraform-provider-google/issues/7270) that allows using project-level association for both enforced and dry-run modes is implemented. - -## How to run this stage - -This stage is meant to be executed after the [resouce management](../01-resman) stage has run, as it leverages the folder and automation resources created there. The relevant user groups must also exist, but that's one of the requirements for the previous stages too, so if you ran those successfully, you're good to go. - -It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap stage for the actual roles needed. - -Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. - -### Providers configuration - -The default way of making sure you have the right permissions, is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). -======= The VPC SC configuration is set to dry-run mode, but switching to enforced mode is a simple operation involving modifying a few lines of code highlighted by ad-hoc comments. Variables are designed to enable easy centralized management of VPC Service Controls, including access levels and [ingress/egress rules](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules) as described below. Some care needs to be taken with project membership in perimeters, which can only be implemented here instead of being delegated (all or partially) to different stages, until the [Google Provider feature request](https://github.com/hashicorp/terraform-provider-google/issues/7270) allowing using project-level association for both enforced and dry-run modes is implemented. @@ -96,7 +51,6 @@ Before running this stage, you need to ensure you have the correct credentials a ### Providers configuration The default way of making sure you have the correct permissions is to use the identity of the service account pre-created for this stage during bootstrap, and that you are a member of the group that can impersonate it via provider-level configuration (`gcp-devops` or `organization-admins`). ->>>>>>> master To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. @@ -119,21 +73,12 @@ terraform output -json providers | jq -r '.["02-security"]' \ There are two broad sets of variables you will need to fill in: -<<<<<<< HEAD -- variables shared by other stages (org id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) -- variables specific to resources managed by this stage - -To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. - -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 avalaible: -======= - variables shared by other stages (organization id, billing account id, etc.), or derived from a resource managed by a different stage (folder id, automation project id, etc.) - variables specific to resources managed by this stage To avoid the tedious job of filling in the first group of variables with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. 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: ->>>>>>> master ```bash # `outputs_location` is set to `../../configs/example` @@ -141,15 +86,9 @@ ln -s ../../configs/example/02-security/terraform-bootstrap.auto.tfvars.json ln -s ../../configs/example/02-security/terraform-resman.auto.tfvars.json ``` -<<<<<<< HEAD -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. - -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. The sections below also describe some of the possible customizations. -======= A second set of optional variables is specific to this stage. If you need to customize them, create an extra `terraform.tfvars` file. 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. The sections below also describe some of the possible customizations. ->>>>>>> master Once done, you can run this stage: @@ -165,15 +104,6 @@ terraform apply Cloud KMS configuration is split in two variables: - `kms_defaults` configures the locations and rotation period, used for keys that don't specifically configure them -<<<<<<< HEAD -- `kms_keys` configures the actual keys to create, and also allows configuring their IAM bindings and labels, and overriding locations and rotation period. When configuring locations for a key, please take into account limitations each cloud product may have. - -The additional `kms_restricted_admins` variable allows granting `roles/cloudkms.admin` to specified principals, restricted via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) so that it only allows granting the roles needed for encryption/decryption on keys. This allows safe delegation of key management to subsequent Terraform stages like the Project Factory, for example to grant usage access on relevant keys to the service agent accounts for compute, storage, etc. - -To support these scenarios, key IAM bindings are configured by default to be additive, so as to enable other stages or Terraform configuration to safely co-manage bindings on the same keys. If this is not desired, follow the comments in the `core-dev.tf` and `core-prod.tf` files to switch to using authoritative bindings on keys. - -An example on how to configure keys: -======= - `kms_keys` configures the actual keys to create, and also allows configuring their IAM bindings and labels, and overriding locations and rotation period. When configuring locations for a key, please consider the limitations each cloud product may have. The additional `kms_restricted_admins` variable allows granting `roles/cloudkms.admin` to specified principals, restricted via [delegated role grants](https://cloud.google.com/iam/docs/setting-limits-on-granting-roles) so that it only allows granting the roles needed for encryption/decryption on keys. This allows safe delegation of key management to subsequent Terraform stages like the Project Factory, for example to grant usage access on relevant keys to the service agent accounts for compute, storage, etc. @@ -181,7 +111,6 @@ The additional `kms_restricted_admins` variable allows granting `roles/cloudkms. To support these scenarios, key IAM bindings are configured by default to be additive, to enable other stages or Terraform configuration to safely co-manage bindings on the same keys. If this is not desired, follow the comments in the `core-dev.tf` and `core-prod.tf` files to switch to authoritative bindings on keys. An example of how to configure keys: ->>>>>>> master ```hcl # terraform.tfvars @@ -210,11 +139,7 @@ kms_keys = { } ``` -<<<<<<< HEAD -The script will create one keyring for each location specified and create necessarily keys on each keyring. -======= The script will create one keyring for each specified location and keys on each keyring. ->>>>>>> master ### VPC Service Controls configuration @@ -225,17 +150,6 @@ A set of variables allows configuring the VPC SC perimeters described above: - `vpc_sc_egress_policies` configures directional egress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_egress_policies` - `vpc_sc_ingress_policies` configures directional ingress policies, which can then be associated to perimeters by key using the `vpc_sc_perimeter_ingress_policies` -<<<<<<< HEAD -This allows configuring VPC SC in a fairly flexible and concise way, without having to repeat similar definitions. Bridges perimeters configuration will be computed automatically to allow communication between regular perimeters: `landing <-> prod` and `landing <-> dev`. - -#### Dry-run vs enforced - -The VPC SC configuration is set up by default in dry-run mode, to allow easy experimentation, and detecting violations before enforcement. Once everything has been set up correctly, switching to enforced mode needs to be done in code, by swapping the contents of the `spec` and `status` attributes for perimeters in the `vpc-sc.tf` file. The effort involved is minimal (2 lines of code per perimeter), and comments help with identifying the correct lines. - -#### Perimeter resources - -Project are added to perimeters via the `vpc_sc_perimeter_projects`, and that's currently the only way of doing it without generating permadiffs or conflicts, since the only Terraform resource that works for both enforced and dry-run mode is authoritative (perimeter resources need to be managed in a single place). -======= This allows configuring VPC SC in a fairly flexible and concise way, without repeating similar definitions. Bridges perimeters configuration will be computed automatically to allow communication between regular perimeters: `landing <-> prod` and `landing <-> dev`. #### Dry-run vs. enforced @@ -245,7 +159,6 @@ The VPC SC configuration is set up by default in dry-run mode to allow easy expe #### Perimeter resources Projects are added to perimeters via the `vpc_sc_perimeter_projects`, and that's currently the only way of doing it without generating permadiffs or conflicts, because of the way the Terraform provider is implemented. ->>>>>>> master Once the Google Terraform Provider [implements support for dry-run mode in the additive resource](https://github.com/hashicorp/terraform-provider-google/issues/7270), it will be possible to concurrently manage perimeter resources both here and in subsequent Terraform configurations, for example to allow the Project Factory to add a project to a perimeter during the creation process. @@ -288,11 +201,7 @@ vpc_sc_perimeter_access_levels = { #### Ingress and Egress policies -<<<<<<< HEAD -The same applies to Ingress and Egress policies, as shown in the examples below that reference the automation service account for this stage. -======= The same applies to Ingress and Egress policies, as shown in the examples below referencing the automation service account for this stage. ->>>>>>> master Below you can find an ingress policy configuration that allows applying Terraform from outside the perimeter, useful when bringing up this stage to avoid generating violations: @@ -356,12 +265,7 @@ Some references that might be useful in setting up this stage: - [VPC SC CSCC requirements](https://cloud.google.com/security-command-center/docs/troubleshooting). -<<<<<<< HEAD - - -======= ->>>>>>> master ## Files @@ -379,24 +283,6 @@ Some references that might be useful in setting up this stage: | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -<<<<<<< HEAD -| billing_account_id | Billing account id. | string | ✓ | | bootstrap | -| folder_id | Folder to be used for the networking resources in folders/nnnn format. | string | ✓ | | resman | -| organization | Organization details. | object({…}) | ✓ | | bootstrap | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | | -| groups | Group names to grant organization-level permissions. | map(string) | | {…} | bootstrap | -| kms_defaults | Defaults used for KMS keys. | object({…}) | | {…} | | -| kms_keys | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | map(object({…})) | | {} | | -| kms_restricted_admins | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | map(list(string)) | | {} | | -| outputs_location | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | -| vpc_sc_access_levels | VPC SC access level definitions. | map(object({…})) | | {} | | -| vpc_sc_egress_policies | VPC SC egress policy defnitions. | map(object({…})) | | {} | | -| vpc_sc_ingress_policies | VPC SC ingress policy defnitions. | map(object({…})) | | {} | | -| vpc_sc_perimeter_access_levels | VPC SC perimeter access_levels. | object({…}) | | null | | -| vpc_sc_perimeter_egress_policies | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | -| vpc_sc_perimeter_ingress_policies | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | -| vpc_sc_perimeter_projects | VPC SC perimeter resources. | object({…}) | | null | | -======= | [billing_account_id](variables.tf#L17) | Billing account id. | string | ✓ | | bootstrap | | [folder_id](variables.tf#L23) | Folder to be used for the networking resources in folders/nnnn format. | string | ✓ | | resman | | [organization](variables.tf#L73) | Organization details. | object({…}) | ✓ | | bootstrap | @@ -413,29 +299,11 @@ Some references that might be useful in setting up this stage: | [vpc_sc_perimeter_egress_policies](variables.tf#L157) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | | [vpc_sc_perimeter_ingress_policies](variables.tf#L167) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | | [vpc_sc_perimeter_projects](variables.tf#L177) | VPC SC perimeter resources. | object({…}) | | null | | ->>>>>>> master ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -<<<<<<< HEAD -| stage_perimeter_projects | Security project numbers. They can be added to perimeter resources. | | | - - - - - - - - - - - - - -======= | [stage_perimeter_projects](outputs.tf#L37) | Security project numbers. They can be added to perimeter resources. | | | ->>>>>>> master diff --git a/tests/fast/stages/s02_networking/fixture/data b/tests/fast/stages/s02_networking/fixture/data deleted file mode 120000 index 423e31f5..00000000 --- a/tests/fast/stages/s02_networking/fixture/data +++ /dev/null @@ -1 +0,0 @@ -../../../../stages/02-networking/data \ No newline at end of file diff --git a/tests/fast/stages/s02_security/fixture/data b/tests/fast/stages/s02_security/fixture/data deleted file mode 120000 index 423e31f5..00000000 --- a/tests/fast/stages/s02_security/fixture/data +++ /dev/null @@ -1 +0,0 @@ -../../../../stages/02-networking/data \ No newline at end of file From 8e8823c641651d189700f69592027b9814b3eab4 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Fri, 28 Jan 2022 17:20:52 +0100 Subject: [PATCH 057/132] Merge master --- fast/stages/01-resman/diagram.png | Bin 109088 -> 233671 bytes fast/stages/03-project-factory/README.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/fast/stages/01-resman/diagram.png b/fast/stages/01-resman/diagram.png index 99c6e26d227fbfe8ad586c818e3fa4fa3b03b38e..d1026318b47fe76e33a2bacf01bb22fbb057607b 100644 GIT binary patch literal 233671 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ℑ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�yoK&cX<(l+#rV=7V~#THp{B24uyKEKTVRb$Zi=Nuk&HXwO#Qi z?`xolovoGi&Hwa_U>2P#3uzkWUpyOI(O;yhdmm3H>x-;loDo4%CW)vot1}jk8hInS z@9eFKlmeF0HCf0t9{IHE9f0s2;3df9xu7=8#c1nw3kaplFnBk z)(I=`OAk0RQx;O?vj~_OKksaWutOjDqh_)0u*L#&HLn3}V0k#1DL>{j4qqd#r_FhP^6 z#bc2!bU~`1Ana=Cl-kZf`4HD3eH_HPo2Y0i%u0D*4*bMSD5Xw)nkY2;@0n!s4B*9H z0hz9|U$kXop_0tGjoVej-G-u-6do`KmJA(c)!dJ9?lu-J*Py}wQlXMnyM?P(GW}jH z>y4hDk2Oi^AKNv2#9V4V2aWzx?j}phOQg^#7|n88c%&hpS&o#=T{b2Gtvqv+l-ym# zwu+&ts%moo^>>>UesS;L^tz%FI%T^wpmE&kj$oEr-;~m@HZLl@jy!BDAM-AWWnXf) z-p^^>fmv*ON`Zj||3kn*RI*+D#s8&oJqL2J!DHflA?l))DYZS31Qf54fwQ_5f_Ii? zo^`5=lj0E7DhOAFU~YLKaU}*a66K7-GI^zH^*fU~oL88~=tigT*8s3LXC(}qEfw2- zjzfAbZ*>o1iUk&`3=gS%_Gkj-i@tYVPAfW?q@E7h*THEDzsr+?-i1(<2coV#xI<6k=L zf=3)@Ml7aBiqgud{tdR^Y1BMFbO)yDqz+P6c?X0ND}J%5-BuRzB~+H{ zl+Cv|7^b;PLVKOwGm<}7;K8q@^#W=3Zq!}%*Y;Dx!XtJ6FBj}Nx_gAQB(#*pslK=( zf<;1D^atsp`H9bujq!|m$OO;$YhhBdR2j(>evrh?&nM@pkhN*EhD6t|wMP}aM~;t5 zHWqkeS`p6~4#2-z=3pcg8FGiIHy*Uon>sdfgyp=~&=;8?4r`>;-y#pZmQ?1G7%}rQ z8+Fp7@$VA$+(U7j|0XI@91d*w99FUcJ$Y7~y4J+&IL81AspCMa`V-&=A6x&|--RB> z3`TFz@om9g*!4MO=;AharDQk{B;(q&ByP4(|z=m+X2H$ z+!8tocv4zi`toif9R*PmPU%%z;hQLh5_5%WI;+uzg+#6Gx*t{L20tO^)Q_;#Ejy4( z-AEl>mv(^y!MpU2IPao9;vjS*1`P>D^q0tA-)6sKM~=qiuCH&y@08P&*V~# zEr~r`y1t#)p*rn6_da2Mn7GM*=fB-cD&IEBPV|PH+<#k*CO{7E1tLzG(u*raa*@$d zw%in{bR(_+Nq=XN`AKHjjG*uRT#mDmckG7qhe(!!Ru=_HYvU)I_@j$KYhQ4Hox;RE zm7m9<&}4YiE6^E)3+&+~^L#~#Jdzh{iq(+3!*2|tUP5+j54Sye99O1js{!*)1lmvT z(W(jG$wRLEMBXjX#^($#UmHUje?m)@K?>;6A#2IO4i`U?*J^$Y;{_ZmO8&bd@JklY z8sK3QJp65q7pS8nRIH8+R0Dps5xQ9!{oEL@`q1``@F8|rob`L~;OP49OGGjWi5XY1 zo6S%f?qy9?QNoe~5npIUF5PxTVPPMS)-I69@*LNN_QDQRqL=M8M?O5+RN8OPj{&UDc054 z$juQp{rq)g*|pi_fvZ`nwAWMm33wH7!3BuDVfJ0V59^q$VnO1cjBkIgh0 z%M4Hja9W+L%;K@tl-^IAYry|3>^whPK)M^*+LhgB_7|GKHjp!3TFg~|QUQ-u#`Qux zS0AG3762*HhV9ggdNk?x%$_u=KV;oCQNxnwcUv`^`b!z@t~YtFYWs=iz~3fTnA>Kc z9lO2`UDqA0-al%}PUdb7G$w^EzOU#@?@aYxd&j}Sqr3|0{(v@?ol^s=wyT|4*P#+U z^e?_PQ&%ksP}$88zZkQ&YIF5ixZB|{Eai$P(*F=~H}Jst33^r9<^aoYxVlM$Ociu~ zI=*av*f?>jf62W?pWTL48NGok@!urmL{t#)cf$Cpx1`vmWXhyGI*eP9A}uS-DF5o% zyIfySkHN;y9w~SLe@ddGBy4?8^ZV*2i~2@BL$RB$P~posuea9`fnc-A&NXVJr<8TW zQ1Wls7VTDLL41|IO~OtaPw%SzcUy9tBic*U!L91*YCcTZ94v9w;kC>^Uof?`tLG(pdwf};gK)W4Vw147nPeGl&MJdb+~8}W=cKUz6peki!p z_1~s`in>!^BTza{u(AWjC>(r08r}V>sLI(BV4M z7Ojh{hIqM-EaE+Ral}$?O7uJ z-AW|{*A7(p7ogfE#}6Q z820=;1_7FprY!GOV_45JSZ^6R7WdizZi26EkMH2>A6z7T7C3CjW3IeR%O* z7`yo@4-FejS?YOFkdW}~b8i|rYI0{fyzYr|fFUE@pqK8?!i68pYscKw%a*Zlb}KL#U-&t6v=eQhxA8s*Nv z?T6>e=k1P&HGJL&Z9nAIRL{OUV4wne{U0kTk$dV;a_x#Xg(MC+z&%ZUx&40uryDZJ6tOnU87D zZCURRxq3XzIJ#rbs91v8p*{aM&);0F;@S1QA5Uvx92u2!JN72$QFJF1t1(l(qLAKl zX4%gP>5FW{F`fB1~y6i-(RNSsRb79D;{^vmuj2^$lGl48UpBU!O>nz$p^t5 z1Ul7@-^4Gm=4?WG{-)#f;p8Wb*?u~7*K=%3$ zS^fTmKDJ-=`c;))&Zys2NW0;w@Jnw{^(>A|rYH>|%7#Z`n4R|7{<1s=)V~is8`htoc5Na^nP{rw`7u zATTtYm(x*g7l)Dby|2I76*l_MRv1M2Of+VeOSP0|0*6n;n z+tMB3*xH$NVfpLzqoF@H#FBsdcPh!*bsb$XRi)VYHe*tNVMu=WHfZNx22>jU>RXDE=wH=7{6tN1wR~>zf~@AIzoev zRM+M$U9EogCFg{Vp8Y#c!UfwBPwCo}BZpT)ZG=jHHZIYw|3CKLGAyd?{U25YBt%de zBoyhClpds1x}^~qx)G2VU@Vlf0Fe$Ea_CM)0qO1%k!I*d;J*ev=je&=@w|Jk>-oLm z8urZId#!ujz3%n7b!?(qgZFOI1#oguy=?V-7KKry61RQ0cBic%g_LV`Y&|Wfha`?# z{QZ{Yl2uVkoUBp-j~Z}zkF_`41@qCZK5<0E0SW?KK@u!KOks!Ugsk69$jD`XPqMvOcfq?f5PKJm!+E!Qz9tXT7rI zJqnzsgoNs@$#oI#g(xjo)TbU@#@9;^=6HIn)1Vl5xpAW64mG}(`jN4pAJSNhcE2pD zJRKE2WH>LTwb^Q=GGMQhg6)Wz-EW(z<<4S#_M297V@FG#oA;N2D`SnhJn z&4lwE#H`%!FS#oh@t`U{bDS@JDyw3=on8Ereb)L-D5@_+saxE2n<}1j^3YcQ>y>l) zNpq&6-^?vskF0|z9iz@v;*r-(Z)%N9p341|2}hRV6W$|o-M@q#slT&I%H1~?2H%W2`gr<4aF7OOTNIp zf6+Q#1o|cj8|1|j3Y6H@i#?UtvNGQh8WyI0XMYv69&wa1E=zt;y+*an?wF91mHHHO z|CkGSWFX}4hV{+jXhfm5WS0+^QVXkKJZ7hrEyU^V!_u$9swk1p!=?F+*{kubnd*H@ z8on+@!kCf9$CR=$g241`*(?^mT8uGDgiq_A?%J9@RE!+ji{X6}@{Cq#A>Fak=7vV( z=x6Qr2Z9CS4tQTID2k^g$rg_-#0djE|v{F}*u;m&4@5ZGARv{qz?4wu&{ z6}K-p4ZHH2?JJuv`lyWYzK$e}*ztdII+`=?Wv;CbLM&QuM0;QMk+ttTDUJLz+V6^M zlZ9yS=6K#N7HrCQncc%1GIaRz6bu?woli?BzW?BuN#8)O(rg5M=1Z+Z^|{eLE9SA@ zSDC_YXIC+%h`g^j6V{Z3TyIxuep&2eRGV$kW6me?kwWjXK+v(=_2l~3o1}W03~ti; zS9P80IvdwEG-gIQ4CAn6ZudS>QZw$dYB8?ceZF{%&o*Z(IB{O*y&?(KeMTJQBgvEe zG~d>Ao|v8pxfhDdiFU()gTsY+rxW6lSK<}8J+)3uYd(F7Cg>$12x~ud-{j--tfC!5 zZ`)Tp2?2!nI3h_2?_YAuTVKTdCONV=EEKH`Uy+=O0WWe>)QktGU@4~^fXhv@G|xWM zr9*bH>C&-8Z!#sU-`sq~O2`jlxTl>QhhsEY;M<54=;zrdZ_gFLCN0hyHh8OrEDFvN!O_3jT49fA_;5 zuR-Xk;{VIAPQaaUi5?4bNodP`GnT)0bJ}Z2;Vq8(&g1w;88Qy*RC$jQ6E(;`VF+zB z&jJhJb_rsv%Y549yt`R$&X*0B^M!*z)HFlh)^SVL_hn2!)^w9jYHu`&*X>27ID;ub z9%6FE(XEW?Tx`cqW18otHqjZB{ie%-Xq&?`2xZv6nkl#A`*C|Z>YfDSOuK!u`p9PI zlaf<6z9jX8FnK@ot(f_9kc24AQdLzY`DCwQ-ol&>zUFy*4o+z>JvNSn>iiY&+}d6Z zLmIr<^=rnu0jDDjubqIO!>TPYde_K!0LDddRR4Bd>y)4Vs<8FW(^DsK_+ubqv?aGj zyp`InP%jM>yvGS+Q{9AH4JGpIOpIGSk*O^&FTXh|AA!z(+Gr=FrZ&{lxgXMed}7~9 zTYtm4RDy0V>%+n2gqhmG8-q;L=uA?ER6pMJGSE}wq z3iTv&i3bkhzPxmM%~pz^zs?bF^)0CA+YT!#r1JUtHf}cIieYMooWOX=!84$<=a!1l zd>JdYmR%&s%oV32H$#Z|MrrN#ZC9M0U`X%jB!aKbngHxyFT`?|VJ_qUcymX;hA z78a<_$i2LJ>Y~7US#ly+5E;n08`~-7yc@O-4#lCNXQKt}qXYy5cIn)otoRlh>^wZ8 zcT%H|A=3H3krEZPMv*xn~{3i&|ybXmrIehX+7uqe8 zQc!3b5*^AaNFHq`23f@wiAlO5pU2Eus8^fet*oMjPpe&O-=HC~PBCFg!RdzM>@E4P9FJFVvsS{BsneYbI;}&n*y~Z10IS?oq)>`cX-4GRxk6<0>>sv`)mcW{vud*tWTfglm zPew7zzD~a1BtrpdzDveWO7OU_xafgse{w~?eD)I$pljjt}M+NaZg=rO$4^v z^A!0&rk{${ggIV^jxs`U7>Ig0Swc?CJcDY*aKELzp915{GNLazK}t7$BR2Z=hPyy1 zj9G4xw~w_j295z+g0uF4jL@Cp-JRF>x&Zzs#eEu_wyHavzP5KxZie6fDzPLr>?>_x zYwJqkp|rES_ti>K+bs8TtJQ`I-u_bv3`YhA#i`t+78wSVuWK-g7V~zgJyo9U+P*iS zzXe=VHcr)Eph*U26%?f?nuy*cNu7hrQAauiOU`599RaG86FpY8NalEkcdgsQc;mFO z;o@acHD~y{_K@wTt-jY1l7g_B$oP34;%T^QUx>v!2Qj&ci53Zr5`o#oAnd&{+bz-c zJ3sq{QCC%}rap;v5^1!(7|@@{KeHvrU#Qc4Z-&N&#pzUsW`Bl3`UybN_;Xb6gd zpGVBf3h%nc3euMX^K~$TYV`2kFzT7gpA51w6<@N^b{R+Rs*vO%lJsBU2aOr&o z7@;3}gdU2644_UZ0ReWTajIu8;nE)avP2X{{?#sb$&HR(X$bxKgq+~Z-*+fY%unt$ zKexYo`kMmrJ1+L7`unl8t@>6GkV=1H{_`J3(sNVNkf`bz82mcBTk+-AWt-hd%>CVnrrC?~P1N zWyTz5qu|85ZmStDH?VxG`JUW>(iX5#XlE)PYnHtkVRjsX!LiRhPO%XB7?&Tt$&|Yp z;(_{jkhcIZyKh_--xk)w%V%UX*{03xErF`p<=T7hQrBdVC?12CFIzJF|_)A&jP&F z#Ql(J^S;hOM~Y%kO|Y=Kx|%3hZL$1#%dqFZfAxM+@UzWa$;DH@v`jtm5GBkc))Uf$ z9eexx?|Wi4Jtzhk0KrV|!e=W~o<=3zf|vcGrcW5c%n%{sY7%NN zS$~BK!RrNZdlc!TFt{TlvZEwK3c+eJNz>zfnA&&bEgZ!DB_f5fqr+poT8cf5rM7(9 zU%KbJ=tHKk0LyR=O|ejL?twlqAjCU|nY3Izn|E&OHSCvcA$*7!ot_v#ARR$u}#H$)pI3CvI|z=`StPH$9ec1!#5K;K>%F|B|R8W_lKr8%3S_ z*=MZf$Z<|_%V7=T2ySDCCo-2Ry9Nh!SI(;ZNzvQf z0JFfmz=;(PEwHfU{<1hw7E@JRG45L?Y8EJ2VX0Ao)O}V6>OuOV0(FC}3h^FmVExmg zLaK1=MoK$3q|sT9A3TIKd!YTB#C(OHN;FTk>~(pHF1@ovXY@xIK-!nz%n&BLFyA(e zTjk4Z8)U&~*Blkb85^BDb;wsBKJDRA$C1rpjfn{N#Jq(LkAKl3WXOVv@rFJ?d|E`c z52F*zzE^F%pIR<<%w=uvhsE6XQudRad~QS8{-Q6ub#O~sc-Z-}y(@xoaX#fnh0BeX zh80PT(|77QC#<`1(omnsm0*+zZlfwDpSF|mrny5~9Zf8G6fOM(ZoiD#-YI!O5YMqiaMSh{Sj zW&3*D`VD!(=38Sz6I-R}l&mUjy>ZRWjrE%({ z_2l5$h5sZToFFGFds_*azGw++((Vf{kOrIP)78N!;kZ19c3;E^eol?X^B7~5%Fr9# z8*vq(8^bl?ad~C2&J|NlVV2hwhDPR`qdP&n@6?+N3VMv2>G;3SXKd(pVlSZ@zxLKHidZ@}%ixtB>{~Jn&vu`Myi>vP|2Sx4uPnTy6+$FoIRIpHq3LR`&^y zn`NF`{)K-MslRa`cqlOlC?6v}STy-Yta6yAR;*oIv1`v${CLlO-iHe_VeSBr8N$AI zmyJb9(y}~%Yu&?VsIAIcUqZ;bd78wZ_1n6w`6ZL%OzLr(nvMC?l)&)`;r zS%Xhpg}tF3HTIG*c`s>w5Q^rygc6xDYEHKlkAgzDlcR=`twm2G@KGf~+YO5;u?OyT z_n0a+Gs5iG?5sIi?NiSq5oiBdn3;@C0ECtMr?>FjiMBT73RLkhQ_VTMZ~n>ei(@i^ zCdF2iC588w6xxNk_JBppizkp6bM4o}B9EAtB%kcOO*S27yHb@pKim8TwKA7QvrS)) z^xi|gi`GXS+D0RL?{}gt@88fFeXS?y?O2sJYG-vaaV}Lk_7V}2jPR2E&*^f5go~#- z%lFqV;wZrl!*R}Qbm+2C7KBDbw#G%|dUj9t^*a`C-|b61Yyq0!usgQEcYN$_es(%i zBHHTX3xr7te$_5d9ZS6VKU3o`I?sd+i;9;>>7?P|Q6Py@kZ^(cZR!Yt!6No3yUwiA zI#NdWyf@-Ib`6PH2a8AvZ>Ua9?20vn{oGfwjGP{2nUJYJ5%On!{U;T}B8{Hm7(Ow( zbe0Jz^jfr#_-&7D$KuRaOh}}zl$FK9&4y@G_wX8zx_b{e<6q^<8!r_xSmsAGl4)vA zYkcl#dSE8->yt}MgGgg1Xxa@H?nX65A`JS+TQG`SLIUMsh6<$QF`4^hrEI6Gx>Z?^S>IE%e z^nqce|6WD#JH!1mo#1-pRv$qy-Qeem)d~ukXZ9k99*}Y8UsIYI93%@K;Jo!47C=D} zZKa^4UJS@pAN5P`|H&TzWvTyoB|Q$mIlUfZ=)sFY+~`gtBkclED~GPqc~K{qtCjSw z=i5&3Kd|zjcwF(>F_lei+6H5tH69hX1m-8X71xDaEp*wq^*xdCD{GdaM6YX4kP*IB zSZdIc`h~+qyRF%seNM_wFJ65Zqs*Y~ES-d1LnuwbLwxcf&?ba~mYeTdUNI{=9Qo6b z{{COcDg5JUf3D%$nBkmB?+r6V_UwU@PZ7X}eKv#DunO2D^*B!JQH!O7nQebIr;ScE zK)o4S$f==?7zNGt#5X#mWsy3i;>cWZa&5oY_@FnbkJBK{Y2WKWBYrOow7kuBdY%-w zJY3RF>%DlT+sxQ_{F6w!W)s?VB9z!M!MRE8lUyH9QXcLyV&hX{E%B>ke~v-$y4<>Q z)mZOn_H`Dt;P6dwlX6V+VB@;*y1AZ!etz{jIhO&BQ zh2pdstp$cTBXz(nnc&Q5^}JeDN<_(+S93LJ*E}3bBg%z)O+y^?j-J@8IT)@fLU*m| z$;fosO>{`PP+~KfSSb&izqKIW{)_HE$bXW8hRd(x0@}YUcBE$+<|Xi7gQ^XCUN+l* zYRQ>wOqIq$461LEUMQzZyB7k$78!2RgETi|*rry+?sRmJ5gOaA!hByg=T`2gV*BAm zTjTwx)90qM($gD3SDCXFd-Ivdf`ab0IB{G35{DuHbu4HnPdO#(m2JwOc&G;fk+zVb zUQSfMmJ+Oj-2KugA=)2XVE6oWD{u9P8lj0dOl7ET-$y0R007Nq}td~PiLa*jcIP5mG%L^Q^K7GGK;%K*h_!Oa-iNY(cqPgzW zVbJk*5%i?Xh3$b;6Ipu`0d!pK3qvMYS}T`DQL8%p zi{XZIJe-ycQ_`ZoUrSMg10^{9q<2;vB<71i5^Kn{h zrAnhlxxbodfj=Pw8`>9Yb=w1vOP}YW58S`nwRIg<6o>VqgnpwB9zh zId0peudg9eimV|4Dcr4IO5TgIPGf}+Z`SoKR-tkZ)s zhT2{OhrjcFZyc$&93&7wWp+LX-E{oPE(l}%1 zI?}DBiseV!{B_X0*YAghhfz)JIfX#j*|YW&31J^!G(MMX8nknF+ShX&Dzt{8y!m6( zw1IC@GU~gJlI?e08WHwh%xkD!<5iU_%4-6+MP{Plgi29y@sK>|A>2$ho@W*SnoYW1 z>>dHCxOP8Z4N(l*{W4&_Gb~oeRwWieu&Z zXdjRjcy<%_4ZkXhJ=80av;pVWkG_bxAl0R;Y@91!h+dJnr`E6iHVh*}Gl^9=c?M5L z<5{2Ra-|sntkJqv_5TUVL7FAmStbftp`olwc zPwA7l*QZ#Hz&WI%J8yZgx4A`Kvr3(eJx5?zv$B1YMo4LS7oY}hzA)jjmxntzJKzrOCawA zX*Pe465Q$+6HX`KtGN7MdSa9UkJ3$eqIi>82eR5zKP|4Ev91V3*aw_Wq*!z@2O@8PlR$3nD=`jasJgSjrajEL*E8c}Ipht+9+KKLR zw*|#_hHOnQ1oWPa^{?TzsXr=oQMkRu0Uh2$kC;d%#Xl`C@ZOAOSo#90WSrJ*HLX=| zb~@UT)Brw)y~IvG=|#I7%9s zTt<}xK)$L~19)p>U-yvQ-3rg~VKg7qU~Gygw&~4S(YP8u-<6_p+PHuJ*9i(76S+dB zc0ED0#t$U6h%8IItaS6Og66YD%aF{|NsJj{L`sy+NoO>v1?_SpA|l=ysu4vQbr;6s zMX3ws)TWc3CciNK1hHDVT2GBa!~fujiMY-8d^kfZojj*KYmng2;1zv`0*xO`8}z<@jKhOJ0h6v*1Hpd z7^ozA9wp-p4m&l^FVE!_hc(q8eKQi zIFNYu7pa-uxSU(8@4X;`2ibc$2P|71V z1JcKT*tq}oDg0d88V{mr@k@u^P?VT|CdPBCsLvX36Fj>Gt0#lCuW>6})Vi__$ISbY z8}ykeO2wu|T_AyjtP-|pt&Tx-XT3#8i?=F8#TA?{A|TBeS#Gi*h2jqbrxZ#mpTp`u z8?Iwiws_EK)WIK(r>x##Oj0Uv$FnMXyT~Byx!o*p%3*dwHn;C;(8XNND5C<9LNAmW ztT_ZdFgo2)LvFf7YFoTjiRpX-jB`%1-KueteiD@_i?`J}HxPJf>aY1MLl4H555^cl zQr`t{dLKEy$Em?o0NS+Pl9!j?(C&O1e$^qWdgIf(`?sU5(#g7Yo9{Ms=y5yM5D8TO`}GhU75C`pS&T_S_NKWxK|h2d#EN`q8IjQp7%SMDtaA>b|pu zXf1yq7VGKl=224xqc=;faEVy;6bZkA9LciAefma0`%+Z)#wfRS2)VJ(6^@)mP0+r) z+RjiZByCZrX)FcmSaYz|96>pUrL>*~VTT@cm81K@OTX1Eti%9q*=0luofh}keX%5UGp`66u|qxDq^%LxR*il zmXs74+AWdJ!eU!^q5wEC1Mx?@@y0LUTp6j64QT|E!VYRq3HMG&?l!7~MujN99toSv z;xUW6*JuPRUxxwIubV$6Ib6kWuX|!g{vAA-v)TKXxswzf|F!kq{rDJRlCK_gul)t> zhhi6pT8DC8X!G=CB6isMY>G z)ThSaO;F|D@Fv_qPmEfx$*-`=hBJVWj<)4-3OxhXWdcqwB@Jzxz*Z|yEE-b%uqj1g z$jA^JX240KHqg>=VSB5TqCVJx&0e!31J?u4;l?|IWv;2>`-=s)+0?Out>Z})Hzx@F z?m87gnq|%Af(eE8*-qp+#OF(*&olar}cAYcVGblAgVAm1wAU zaL~GB)Lp-~=sEVcn$q8I3Fbs+n3Ssf3q*~(XGFSDy+1s)Bjj3R_<&79nGq2yrKfG-`i$1F~=;;{1n->0p#y;wFw;R9Cc1wnu z9LJzRsna_&aM117pllD@xL+g%GYM&2BVmrMLBn0SQp*SmR19S`w8$*Ts7G!+jAxmQ z_8mi;plP8R%6n;Xp3{`wnsH64WTa5p{UwhYTV~}L9;{cS*t)WVT_kphDZHS2D9!2C zXiIy0`-hHN8=j1z(Fiuw@-iWcjmgTPl;-i4&T_bZgVW~PKcp=*U!hY@r!8Zyy3Lco zt$N+=S`qvdZmI&PYV0INF<;xc#Wx_u>74et>$!ETEFZD^ay^-TO(8303hRfK$%i+& zlq6CtQX9^}rrWeDygXkX%xI7=zHQ8c=GW;Ibqw=1@MT*+>a^h)wCm#!H`h!`$erDO zPAgECf{5j4!4&Mu_cCm@AFCXU^O|S%UO<*8p0tk2SBE<~f;Q#1v?%)nk&cD-r2BFA zCefe-`vB~^od;_M1b$q&GU(pNi_hh0ap8I{PcS;eWI0{R*P7Usy8Xx^SaKEMZlL># z+elJzMr^XuMUf%jH{B9Bs5;)~`xor*qqlzkNPGPAC-@pxhG>fR?!Lf$RFf%C_m#8s zyjb4WBJJLSg#%g&FZ#kd_+zH964P&kV}2&l7+r!~3PY*Ob_KiLRb7{Y5T^}!7;f{4 ze7zBloy5wb_|Ak44?}_JW%8j6+!VmU+&^h9>oO3*sXxMR@lZ+=?_T#ZTX;-_hHm*; zTCuAZ-GUAyR!T||9vQEa<)0emS0WNLd7_#?-oXI|gH7xLf5WzXSOsb&$X|u9hG=aq zkJDj{)N+#^s-%Pt7g~37WD{n*vI=ZdkIo?Zs@6&K{Fhyic~Yy9U@O=2eV@JKSKnAA zPswP+=!_({^>NtYNm-?vPhF}Zd4|{%y!)tVINPT9$T!=&Wlp$AIa*~^IGJI3L3{c1 zLLIsBnGg-0uK0t&_=j^>(ehq0PCawK4S7F)fVa9lJ#C?(oijEz)*PYb zG=jF7D~Fjm)l~PUlI*O2(@<(2*l7h-YAgf!n%vNP6A@g7R8^n{0Pv1cM<4)~7CC_J zZ#{fl#=?UfsGl!oeXF`Pg-m-V?W({8hvWA*J-tVp-*8F>OZ4gG1atwP3vp!NjqRl%of z6T9m(s929NTm|dEMpJ~AovkhYJDn(HSJ2oB096aK1N7ECj2A3s+4Xt9KFBcx}&{K&Ie5f1;Q1NnPpY7mNzEcVaUX-WfL~AXvq; zdZs7t6^F^9c4y)Vn`fO1A^b(_0L3mFjgFgLwKeo^(eC!txkHV)NyOG6@DR>Yj+5AV z9BlSK&4uDO?1K)@Qjr#JAenOUgi?cFZ-!Q}>r5Mwf2vugN#*mv2#rT&00_L7jN_J5 zqp66mnpy|L&zpw;zaciD5(w(Vh-E2iUd*Y~wIQ)tV)x6-_hXCvcgkwV9m}T3x(QUw z!qFhbedFVjeWtG}{Q&$msoPSv>w6a4T}IO2wPxVuJ2VxGYwrSooW#L|%C@X_dl^KL z-B&-{n(CCNqp^M7JXSLk=dJjo7zNU{ITJ>6H4nZPMNS1h0Md2sYN+3vCx$Z^>N_ z*+}BuZZ^+KTFnG`Cikp5E>C01jb$K|3P#jRuK?)m0J@9w#2D480Bmc6@}k?)Q%Tiom*8Cu5_pe)-O4fPCUAz zGYCTWs)McJR~)JBKAOC~+dCc0jUX_87L?%A~@ZEqaU6Bkvubz-$s!6UCs}>K`P?1@7lAlmr zi?aHy=krtf{P3$_RIScCWW;-iyja&D+d=1 zFfK2w3k?-Lu)4j8-xsUDcE z)e85 zuo8Q=IW^FD02jC+vNgTf^^x~-mC)q^3gm={UzcZ(lkAb;#xi%5y-g|f?zi1vcsdF2 zwD{q+Hm&UB%&5_1Nk4f}-eLBHQIE!l_U+?vK$?w@-oDE6&><~KcA&yawho@_|MEqFpvXg18tRVmMn{jD@y zE+x#A`&zm~?0&dIzp*dJOpryUCkzCKsiTxZ76cahY3> zH`#AH35}30$(Om2_g(2)cd2a1e_Wj6cPb?+m{gIX7wK1#(ud5;H4QvOzS8r!Lhf0F zav2hb?QNJw(NrhjwD-sxx5dL|DOxf;tg>xrO0QrcGQJMGmX4xb7&j}l519+sE5YCl zV#On(K`u>HHwWUb^%lDopsd^o`>RbI-f)#HnK~A8rH%smz2M-%<*g(ok`l#Yk{7A2 zJeoXeXHcqy#T7o}fW<+kafiV(-ECod&Ge?Q8#(ag5lC&3?S^){i|z({n!A4XY-Hl8$S7q2ws!ID81bzE zvk8hhu1Iy2s3foNCA7aWYal6$z$ux$%|GvmLnd)$`WvYn+Xv0i)*r4%lx7g7%(-YU zMIHXoF6wk5(kQafY(@-U*IKBZtVi=*DxM5=3X7_}ocD-e&P}Bj{={Uvih+`_Q3VgH zOqNV7R=A0t_JY;WjZuPp%l5cuC$k3Z*-z1coo<%=wY=tPt;xa|?F*A2;#eN&HUW5TYJGgYDO5|8PI)fW5aO()wvp2za8!yg)B=iqHbiGg!D)H!S5K)$1y*YhiEFhwx*@74JVkvL=y zR7+`e5FQ8_-fDMh4?=n`jg*-wJ6|pI-DIvKfnswM)2PAf4pB&V^8MFrMGglj!}*zt z0e7JLB*($^WWQx%x3&bkCn<4!BKTiLcgIB{IBTdb@WvLE-XZYbY#d5@jUx|1nD3II%U-%qz+ zKV!~Go%xw}%nzzFbU4>QzBn+L6@T5p2wvZJek{^kP;i*e=dH?z2IVjAo{XeEQy^QH zpW`#do+yV!DEqoSERnrwX>+&TZ;U_IoE5q}R<%`gw0(3oljYeWar}X=OryIFK8?(?8wlsFPUEl|(oymj-iUvuOuJNb z%=oUtK#;;2jnJ( zm4rt5lj4;_!(^2QKPTO3-7`DlKK#rrH@eheue7zi7d+>hB*ji}efEJWip%}2xJ`}( zdEHqb*@f?LK;O5!!_TP-PFsmY5>N{cfK2|a*MaFjd$y2-iJB`x1l z?}*>p{_up@{qXDVGg*j-MtZ2}(wMeBX=&;vX(R$tWx7np1(jqks zRLW%Exr4nBe95W4L_5=)bV|Qkhn5xtc|X&8lgX^uTK6SqbnF=o<4}?>q)a?C7ge*) z2S^ts8uf7Ap0CE^1IKOVI|Mx+Z3M~M#)M1|2v zOloY!w(Cuti4DY8uxxdwX&!0`Efo-|Q(Vx@_#%dZ6pAf*&s@;g`;uCaL0?{Bgtoj$ zpio1~`maGZD5%vfu+WtlnXu>sMU$cgsVP6v{*MX^2rdQz-D4(pC`EYP@cj77 zG26irZ|g>W<(RFHg#h>goHNZru3L-gi%}re87GIm0Q%X%?5isG#N{fK+-5_SbAPs7 z{=(N(R-k<4HrKzQT>bG5a1x6>7bNPQjGid^Q#D>s(OV!s=AZ=NHXSJTUhpy=6h1bO zLtwiV^?6mnE2@B6oCq~4O*!l)X7>7Cp%tE-X#dM1=8{b>!NLfl;E0Wc+*NZqjo5?N zl~vV3X5(P6-Djvg!#FPu%I6fK=@kFAhHC)Cjxd6@rlaiD*W>UCfE8ah>$d7dU{u3Tt zaK?d@o_)t|xAFn(Q#o05UsUb~qS%+t&Uepdgfk9ha%u4xzC;Zp40UFyslo$t2HHhQ-4wkWdh z5iY8G4J7`zidE)zVT6!j4-Qct(ixuTwv93^eH=FV+&*dIiljHW3NsGVICEcCz)TVCHW9{g!<9-WN>z z6+G5KGneg#>eBheJ)>g9qWQ2g?T23#lY49o@5UKK1{Uz-D!J8*`*Ti7SuMO0986ud zDxyR=^Crp;i2ZuaBSZ^e3kcRubn?f#icEqWtA*|M+x(far4oGDDJk<#Haw8Wt$!+L)=>{0 ze6l_WGOW7Icf zo4=8sAGr9pZ+jkqLX_kSOjv+N5^l8!a@po@X^!}A*gT~XLA~x2E48z_$EP&=`I;EL*v%zKQ3euyY{+nH~w7eH5|6hYVjScwTw;8=A)-89lfj=>XWPhy{CR{C>bz z7F%^D>5Y;O{nJUBU=~n)`ZDsd%Olh?wQoEe#%((1cs5sz8{#nzs=uMsmGMKT)B zT+z{mPe(^=`g0WZc%_K+(nG%i?))%42q!pwfm)e(pO_9dY{3aUva?k7Kc5K5xc&*` zu8J7sLm!aYgHm@v$1ww0Wb*d{t4d}t7;my<34F>HF>SkvtOu}-dNOZ&$D!ig_UC-U z3rp?#+TD(!=fe=2;wSY!`V0EW)uMB>`#JV`!rf+l0DQ?S{y(im5?BdF+9LDXtOCFa zNZVq!ps2-=1Hg7B^;>Zt^^Co~oUwL(%CCQw&p{|#dC*OVe{hJg@fkE^r{Fx{iJ(6j zlpnb42f`e$JERwYMQBf7RKqUN?f&cz#1p9-S@=GV(tc*55G4{-_xAVZgnQ=V=uquGiUhEbD4at% zFW3p`i?QDp%2^GzJd`tV+bpq$e z!5hD>Ga)9Q!HJUStNJ!S7bX91RZDdZ5cbj4mc`5eYSI5-10H9fc9dc`1%91pPGAtA zQHrE5Sh#;fb^Sxo|JSF;tMD%$w3J%^P8)*#;d~znI(iXN(Ubq=k6MFREt33Y zdYVCR!G2Cg3Txr1pGS~@jpa~2$Fu-;>Np)89dq7^Co~?vKjadaLGlIbE#jYV_}O-S z+pkKP#tgHL{wBacn|!cMd~RdLL6#Yg=u!r2nqZ2tw-n^drp-$Fr#;WV@X>cwOG=iu z2Pv#QQFvEO8KU=4@`NTpyDVZ*-GSW_MI!#mEB#GRe|u^N4u%%pW5j(T=qYvjp)%JQ zs)^EEbN~w>Ds`H;Co5G7&RHh@+J>7A>2+<}_jto8^k)t(pT`|Wt7>uYK9{l>Xu;r0 zWc!SgB-))$h`_?T@9?tGd?!?61%(C?**4ygIB8wv6J{;?mo@pD$#1fvPm!s8dU%gl zJv|Iv#P*y)J}Wb`863Rh#+A!#m;PV`4w@(9%+!q!X&y2(HK4@LU zL8?g*EdBTm)^gR07vcDFw{u+86Y1^f(7{DY#&A+WzC%`n{_ z?+AKrMGax+x&4CzGQmI}>Qvgl06iI)pUlc4t$Xj|Bf*pLI2iy+gq8epev<6C=ib@N zIQC71_yj+&RD#_}tRxxJ&=>+3 z!S{GKVj%pis(?x3uy7z3{_fi@y+$ zg!x(fEeTkDDO48^&PH6i8a37D(R;5WBXsL*^EMqapRDTwW+3t7lxgx3dWGM4sh8!M z%D(4Rx{V#B{WTLscrW|R;Jvc$T9@_dA^-%4pupG6JTVjA>t$|5juDPVvvY|6j`dI4 zkq3^kJH|kKOH_*iSYN*3s->-gIs$`SpXG;7hA9LXc+@afnI`YOlbKNG{(hrk1bFC6 z##VPW&Rm=4TnmJlNw0TXy@H=>MnN&BxiHL6-r<0zJ$&~R1Wy_j(FUpeHDAwlfX zC~LzFPy7Ch3!uRTyy7rA461*X(I1A=Qw$v2BG}hoJ|==5CC?PvL;3&mq#!Q&QSsL= zGudj`{f9F);Dq^kEG9-GPO>0fOUs$dOJvdCP!E4&-5)gi-|34;j+2P#7!K>=U$a)< zmZ2vdtbm)B$si}%7{Ecl3DJ}Ji)@6L2Q%9HNo?siy>Bq$qVTWd;653}poH5M3)2H@fG4L=TJD4f}(a-_A;1diy=dt0KL!DgVH#69@ZlvgG z?nf+mySBA@rMF~dHBE8uu2n4$rTgqIi8mj#NhcnS9>qxepE!wui9U2+e-0^oy3hXH|9I#RUnQtvobRM!MAH1rIDWgOFb#}{jOJE}>^~0q zx1SzQ3oeIyUB))7(EWeT@{e0OlE8TM?wE{{{TpSz-$TU+Cal&qbn%~Q_{Y;B{*WL% zFrH4hoBsc%`|tPs-vay#=l$OT{BO?tKMD93(f|LU3c6D=3oGONlgdqah}AUpI8pt> z;bG|A`IN^56(W-+ewM37Z(Lhl?%a{#nB{6nS5Rjs1U=Ts1-E|W{s!R)SIuS(X1r>2yyB_%sg-6PSNzS}Kzthu9ZYs%Tbs`mogEO8Cj%%uP&vB}x3q9POz-*rK2;qxw2Z!oXHXN9WER zu0WcA3e$0%=m!^rvYZmv&=2A750;gkp{#Y`!-OfN zF56B9!o~)|?oJ2C7OjAb{+xYZ0Oa&f4*K}vb7@~Ai+wN8$LJn{ zQkcqt&psKIpKhqGPArt2QPu3~W%KwX^wYe0jOiP~^#cJ3VuIN{z-t}HO|q1qSbc8V z^D`P{8?&w;Wsj^WfN6A(h@8M0kfw0GqWDvB%N_OO?AW zYvs%it2@i?Z&1=t&$u@v9s;aDPnM>+C-4C5hHaNeKQ4y=`E_TLQ14G~xctByc_aGvOlLf^5RL$O|bG z%-BT^pMeh!%XU(H@??DW`T>n%qt*R6T9?=t9Zc?P03z!TV>{3KY_iEk*Faar%#9e# z5aNwC$nDM^{eS!hO?#8G_;+MjTaD=0UR`obU7J_SypyM6n^l>i)mm1P(&qB~L4@U4 zYT0%;(^sY+6)^k}?weBHhi0xK*@_3`I)aO*k}slPADILk7uIWK!9NlNT(>$84{O!B zsd8pb#fTJ%kPpeE9xtgRwKS?jP9M;YKdbp^?k3mZmllb2l&vO}j_0I`1W&cW3P)4x z$5~Qwsa|%SGB2dM92%P&NF3KvB0h&@X4jP+V@+C8_+oe?FeLpw-A^-CO@Rb9PJ0>X zruct8{vv@3xI*)_8R|b!4^5c_S`1-7_8g`Nzd)@{L`;Xk6uxB-D!ngmtt0EPAj9p4 zKeDc>D4awqG^ zQQ(=@X5siYt2BONhfo$%mYnGbUlCD_sa@84-=6UsbG&)J`QRBXil!+i zXf|agx?K=Dhb$jr=7v>bmg*?bN|JK9i5m@(=b1~%XBp~-Vu=^ju;|I_g(LY6zKnaq zi@y$k)0p4L3fCt4n5j8qUf4KUvB#n~wshHfYK*_ky1F!9A~{+6nAOq&O;`D^d}kJ!dZ6ue_`olJ@MTD#kxRbFUM?DY zL-sLJ3yZQwmKemIuJ>`UErO&nhp|2?zpwU&d`Vex$~>?5t#ei**-vAsCE&J5Ok=QM zsvl2oc`~NC7}G3(U<8Juc9afS`qBn1R|?beK-v6QObf>)7K<*qUi#}@rt;^dFZa@p zt;pG2m4&=0wv<M;50W(*uG`KLAo9Bc@)iPARa%H?6Wp zSu1{vtt%O~h2OTf)V$U*r{}X16Jb(x!8(wL@_V&h;rN$P<2%RRPuqqDroTF$4{$6Z z>ic>=GuCW*YoE;jtFwa_EGhqeXBdian~e3K8@Q6?D8RgOwGyWGf=( z{U_SZXM;96DI;h88jzS)j$ecA-R*K#DfFKDXc4mZ!^^|@bDho7V$wD0*VqD9lIPgM z+q+!7EtnCXuG?fQNJxwNYK_5%3UV(gX=ccc|tF`W1=>SqIjP+r!DEOJU;IhOmIQMUDCs)r6nwR@Pr(tnDvecHg8_icU)^tb~Ve zR?Kplg;bMMJHDOfRG7MZM7Sl{z+%SAP?`R8Q=$N-nZaC^q*Q5Z08JH-$Cq>cG#(w5 z%+TAU)U(nQs`sGwB(M`&&caCPV{G=cHb_fjOYVwG_0>vMSiXLp~ zw8>UpOp#ppJj)FJ_Kx=1Q{(WI0&?z>y!#k60n&X3H4qM|C;yXu03znmMU+$fAF+@7 zlO|t5NMtt#h_v|6W)dnOr~DeNI?YK<9+=L+kBp*d1g)lh3r~j|7Tw`s-MjGb2dr`)pxttw)Eqnr>Rq{C^%+3M$fpWs zQyX^J?OpVd-^8a4o{`VYS@fBWCfs%;=;SY?4=Nu5uS=dWH~9H=3F=!H&G;20?omz) z@!~iTLV-;71KP${1F4%!d2p7b8}H75B^*w9_45)q4$F&Wak&&5`C02zu2AAy zEp_G%KF9B7&yW;Xa8rUpFl>V6`ApfNgu=IvRqV*gD3(c0Sc=3n%Shgq)gsN<9SYzB zJE!v4Pna%OP(R{UQ?q%SboGrltZJ&MWTV8Z1C5Ld`5tuuVgo5^G@`I{B=JB}-9bT) znRH+-cg?@eb|KT$yWG#NOcz}+3m0;<2!CJD|7xBKJnw!KAma#l=LxbWNguVMLfmS< z;ZDgR#EYY^pWTAW&`aAMOsuvQ%%ZJThySOS)7q&%3BKwpqwOP8BJi=6<5O>SrlpD( zzm;Diy<&e2*j*iIzH*hH*m(O_^Q=uu1yg3E;-}9QqR%~EE6XG);!$TL2`GL?4-j&3 z$nga^ze)0;QhVs3#)CU^MW+nxj#(CxFkRs#AaMdp8iEVK5jyr`*QRwnE5Y=DzwSqnh;60~ENSSetdXBUeeQ*4cDadV<4NiK z3h!kaRrAOu$0GR5h8d1Ep$|G(7s8hm`emZZe`b;r0vQZC1&+FW~>yEfn z6)@SI!<7yLlgrWtbD%WU2UgdELCn1>0^Mhb3%)Ap+e{YicAiHRb(S!8oxQEFcZWK* zN6LVdOK1}XLa^1SPD42nW%J`xd9Qh16=ORq9LPnyS6dlsDz3@+eH9wOmg#6tQIS7E z(ja5%?p_a?lXp}DkB29eDIVsIBhx0a1u9IlDJK=j0l!NCTI)Cdgu6pIf!a}87E4;^ zrB#R?#UIkjs2`V3JE0WkP92cQ-WKRlu6+4YIHel2+9G0t@2D0{vnwyWl>FO#|KLX> za14=>4k2FDu{xJd$^w@FLTSyJjq$H-9J)u662+5t#>4=Ay1chU@qLb}%vv z)3c}3(FbfK8$}{hS<_P>PP1)Ds*23Bf;nd}f2L2WL+U7L%N{^WVQ#?7N5A#tk`enr zS?E58y1XbG*W{YFBwt^r6@tMeM3Bu1C39es904hv6?R=xC`l<%s$Awo^Z5?@@9EuP z>96$2=BCYk9+jm2hIO3Y^vWl`*+XC%cZIoU|N#87M2rv(az7d%N zQw~2|hm`y+*Vf}>v)SEuq2Ns&E-;S6<7;Z3t=owsKA`!-uDCCoV zX-8ooooP|vxwCI`e@+8h%~Q>pJIJ}~CTG8d_*!@ByBtJOe~%_6j$khbsNqiLcPAVl zOmena{N%QT#H(J5^Ukdfj_u`wjua+DSV&*{-sR|wM{0w)Bo$GPChgA@{Mz;N2GAEH zVCO6EekGxbe^;oj2MqL_PuX|)U?>PkV0INtp#Xpa(SDA$uS2%UKrc%FP?`URcr2(1 zpQva~&R@%>$e$Di1GM6B#O$J3&*|_85*y9}q3WCEKk)rzEA2H(yY1RuzTJ*?19n0? z^E??HPBEzG13!TnPasVXQ9`oT>zu#-LiRe4@U-B=-S6xp?l6dnv`GtWlljqF6T}5K z5T&a+B-8-)5ND7D#6X-SS=`?<-u+AM12qA^XE1l~f3IkCVA6g}L)q`xQ^66($}wTm zZ(7TN82m6y64ZWaGyt0Q7rT7xkUW#U$i)Le+y( zJAb9}pydOYiy49>GuFy!9Mq3_rC(_FoVLN8BMCs73;-OGjncD+$-fUiCZ>CSCQU@F5=3H=uHr3r&A{lM`?U>-LYg9wiuuWR2iX*o5&nC`wBM+B z=m#9rvW}C>WKpK_X^_pu$*eCOh~*tM67E1dB7eO0_xtfG=kp!|i>vYM&fI+&kk-;# zO(261RjJ4^_%$7rS= zKA2mlG!U!s!hX7+iJ^m#@EPlhq4Ke6wRCNM*+q`e8jd~{Y`nMm9mn<>0S9W~W2F1f z^1uJkYIwfGOcbdpE{uX&9F|=$tDs2F>eB=D(qUOJ(1;M!x%VzI*94_I*V99dL*hO~yj{CutVFhSNm_Np8+5_N=aMKDLsJ|5K9c4+kcMm1pFK+U zAZchZvox;9uhQjN-h5lDw~rh0Pd1>EvpMga(j9H7aVDuPzw-*&8__53zkUjUZ1{3qB3Qs`z@bA1eAMRmjo%hhNMfv(< zDOtJ`lv@`pP4Y5&$?XAfvJ3~iC+8R-{U<#hGloY9iptxb_m!bv%Fb3kpQ&PH^#;VW^q*aMrmux|STDYvX{&>tr}A0`ET&o}Og z4KXQ5b2Q$5Ns$)NR{N3@JyY+OXZZ}%T!r7f1ION9yc^)mgQ^s1L_%`I>M_k`^x2W5 zY5d+9wO+T*=wfj4kuHTEgDf0jJ?uz|{E`DNX^tQcNLJ`7o9dR8dgc2!m6SOSHy`3h z`M--em35#>x_swR^1nw$Q=6%#6&ktJ1yBt6lYWsY>Vw)vl8SJ6<&sI%zk2+WM3ZJ? z3uK|VRll6}Cx-p!qkn6`f5~kAw~v_J0NT`Y@(tThkoYH2{%1-5Tfjf0_%A=SMksGA zN@N@T%rFpm5;6P4TdNy1+7;h0swXh}yM>Y36-ks2iftUW9NpI3?X#;9PiHH`bh}SZ z>@~>UF)ZOYe(L1u`V*o{PjPbVb%ZyeHf^Ta)DyRDppyBArJXelA5|k zr1qNh#U1QW$e~IPyWLODUX2-TBR2Q4B)a$7+uOZyjE<9~N~`N8XshwhJC-C>P+kC_ z+Hvoa=MzdfN_#^+?mLOSB)vJ$Jc>;8`x4jDfOx28J@@<(9Gt1lsZ=2N5&dBg!>q#m zqUd+3@wke~nGGd{r7$IOEY*3IswGw^#H(`aosG*Kdwag6?JfHH9mjSkBXg2h!=-^F zkA|kk%7GB-vp$d94z`Jlo;+~18nHRL`zNx)J?ZF zb%37Da`4a;oH9KgXq?_X^L8Nh0JA1zP=yQ^_#) z-F!D5Q;UqQc&Ff|5Y15r_Jg-CRxJKC6GW^v!0YYrhF&-(%$v41?ph^|^1&VE6`JIx zm|+;>=X}%g5Vs7qh~*5rBf7>xH}yiCdF9+-*$#P0XUo@%^MWhG=Zf?E+Tyv3G)Fi% zI2>%t*e>h|L^P(38_Hl0ws?xrAe~|6|6uGK#o)k;=GQi~jZ8*VoC}`0a?*9~MV?vt z?LVO2#25GyDGOTWB){#t!;#f~v-e=bF1Ib528$G~xQsJZcyjWU{BG*eOpp%3Ip9+g zQNr_VFb&wgvj60W7qctOYweM$Ut-DP(;ZGrWlWJvl_?=b*nSE}B@n}`Kj?k=gh zgvdJvjID_3+;X~6d=nEhx<06$fmC9!D-tYy%uEggx;FJXBHC7Ab0ZC$Z171uFVudx-C zn=^U)^B(z5bFaKN^JgZzc%>Q)I$VMu9*L}bUVj2}3Ia29iYxQ(+Qkef$*=do2VIR{toQ>1_j~HiXOgO9w|908k9=^A2WP=vW_ zHA-DQJ1KF|D=-Tm99bpB(;_TzE_Fjmj+O-#={z~9Z;~tifUE-3$5saEr zfW%Vu#@o$krOjd!zxB5^sJFXOyexaTxP2Z%^JseH^(THd*EKa;l*%F|c5=Py6AF#P ztGPXKvSM_4)hoGq;i+^QUqDbSgoJ+XmP zVu)1m;pIhx1JJRs)3J;<=^i5=)H%=3BbJJ zYMQ&-mX~_uUB%so>SyX4+vT|06(?f`y}sO&FLrU4-~7D&S;HDeY>1p}?a_@W>@EwA zXzckEYS*2!mQqADED5nV1F>{-VV_%$d~%C3Rd;z=Z!KVZ*(FlSBFMpi&s-@SwaT;q zhB;S5e5xaKX$fVLYV6)Z!$(+L4bZS&r<{wYZdl@mmTKs3x>kgfKQSwJrw>ikc@H z_bMvnrj-bJE)6qpC>Dw2J}ap*;ootG5&zh&PbJ>SC#=V7MiBA1!2mwH{o#5+q)J(d3cytCCE@_zClpMrE@mInJ334U^L(-)v6~{Y+yua!i#DfC23khU~Oblcmp>_4&t5+>BOZkLE8>fuS+8XR;SSd3Rzj~Ww@0N1z{pF zTS=4VvL;;3Yz|kdxLMNdKv_GIc7w`mbgg9*H1jB}ujJ3Yu}(Bci5j!D^ubtX8Y;%u z%Ln71=Mm>?f_R;ahuBiLN4-K##9UKNW!hyNJ(8Y3Z{O~29!%Zb^a~BvLb}J*4dJI3 zL?f5Swv_!vbtIaD9VRL!KWx_ejhmF+Ze33LE0$Mb&oyWl5g&I^$|ypr>TCCA5OJOp zgT?t_mUdDE#@k136El^z^t(K<H^Vox+CEck*nhH{~o80jXtA7cF?F?9-XeFVD#-N?)OB(i!k1I8Y zd6fbkg|v@Z%S0`%b1Im~811&t?9X~luD@V_IKIvGpV~@fSYaJ zN-A0dPA|N8=A`Sw3kXheJm3E7?&hbU{UxxHt>H?{dc}C)TD)~l>P1N`H5%VIi!O}C zPnEjVNTukQ2To5mEQo~nW$T`uC_3POnkvXa7thv~|3{(^T-z%w7s86&8Y>;WXFBy) z!6b)ZcmKsW=zUN4_E1rB6M&2j&;-uYVU{19TAR{?p#C3B>$4?j>6M` zLuK?S=4I9nt$OCgh7oMt0uJ;XWulp!V@tL(s6BcMi~>2{J(G3q%Llc7W8BzC_Astg&QEcBsyMXo7V=yOi#I zL+d*Kr75IVV-7)Kdz*b{nmEOkS8E(Pi7L7awak^{EDPd9+hQ2U?smm~-<#f|wzNnu z+}h4iBljW)MmphG&cwHQZNlD>2$gYTtV&~Ttv*~fXs%`tS(Pv0+$FOu?exDN%x`rnyf_;+>C>h$=rC)nQv~{N$))cBGH%Dx1trXS<1N z%bpjz`z4t^EDk zuY=V#u2Jg!QH3oxIArD))Rg=rEQiYlc^=>*iHHv-FRYuABs}|U9c{Wk`qcONc;Em6 z9B!4%^2s zSMf4sAV4UTVUX z#4Q$f_OnB;+y!_*Kk)pIo+-CuA}i5DQq_Bfn{%QmPj5LaYTAjuY0~xaYi&#!HH69y zDe0OQW6*K;J*yKoOMLr+Cz~{%L$Moh!Q`pBPmv0ou+8nr5Wl3j5^$r=L={i&c$M4p zTe=5mD4UdXE~fQR`bi#Y|DxJ+xafmL&l8HfqKK~V8Z~&UWD|Gi>`Lq6s_~PnooNg! zGi!B;6W*p%&e9N+37uNE_F8hqM!ms8N!@ja6R|H}b{|WOpJ(1Ps9KViD@l4!^V4#J z4+DS{dLmZH@(gp1!ekwDq+BB8W;Q#poy1KIGgCQEAHR05O6)rm_i6ror>0kf75n=6 z3e$%i*gDrbXn>^iOLZ;3)m2HjJLOj?tjv4YZ`V)8XK4jimadL!(XbDSF}~*4CXw7Q zFy~5Az4%1H(&=gR9BEIK9Q}#uAfjVy613OHy)4=6lHtt~jx5!?oYj7MQGfMES3~z< z`?GuSb+m80+(r?4X+9LQ1~5b_v0}uo_whA~p*rQMWKvp39HwvQPV9S0H5&4s2(Nxf z!m%4m-tWB^dX0GNQ-cWCqe}B~*fW#MFNg%TnUcIms4H=$W3s*C+}otP^AmdVd)Ibx zae?~Ubpp>j12+`lU$^Y8c%*t9>?+RlcAD0_>(iz*_&_$#A^I|tB)VVl_B)^RQAlR4 z3OJPSyp!_kvm_)rNSkhWw%pR&VP;n8C*6|~crVKnft~F9T0MyN*$G>+?uq5#!ny=D z_Srn|yrIh;fd3epXDLU7itp_ihU6-()N|HZnVpi?>))o4IfkRsuy2#Nu}w;y-yp&3 zBJ?^rc}Y;ulom}WD2@V0ivYO4!XQ4R*BhmlI?A%4P>C8ed+j#%;kw$5=3Hq92Q1De za7Os@O3uBXDyjnzl^74~CU%ldKs0h^r#)4XZb2$y%yLCJgjY~)Ja)yq@#Uwg{6Bh1 zCX-zfHc|sUwRShaEmI)sImP>OuU!}(#a)!a%EIMxW8JdnD7f{b`MDrSp&#z`q9?X8WGx1JA0F8eH^llRAyX&R(z zc2+i9^d^}036oFMP!-=QA&}#KzI(Az8)`Mu{;x$9*GeZ8TykW$hd~n7W_o|!tF*B; zi@T^YH|u+n`lQ~W>7Nh)5QsyFjhdOq-U4mIfwlug?{86SQ8iORJK>PJEl0h`q^uI` z8wlpbjAhtJ&%=k6#h(ebp%^KpZ?Tsy$64O@91U1D$p8Atng0bY!n?;Kt{!0Rbal;< z_de#kt2279%Tq%SpO{v4b={9IApWz?19wu!+u22n2P$m0Ta&uoc)@tL0c3I7A?uAm z6gg*-0`A6Wz42DewO;=m&4xkwrSQ3Y`?%5(2J=}l;*#@lzkJSK;T<0V0?dQOB=l2#x48J!XT`UH zJ3<&Lt)z&@n{(y;d1ZTZEio^YxwM+j$EY~zUThGPsM)hSIW(%S6Uy*8hipjQlZlP# z&FqdqyZbluD8e19h~r5C4gC20$Z__djXt?_(fU9TbmwF2EcLu%y1NH`LYtf6#^viY z`+F7X6&2Y8WBiVnqMO0)z&^f8+WVWj|9<37t(saC*$lLv5PKnruv5w;-qScxdAsT3CXDzc0KSHtVHO`;*#ZG%D0k8Bv#$lA z19hpyuR9l%v?*(b(Q04y4Q^$0+X7w~ok=#Y>^ws#`&`EkuKUVa?5BkA|QGr zipQz^*3Su*bd46<1>MiA%SZAGhr4%Ja`+Is4I{2U+3D6C8L^pJ3dr|tdz(MqYSb0A z@r%b9dn2$DlpT??b3qIwzlT{)2mOPONk1qXkP7dc{~+x^4XcBaoGWLYN;MCRyO|+K zmWQ6s8M&Zr)eiQgW?J><_`rb;r#937Eb*g&|Kxl=1FC0^lKEhBv&ls)z0(x@$yZEgDVIT0 z(iH}rL7b(nla`)tbCL#(^|v8suv7E=UcZmT zR!(7|_~W3*^xbFv0T``NP*?0O$8+oqfbqy+ZA^2S_#I!(v=^kxi5~me6BHq3uj+Xv zA(pcOZ&F|Kc?muBY3T@G^3k%n_oefmpXXdoXoV&i=P<%rsy!mczn>oaa(q_qJ-_C| zs;<61WT31XUEiITb_qW+daVwKOK{s6H2RjdOlG6;%&{{py!W)BWtZyX_8j66x^S$t zKoH&edpTgM&PJz_vH(q(TmMCVN_6LMza6pp8d3;Qn}6t-La6zy?$~iK=Z0hV*HjuS zqiw!?Il(pJo^iOFJ3ZWn!29=dtS%$E;I{j<%0gy7FVSw7CBVn+Pa)F5?>@9$%Hyq% zcUW_V+d7`itvIhQidgN7ra18e4u_y>za4sb>tu#X(fGK8pZH%NbLED?b{?qB^O5{q zU5&s@Ahsr+9Hg5M0*AR1VG)8WLFU1NnXXyXpa>jdb?-gH-X0*h+@HR>Y57#u0j@cv z{gh$?E;0GzYI_Br@pU#9?IxDJONIiH?~jm*!fF?flL9~`G4&d)xrw0XU3a|Xi~g?U zX;nbr;kK?Y7=FUYeOY%k9!RJ-J&U&7Nd{82r1&Aj5amQthNSRUFCa|+f;M|81cdYK z>!C!46UYLUBF~+pXc(2V@H5E6MNW1;I=3hEUBd~VNCi{}Na+r@ez2gR2Jub7FmN|+9pubK>h*cv~dz|6hpXA z2D7Y9lWLK4EQ9&C=%#Rw4!ZfEaKvINJNqZl<4jtsiIxmvapSRP-E>aja zi9F>%lTJDP2w}M=m@?e;{69vN5PV|0`m^}Z zgh$`5N#Q%e@|D=+xu&au_pBD$O*!uz)W82=1USjn(1_e=tVceHWQMHArI!Xtqx>&6 zB0DnBgq*Kv!Xj7=POcv5!FGl5zyu-w@5*_r5^A?wrsIL`YG!MTU&HtCQ_~-$W6u2+ zbL*B_R8p*Xr|!Ke$B5SpJL@Ow1$Q*z6Xisc!g4{ED$lEx)2mA+m1$f|?S-y0$Brgb z76&tB)ggT(aZjX9HX@%GB*~1S30T8GyAZ+UfDEN}g<`?+6<;_uiD)$|f3PU~3qZSd zBv{J7S0euq(p;`*^o@k;e^zodR~KQ1BEY6Bec}=8PwO9~<4eZa&uY?~xTvAXmUpw) zwAVa>Xc)NH@3z~9dxBYIOoo4|U%@<=NDt^Ew3{S6?=J7IPA)NF6`iHX$+dDIqR^Ll z5^-|S5S$Fk|Bv%mmeheq2oW^Umq|yv@zrNLC@ir;1Fr$ zc1f<>OuOBU?}rX*oj~F<5l7pp_=8OcQV$XefhPB)c@nFN9Qm#+X`A1zmOmp}eVO6V zoEEWi-^>#LZ`)eINZNTe^4Q&PoR5}O5X&+%J4~*; z_La)IVsdU0s8LXexMpquePP||TaWAj_ZirP2J%5%xa;mjwd*(vWXwK za4@&{Y*HG0{_`B=PMri&*>X>)JtIh;G+>r35Be()MG zd=zNER0d-)Y}_Km;s_fvWNB0>9@G5t0!m)gdBkVjVGNFy8Oy(eL>F91&tdv*h=##z z&HVhwI4B6)t8t$90k*E7xuZScI({=2;l2Lm!|JoN7`krFp79HB5>Uq5U5RwvzDFL@ ze*x_r%3Ljc@7?o-RARfg>ciGGFDE$5Wp^1<97*as0p{oR8chgp14eOJ9*&Jxn`~Y& zfNo7QKrCa7H5O!Ln>3)Ndv?m-&u*mkk!mG}pO$kTO!bGQ%c@Y;-EJ04T00dpiB))m zb`5|HpL2gd7~w9K8)mG@yRtPT@5ULq8fVjjl6a_?+qz!6jW8+B5qBxS8#sEeY1iag zHSO$N$+cmph^P`WOK%S&0YS61oTq|4$L*i=cqToR$!N{D6|Y0SRYIZ(L~yf+94ryj z96HkoF8J69d@v^SYm7#O>W{ksNYj)i)oV`HdQ+Z?ILfZ~MSuH(qHEPwt@~d7AB|RK z2%+3S;Fl)F!@zuh=Nj+H)dSoh%`)=)` z@t7289|ybp8S4j|Xg?@&zEc{(4siiC^x+!Nt$U^`Xt($5GN@flBrIIe6%DRRLF>;wYu&X=GV=CM z{J4A#*sk+Ox3@g~CYsC?Q9U5@#9DN;9bdfzQv-D)c z%j#R2=dyL6FLqKVC(Y{WaoaNYCJv0nf0NG|<#y`qi)@SbG%No2_V$R+G{?t5ZI!gE z2SiBkn8RFdl#N)x+ZuNmf4?hRnCpZWx23u{V%k~Rv2*txn`E~A>z0F}%YPg@s7Duo zW6OZ2{JkDIlfsAc_mM`8#yszSWek54UvQNxm-T>|Vp2e1+QQ-8T9mxKia-_74f|wQ zOe#3AMH}eN3zcBmlmCi!Ez1_S_K+=gbI{8%8)k)VH+}V1|(@2tq_tbJa-@(W9c@e7~;Cea=GjhhhNLg3fTq2JPc(p&0 z@(To~H}1dg>x#gss+EiIrejxTQpXwhryCwcaf(cB{PfjoDj5`19$f2_2QzVMlmQy} zr+7K$p_EXV5%}`n7qt8LFRLMrRbx;#j_)Y2DVRtIcR`ATNTRw4Tum&4CmPzDx2Oaf~%a>)}zTNFE_0gQQ|{?30sP(?MF82 z4u_ri;e)@+k?LVL=+>~iU&!FS8*r=}+%_~&VwCBClJSzjcNgBs`-76B5JZxaXG4=k z{_PLF(VaKUlif8T`N?*r+C^PL2ZeY)_>ksI?FRL^!wAGDyBxEMO#U6^lo%Pt_B~=h zMLN8B=1w>{2|xg!PbJ7i!~3%B_RYe>DYmTP{(sjE(1#g0$0@O ziti_z5EPXxHH@0_; zjF_+Z8vT1SL)sYiyGTL1%aZTLV#b!W!Lc-PqycGv*4;eK&&3P2OlG+O>aHDS81zCj zyt$=PCdd<`v%>WFR#`Q|?Pg$tYT`5j^bfpJz1^3`w71nZvD=3J_VGk!Z>ej*f;oQB zCX$iVy&a4HKofXR6MLgH?gSKG&xl9R_*-e(s=*}6_jZX-ly<+OH!-1VFt?8bse6ld zlh`;|%D04w7*n*n&)pe)!(t+b!foy*)grVuzuqp+&)zmhl+2FkBLPX-Rz#&PUhlb_y}eyL0g zi3Ka9a8Rv?)?R`6Q?rFDa!v5WxqeG znVBf}Kb5N*Eh2aOW$*v4*^q*{wUzE6*W&KWmefojvKfG7JWrQpwOP1D(IU zn*|-iT}0zL*0jBhP27mdh~ zjhH$l$X@O1sY7LKWDf%#cXm@5t2oYm_4rAoa;Pd!$6{W@{WiJA+!{L9O2?+{qqhJ% zCqy?yIJxdAOVG663SDZxBqpYx6S4kSs`4eFKPG6VAX1*74h`B{GZ0ysO^=Ll2x>me zi@w+NoCjY%Q03D4BB?i)zQ)oYUtc6}Rfg1Y{_}_lsaZGJBrUpcc_;#O`)0^a$KK8d ztsWcZGE*&kj60O}cQHh_H#5mzmO2Z@2Nz!)ED+&IKj>j4jjFs-j|6msmbolgRVMLD z2MdD#Z++ALY6izLJcE*U_L)eE^YP)rPnj2)!-NZvhu5*_ojIbkyDcQC`judL<}*79 zF_Vay(o^*T3pkAU-h1cd0N6ztWdlH&>nMYKsK@-q=zaAZxmXT|*-PKt> zj?Hu6%7BuBoS3}d?t=c4aL^H0*F@El6}OVQ?;rJkZF4fR(W_=>%p3G~m-<><9O#b6 z)Z^`f*6r}E;vIEw;w;Z9C@4reW5$*Py^{Qv%2ch#RGBRdd&-S&{1cz0C6m0BLWi~z z%6|#_5)Fg$qjGxot?G~-e;Sbv@l8Liy?@c7Bg(v6hr+6z@7$&G>EDU~i1U-0uZ`b! z@_q_mzL0C_EzSMUGhUoRjipPtaGjAOyp<(e%f8bJpoguU({Rz-tS<}ni74Bo{lT&aCdmCHoJi?SH%1s7I- z*3M4!EQ{8aeXHJSUg9)V)S_#Ao)>T7Ib;jR*E2KI1YE2b_aU2Wgy^%$I(EHrygiH- zrppB4)=p0ju!|W`h5vXO4lbeFOwGO9@l-II(U4T;?+yWJ(oqgM1&W&%5HccS@t0du zbVi)UiB_(aAcXl1RJF;>Iju%9Erk#fZcZZ#3_)AT*YUFr#96JN(h>ZZhcpqRggyw2 zV=FT~J>5I6;MVZx!_k$W&8?^Z%JSDPcEXmAPHzfJ@;bQO$Yd5aV<2^z3@z|s>x#4V zb?EkWD=@R1bG{NW>N##!bWUwNLXxd@b#`GP+kE2AFfaCBztpRFKCh(8WkGqAsPF2e z1D{lZ+v@5IVw#H>!6h;#mQWcyuRc@;91nG~W?lv(WzL_`=U?(GQCbSf5Or>3 zjVhhVF*<7oKOu>Z)Nn0oKH=up2m0$ldkZ{q!zN{Jw)pMJmG;n?)SznVjqIB3Y_2BT zXvw(JV@)I16hUWM)gg12GM@5^$bCi)-S_W`Qd@*A%I_Ckyz3SS`s;G7Esm7BPI+KK zg@vp9YUlN+=vB|f!JFKU9v&X5DLyF>XTM3d2^K-)oGhp!b^>?+8m>~WX8z2&kqP4Z zv6fS(PdB7TDxM|vXU~5JCEilx-M||4ilD#z>Ahe+aW)fl-M(bIB5=QEvtBd=oIHsw z)%Mm=d;v+VSU^=+gC=HCr2=hgmi$Y*D;<$+F6iG0Ghb91qxBR`2c5>sB|VqNEx3iJ zar%W?A}~|uB^3|pe*-=%k)wu~X89(EjhCN5SO=nU@d}#{Xf$CQ+zAC8U{<@!0cl6X zUye*iUk;IW-L*4=%2-bI)c`bQ<_&HD1o!;0@0<6}^dx4w^d*{CZnuR{95Xb`2!q&&#H7Y%XhYGR?aq}GdqWhDKzZa$r@0z+S^{^ zg2h8hbeo*912VME`^3F`m$7p6)N`$j-bYC)9%CK@jKTI#XgDKyQ@pKjujK~|e1<7< z0_P*W22AwtZd*6l(@RgaX_%x(R_e^fRjK!sONyq5B2*z2k&5z6>Q>9%-*dua&8%x4;lWu>GwntXT9&%nYvFYAX0N4`cEq(2p;;oxta z1|5WqaNG6BF&R{j?E~nM1t`cOc)NAjTLMGjwmS2{5zxO^69hcD+|JU+Zx67=mMC&NIP-VMQV`F16@ z=R+irgmJPg6Um#8T~^?wntuHWpahin9?msLjx!VbS20ifho-ss;W~fE@F!KKzOCAU0O|tb}Y3N?zzGtQkh7#Y9JE zxJ^x=9P_HK1g@rYG9J_Q*=cBwO-*l^VXP#yZuw6!4!V{3a>^hUTVO=DDQBLQDEcKg zI;bMyxIJutdmxm%aCc)9`GL?MEj4!EVCn@fb!T)YW*`RA2yB5XP!; zttGoT0MM8Ht=s*P)U8XEuO=^eh-2Vow06an>4VwSlnG+}O8CFJ^n)X-Z3G<%nD zTU#60-(MV?iDn=05!)6oEIh_atxRRbz#noo_!-}paFt<^2V+3q^{vnr2*i!gyxK@) z|5xI)Yblg?H}2t-VMz`sH;9Z`oI1|L+yen6;B3Ezk6s6rCj}kWy*;d$|O|RdUn(%ku zUbIuHaBA)wRD3>dU7@a=n2=y5Fhc*Ysj6aPX}SJN723~0@GyLY3l(%<6hJXRN;Ywf zkdmXelHkeJe2|J!bdfirbh|j(!AmJ>bfMXYIYh#57n+F(J^y%m36;UN9{zePx=%tf zI#b`rLnPU!Dn%Dd+uc4RH)J<6?;xvmfi<`1uOiLyEqm@>5AT!aRa^MU#<+}+l>q4H z4Ovt&2iwCh@8mb0)1s08Rx7!Xvvxzfa4TI=+XYJ$DpCPbj;Iydo@!j zS#hJRgE4S_Gjczk)MGl}ZBm>37U&0@Ov0?zJ?h7uP!M;Y{AE(hU%mW}ouvv_eg~9K za$m}RA8gO)=0KI-Mn>vRS*m&LU;QRZbf<#w%t4p-)?N2Kqs}mEct#a5X=#tKKF(Kp zWh(qQ-Xx!0&Qs{o9OBRsV}H!Q2PzTc#}`}Swx`8qEnkIwKIB-3d;$W^JrJaKuVQ+W z^04U3H^!^az_CtXzfmG(41gh@84S2YOb%uZ{xRzZ5vX#|aj4%7lv3pNT*_chxr2Ir z5$*QjyvSK7Yi>?oa--^>-n|6mpLllUgqyuY)mpz<`QBr*VWg-^-2-zt?e86(FE}S< z@-JpAXscQe$JNLsSL64F?qiC#B$7qXwN8f~!J8%;@x7Bx$Yc)ZzmYCtYk8Ins_NE# zm&;d`uDd#5r+cW-F0||Yu!c&g1J5$R6{}OxdQ2mMo1ZvE=-SVoi+a#M7_?|tOxtsI zp=3vHo~c)*OjSTFH%jt?xQyZ0AcyPr+ljvvjWi#8FNUYsc9k5p@q*Xrqf+ zdxb*<)!d;z_av%tCF~GOm{!!65`X}}xuD%rkp?8KK?n#zaJw^HC?~a+auBS~>VZVV z-jK`Pe~l^0Adc|8cdtx{{%qY_uvOwnd(3{v)3{qfDm-InnMLc#hEb@4c8LG`-EIJ#y7x-{$h(@^Xs0UbS=_5mzBTC)d%QVZc0Xpa9di_j=LF%zKf*=VWV1 z3cKIXqOE-!)gPFZ)%is^F>={R_Pg>93EYqY2Xx(M+TQ@6@G8DMKR=S?g%DkX_1v_* zeK-NKqoAmm;)4GA7!z+@R}vNFZ#&aW*T`&uY#;y}@wK^PVLxQ;0!>3oJ_bi^*1grBz%@Rn>Z^W^bfMxXKdOw7BSm_vAj2BJWe6t`fTZ zaR)ShU+vi6?eK|tGPG)5vt~XA;#XlIuBi7&*}M|b<$nqo&I}$$B-6{FT3^^3IMBmw z&l&jz>D~rfA+^{6wH&CSp1M+REaa zeo>Y>KD)^+>OBshsY1>&ccHOBv~gvGT;G7KGpw=(^C{j1o08}_cd9H&Ej2}hW`9H5 zcXw;ZdbpiMHMb92ZqZl73)?i|Db4P?P~u-}(`f}`1f zyc%zrU6YxS(cRjroxkl|I@UjbM>vl;_(3>uU|X7#4q7!GsY*3P<42S>KV10Z65Y_N z_KCeEuTVAaw`2Evboeu-!y0|LP{NGU$NqNqz=sQyu1(4ujuf1TpKRjvnKbdeeguxK zl0k()`R3iKk+&(q-6Rw>6p@L1#l68u2 zsSllmm;|o5ra1G7HT&G>OR{EKoW2Vx<#V;_YR9b7WmughXw=sbQjyFCdAn^~26NeZ z?XO>y_2;p$SGjO=3`UEx6a^ktuqgC7+dK&8;&)2dlm~6o=ST*Sy#TIXyawivyTbsm;pv~n{)Cf>PEpDhPopBg@ ze`SE+$d$Yv^=qLUW@BC}>ud_HBKvKwf$leF!b=?d&-IjCiM_Yi`9v|s`fex4O(6ZLm_BExL=uS_)>9^*Xa5|AF2Bs#f{5p+2)rDfgv%$tul}rXeS5_T0!WiaKf>~)SpPYzgokZ%g--?OvJa$N;=6&E? ze%n&8=5Q+gBp{w*Adw^}y}$iOuj+nYY5Ii$G3vLg;y;wQsv8UDxSz^6@S; zd+V!v%Rx0PKEmMkF0S|WJ5Nq?2psNN8-T&Mr&PPgXac`IjGgbT96u#B+Sz?2JC^|m zA~nQ)!c^1hsCx(FF$cV5Y3!XzlKq}kDVp}Jhc|K!OFn1VU+f^}Y^IPRZVhlVf6UB3 zsp2Yx+9Jrr=c#=&#;N@{?ZVmk07pD zGId=$r6lLMXp-N)x_VzWaCftO;mLmL{#GipGi|Utn{d>bE3&d?IqS*G{&o%wrNt$o ze-*G4i`=kxv`q1tezCd{w7=ot;)2@3y_va$vy-N|*>@3`J*@~Cw9n< z_yLfZ(F49Ftdt4T&}J$XDi{~wQHN}iHV*h51x3~;0;;p{A#$aUDC#Sprno&H_?nAn zj@i2clQ&tsCSDVA;g27)?QB#@V(fV636@EjCwA8N;kV+}uDr9*2$4$=JsacU zpeyD(BR4VRCdsyN!>xOuhPW168p=q@doVOP;dfJsbEz0eLQ;~G)#X#Puu_mzFc~{IP;_q@QBPVO!icIznxooO0sVJ+L!m8z{3#<**P>yAyERUS1lr z*mc}zCBlBGbudVJ2{m5b%I^}nP=w~0aAK5_L3+?aP-B-d%^-vA7^LJ+O6ux@iY*TT z`|mqsKXjQz6LD8jD`*8_CR^piPFoDJhfiPrMKhO(N@f0kO%0g-6C`8 zQdfU(@TCi8+>!r}y|0d{di(ZOL1XnrCX#yx?>-XQj)Sk8bwm+ zZdAHUI+Wg|w1B`{Ur-NtkN&)4yf?-j0xSdw{IfB9Wsc}-T z|D{;E!0zxmnkzSZA=+*@oXq|sLgxdK&6^yXX*T;DTSRG4cDi+@nn?yAflyUUWVrX+Lu7oF>WDR;=ZSZlQFdMfq}n_^sHz(g>sTqh|FSn0dI0 z!svwcI;us2n>eawA>IV>79))5rHOP^$euv8l_!PE2Oh81M|7?deA|z=NU(pn0{brv zq$q;R*T+lRN1i85f#O{?X=^dVG)#^YU@uj=pj;S*fEAc=e1{hM-e%7{pB&x2wD~FYtf;+_9w>5o(npw<$(r(%jOL znBlti8U^Cd)e0d?4m6*@y^=RFBFwNYp}aST7_+d;`NYt$L-YWYoFr&kFf#Wc9yp(>emIlyRlv}Rhbd7?IJ z_Z{=scXFdD7ca%?hM+vIVsm)Q_}}XwPA3=ZnHW#ESD(BxLl_@NTB4gP8t3~emzBw{z3rtL6kO|1 z*U?qT>CGIv+YY(r)&r&m_}fnf6W)7XAZ%@1*M0DSW(NqI^;TajIGc^v1trYfpnXxT zK1se=C(@DewISoxP>CILTBjeaHXe2Skn8rmMD-0?2O+us(|n&1tBn<38x6t3Gl|*U zvj4pWnpovTff#g9J8InK{* zTF7eJHC^1IuZC0$m8gig1NDk9uLDy-hCpyj~EaKZ< zq2t(iD}`K4QMIhfRVM0IkcQKIo{maWubt${eo?B~2YW%z1f9OZeb>d7UXrf6{x3u7;P9>aCPg?)3;FB%*RsG^T@$b z~ZAa8orXZjRI?&5fL=hI$sh_u;7MOUs`LSbRCZdB3S zn|~Ktu4Q=JVPY#$Y8kcQQ)qmj?A!6j7Ts1-p`(oBL&pWpXo-xdNan<8wW6Iolp(BV zx@9(R9ZyVuEKhvv3wLd2l~Rm&0(XhLxEQ+5nI?OaT1#+{z8H|Er1K!JWTDu&Q%FQk zjfdffJA&T8l({0VwmDZn?+5dJZs}KgS|>7sJwu`BSilH>kcd#DT^e(S25m|HelyM=AZ~p- z&I`68C9qg-#Xve#VXL;ew%BT>tmeR4iyTp5P9Kz)ifyu(==Cbh#*#|M06rTi2FL^_ zg;V*4q(@uvuZmMxmYCHMVu6FUV7Zgarr4%q%h|~0c%;jUXAU@JNhPI(M=|89ZKdd610GMVk`d&z-4_w)j(%*HrMKxEnKXyrhN48>@H7i=VKtRQc! zA$&D2>0!qN=q-3xqY4=?o}DxmTKmSYkaY$>KHnSGxG{A1;F)?#^s$eMlme5qgi6Zv0Kr#oNt1dvlFm+FNTzIV*n6 z6G-!$tjcjRCu@$+^GhJrO&p)A8puD)4G8=^T#kzUF_+F)TZao_E9$U*b$Ep-Z+2JIs&1V)hyKY6mGr*>I^p1j6Uc|RJiYm=V_Emzn^hfdr(Vxgr z1edKZrX~hd=(3X5%cnklb4!Ap@IuQ}P$QYMNvfzQ_cLY&cBX5&srZCs8eD9#e0v#T z%c2Hh8AVFi6M|IzG4)1U?F9iz8&fEGs@pC9xXjRzcY@HXkkyQSt2e9O+TJbZj1?g9 z$oT3<4lZAqCJ%2c-v%>bCt*Masp0}aH_t_4nZ*))Nj2r&4FAjfXoQf%Ai_zz*c^v@ zn28iD7-&HY`0^=()^QPhtD#G))#jSgv@I=3j?!etO;wku!ig!X2fk{($EkcakgdJaogjKE?*fmDK2eLV944d9ka;NqH>|( z7Z&a(w=kX9{i)@^Fb4Z*L*xp^19G5t6|S&aeWpZBO@*0UE z^pWP~g7wzI3UL*(_Nq^BcIcS+ccqEL2XdcwP5DS#Hz)|8WZ}CnwlS+ylw8{7ZC7iSt)t9|WnxR+ zsH7$z6y7@pF7;YCniCwN6j#`Otrl4uG1@?MWjmsf^>kh!Z7mwO< zp+*HQN+l(&JpQXEX~ytsMsIKWrK?QR3M;xB4{o@ulH#Gi67jwt zaHg3NK<%l`f#|h}Zw&y;P-j3~gOp*cL3*ZFA%ncS#np;vM;q)WQMX2z(&sYFefkd+ zX&E$Ag~_NU{}bVZ_ICC;oW|SFU%`jFgMV$86t(BFYIz-h%9`wh$pr3V6Hp=(>08?> zF|I2e6OLRi=`na8W!8hkCMmFB`2s_H zM;=~i8K958yiMatT4YAEQ&mFS`9444L| zGr*oXM1&5QCnsE(3Thj+sWrIVn!$KPKv|T~fFYPh1g*N28ne29?91!T537(^*Jo@y zKi$-6_`(~e+i-uhf&2Va;c~BFv8Gxd4eDECoyg(L6g95m_wQJ!KTPa!N^viVVT zYuW`5RkM?Wnr&^m7$09KVEf_RF7Wlom|GIe`MD!CN(yBq>x(qxRCFU+)t@23$wj2IWQ09**tydH zV1(FG_|ajW2xw{g!`xosD3-H?l|LJ6U<)Jg!D&z(h+*Y#RDQ9 zJgLSevP2M7o9LKW6t@sxQ0P3pd@*vQ!7huuRm?DnxhZM0D8zR1?>t`aR1{-@e*(E% zk6?^H8xs|KaMu%(wogY^RddWhgn^pseh(I5eUe8jw!Y}8j8}k<%*+Ff?_mLr|89WQq_oo~C;U2BZQOHdH z`0s}!`^y)Rp!I#e1GnnYC)~mJVs2G0Z^Wky7 zIb6WOxBK;{Kh4GkUF?Aq>vsgEe)DTMRm9>E$L`>)_3xXthOWAx<|95VPQMu&+`z^@ za&#mjCPc#{()9Q%aILzN^t;pNHaRMfNM{_|acw<)*Ziw24}i8ldGnU^$VHn^?!lRA zGV@EvuEHtM>31=0wN?FDE3~3C|W&rk_Pa&zFb`QoApCJBr!A5-zQnF!}y6dzD{> z1Kya>vZ)$_-`x%Zy!NM0Zmw$Le!B$&J^NHOo%F?Fgt));REI> zD3DhPcssw}SL}xxIl?k;QK`g)t~7WVEOEuOPNMs5MuiY~8wv}|nnv~lE~`>!va5*1 z_}mI^uK}^0l5SnWT%s~+VTnVmVg?2IBTWQ=ze(V#5B>qEG|Uh&FkQHcfclTApciqo zXugDJU-kyQoGX0ICM*)!C~ql@rXtyB2o8&taTrwwq<&Zl)SQ%zxaPV>qcsKwhYV{R zFKDjgq|tp99#;W`erL$lNJgs!#I;TBuzHV{=a>}1$6nq}$bSCxdUoKmcB8z6pf*ds zeQ)aoWm_GaF%5|Mr(di$TpIjw0?Xt)ZZR=AXd>he-@|0TBLXVl^P(@~UGo42k2Uu% zt{;*Ky{HVus;bYa7AbokBfZ=g`fT7hl%UY&{E1?BUHQkBxd^cpZOxL=(a^U<;&8-o z?yj%NG=B&=hM2zyWFdNM{b+^*FQJmdGDG4N6q42oM5l>4Cuhv{&49!`1-xa=_u;~! zXp;hT;JASl(kg^tMA3=+Xx^j}<;SZ2GuX^W1TBsc{76;p{*?KbHC* zUc{XSc<1PDvfr!wCs6;lpAtO)VB|4a?EljhOh<@#7}mC~p07x>L#^QJSJ4UH5QV^Wg>mG%x%D;KkapKBj-8 z#=p$MQU?fns>p`)Cm{WouO4j5kpke_={noP7rFdqFNoW~?BA?Qo9+F>1+_ti_q_0Y ztivYhm+S&Zp=?0V+h*#u|D@ah&e$G9kbx7`?D1^%-;8NTE1=H2s$Hn1>G^`s|Q$T2|Q==`Y*L>*?;*-PR~^avLr*IX3&?u0d-6fF_18N3UG~rB zfc0pbxzzsT*8fV95loP1p}nN~)fax!6F?x~ycMmHa{u(fua;H}=^mDs&-Vo$meYTs zY9$K%Q`5cK^;4T*Ef-)5xA^#=+B}3MhQf>koyfheKa~&k zt-F<7Q9HdxGe5Np`W1f=xZuzMyQ`ZFHLCg5mkxilbr%#Lsc2D&>SEwSZ~mV<$PEeMD#?u{p@|3Ngq!%G}FK({sB*C}j=xAmK?i$jMS zLBALDf4HD&5rhi=eU<1VP5>mUJ5-_3ts}nE z;or1J7BopDZg^Ec?tZy6Kp+5LF$uvx$oKmfEFlnlW%rS?|9Zb8FX(2z_X0RVmSS2UNB^Uk zAxqp4Z~I?(jUdA$x%DVc#3`Hj2-%4Q0WwkKr|Lo-pR~>ZWV+NOV5RU&;{HUUHmu|F zR_k~Ae!3&}2#F3;pQRGe`c$C>=|O1wCuRAUSsf_>eF+=Z|I|%84!LQSezSk-iGvMz z8ZWNxw)AoKpI$qrdYPLPcw9m^LD zxl3bQpqR3fH1@<1)Ip@-V=J1aWpqHW80Q51q`CjQdUr&S{`iFnxYczRMEL>trkSIBlyyWlpxQC!1}5B>U=( z2Ssvyx@!>}FcTS))E(U0qaSq?yrfguS$u!AZ$x z&RnU*%0sr4ND#rM-*R?ToAc_v3})(~(aOXOW@qCcxI9~TKX#?a?s_4#0b*?_!co+I@70Rm~#;j~?&k_?8eQ?4+v7ya8 zB7ez$u|7{p(ovsPw0qv)$389lprcylXAJ9Qb9d?dAyFm3?(3J-K+eB-`AFHeYmV8F ziitxS(hhRq-X8UMJSZQ1{j#LjspmY&7JKRG39F0yU>4_#24&WTiTlz$$X~wG*x}D} zATlmg*n}U*oj<=#==XtzUHkT~1X{b|P26^!GXqd@4~k6&1NiPKoSy%qd~0|bmfp3a zzN}=!?)&B9DLDJ~0(f(U+25TlS#c0Kp^Fqs5T}q$nMy!k zlZ86b{gIzHEe}~6!}lqNFVFnlZyTWAm<)_B$bR>wLvw=(ybWCXAEo}OtC0=R9VQ`> z{9oYrUmP(A%eJ7l)Uh|CjQ-SDVgUBCp#yj3PwgKcK5(>?&Lx3>?jMo#nlG?t*|~o^ z{?WGq$jh8Ga#1n=aHJs%;vTv`Ie+=w5G1ufObsF=V!II}2qxKoOPxmuA@FFW8RFYy zn8rRI2+&1vk=2)v!Bs)jBEc(}5+pHap|*aMduX?u4++I7nUA3MPL=B#Ej;ueRcUsa zs0G#j2^+izDuqsPGJ-kX4d~z7Z^x21RBfWe{(|fv>A@G+rd6q5+x4T{NBD#2lTDSr z?x98emY(2|5cg3)_znI4e|{wN8xBm10^JaCzIjBne)2bu(Ae9c{KRj_eB`6HAb47Q zn(5I4bOPQjkhG7Qd39(1yK$0eTMevvPl_R701txBlk=(77^UDHU>GPe&SsZ-L3IT$ zxobr(n=9q~@78u$a8`o=hHoclDg1n$^RI}Z2TsfPYwq8rJv_=$2|Y45m;jW+8Zq2E z=>2nK<3}j=Z9K3yaKC8Qy{Ai*f1Cb@Q@5{H zaB}F#7GEQG?+D>~FW6kDH2YS&;3{;d8p0 zur(-4R^qYXh8P=hv_w&yA9+Mk{r(OJmeK3>Z0P2BCbobkl7=$~?l!!PDCs2whK;g$ zDZeCFJdo%m&UjBaV>W~9f~qNDqNS39cCUZ`(s?hqmF6W+nsk!DC#3J*Pur@|;h17n z2%i5@6sV<(cq}24z;0(ahM%_YwWw0b4uvz@iU|(j4w&Iz_BL2cnJT##WeJfXP72Y` zi%nNV96fUxM1$%o{V7{x%HV;asYp) zq~r3b@7WFZeR$7$Ltl{-|4&ckpFlh3o6e>SUVjK?ZTEm;t4}B#&1U{J96yl(kGekR z6;c18A%iOdG`2y?YyU=U4u3?%2iEhxXVM?)x_$VAA0q$L3w6jRyeIwm4 zYDRMF{L$#>7}oay82>K=ey!I3%YdIG@+YbNUk&()AU`Yi|1AibAWROa@G<(Q72}a( zDuhGod7uBwmEphgKyHHAtSO$s&2PuAU(5fu-65d<$>}Qge+bma(ITM~A58YhUt{gj zIV&NgR%GP)!yJQS2q|!UA3%?gIy%hH)c?jHkV&{mJ<6DoFFRut+o)FMw4c0_Y@b(VwX!*Ewh(u=@m zi(kWD(p?SHPofu6+Z^mODrT2w9EQo;yykQzAx}$+FCpwu_ay8H;-&MOm;))q;VNS4 z`4T`fZ0iS@57~SdO6;B%>b|*m)>n9!o4{QcwssM9ed?a{#%Ec|K2@)uQ=D$P5{sTP z^`?kcGg3M!i7CP9LTjQx$qXG6%|TG6@;;`ZCMU0=m`s_9luT5VD6*Z@aHQO(nEX+YjsKge|1y5%)U#lpZYydO8+w;YfX73cDG z)5vk@T?;$c_3-lxUT{-C$!VLC_cr{c`DpXyRtXu-3v1yZ=}?n1(yReLqrLY_|Dfwg}q{{AAN!QnSK4zBg2j^Bjv=_+!ajKFJl2 z6*r<{h$lO%k_#;v85b`=iPWDAjYSUi>;>vKiY}_g@TD6o-Y_+rWBxgbST)^K$MPE) z9`c`6Z7bEZUaG|V@c09-vYcoYmJ3h23_%hlTv^v*U zn8}xAKgr8Co%w$D(aMJlYvM0-^Hd@-7Q|Z1ZRs=_o;OgxYM#QHQ_ZgZF#AazA`a1I z#5AtH<-7G~8QmQy)R3XGrOEZErx&=?xR|w(XVmDdtxXuDn8$Z{H;`lTVV&nnCVlS; zD)zloJs=A;Sbz?DZf6)hYDw{`yp4(C5I(4^A$KcCri6R;Lu1?Wn z7PgocwFsG#nupQoMg5QVK@LuM@+C(pyQ)-%28m1#(pj5#SZRhTEXLX*V2F<1h=8M8 zGd*C+mvjYH939p<9OZS@yo9@49I^-i?c1kHLz6!7aPx*w!Zth9Ebf-IkA)OusPX(| zRD7#|NO2)rNtAnKwfh^bZ4iTHRjVJEV~8c#kfgqp6CDn{%j>*;5ff|~sIKZw9=7TP zYYN(zHyk(9x#Lw;($48;=CXu_Nh50jjat3hcdG&?|`iK*m{>{XVdF5v#&A+Gh?4_ z<>q$O;I|qJ7Ti*7tv4vjh*E#vY_U|D@J|K| z8=W@%Pa{kV?{I%;b(NcM(?W3yndZmI4qEmzO+t$l%7kMq61EZ+NDX zxan-BLs{dwxGd)Lfqo#ZlLF~n^N?>*%k@QsXMStgg_l}{J&M#DWYFZGWOPVkHEeBA zvl|@e_6BAWN}Q6X{$3$kWB0$USoFa(2@1cSjjzTBO_W!WIEpI! z#;KA3Q*hKO%2T~=M;gX~ZWn)f$cK^GMQ=51w81R*DeD$U_*3C8h$%i4>25oLaK6hG z+`fz%IN@M0#3hxrMQI(sS}2$)Tfeq&`%dMI$Ty`SKoH?GGQDBeU@_pEF}!cO7vQHN-HymPaW`P4AhRL-8KC z2wHoInm3GaxvxpDzIAB2{eFg8fjHAv<7mwrs>j?LVGk3P)CWu6gRCS|B9*@Dhk`^P zY-pj)8+*(frBGaKJu6vz8R@ff1IyuB!E-%^benG&8j3mYdUdODb5PUP26cN6f5_?S zc>eQ?*e)&?Z2Rxg>>s?~{1}3gH^bZ*R+Tj`mFXm0UNc)o?!=|}LqgP+fu?BwXvR{l zdt&tdR*gfimuIwxI^EbB3zWN2bKkCM9}EtxY3>zlfwzRzFKs^cZxv7Rbnj%Q(G$WH zmkkWooZnLH>K{mlDjha&^QeJAQ4cC}pzqPjIYom-(bw&Ylj3$oz3cSarjXJQ*(0V6 zXDkp~4I%2$B%h02x{Jy4D|$e^iXve3yt(N?&Rtt-rOxU5RY=ZF&gEEc<(q@f@A3Nw zoIW`8u2;U{Z38%Ss@Q>)^F3#oncT*n;4S|7Z*<#k`8b&9{^&S257p>UR_>8^AX5muB#Y!Kl2 zAEY0{H%^YebX0q_u%tjUG|g-^MdYGK(@}lBDujNkaluU>1o+g5PXvV>GLwbbvzIDcv-zzcQ~DX?>oY|A6e z3uK=k|CW8e?}eJs$PP9W(BgE1l%J-gQT<8z;zyhbzHG=`D~5zr*evmlf>tPu0sKIV zm@?2a3RTlRP1IxWgy%H7aP{tpFWZ{6c53iL+j|{?XLnv!%rRwmKdjSJIpLF^_3RbLi1pLp26-5>t2Q z1rQ**DJND^JDUKiJ^wBg=UF~pWxijVx|&sq6r6t^%C@jOoUWJSqSs|-lpmvU>zU81 z`S(t8m($GL_PPdTZpN60rS0si_Y|5hwdcB_TKI*%uL``pk=qU4?IGy{K^>x?DSnQ% z!ku_Gld3RM{D=Etv}=z+nf(A~wH__hz|c?gR%C0%lClyOy%c=Wbn$NUK+#6c}F3OksE zw#)VO!E(NbDSV1Cx&4Y7ElZy-NZ;!;&Ja=KjJ5R_q3T~J6}bVfc><*qPYYQ$p5bWT zUq!+qdyIkluSQpnI`qOiRYPB`gH_xeVgpXbZ0r3_iHr}OZ`IeL3d>wv^s10~B#P4G zyOxRz1!FuWY3Lm4$XCkubg@qQG+t0uDt~!6j0OsBtE<-%uuknb+{$rC%_VI8uuM9~ z`GSdDf_XtrbCF$wG4}61?z=K}Bt9bL@vc)y?ir`MS-U28@<6lFg@@+HjL zP2H7}VA+!Nj+FfXXe(=km?kGQxAa*FOGuw7MwYO12glgw1@CSPTEJ)12huL|OsbHC z&)3B`I?DUQ%t48yH}|Oc3%!C>WR4uL(c(B6M~FfbxL5&`cCQj!uc`j)7G+0(#PwqF z!BYiSBucI#t+f+l%GWxaxnj-JD)#an`(u;rysO0mg--K>LE2={s?}$U+)SR5^=G)~ zJr5XvQL9s&wZJ^Tj&vB0Qu8LCEeu?PE_?G}X1V5~cSXb` zF9_TqBWBR&s_9_Jlt8PYEIP#mz7OhQMWqM*Q^f|!zV0QGfN~`dy39b2hR0D~21UhZ zd*T`7)h_9)YL;gPN>7wq(#Hn4thB?}9^S$n$k5E_=uxFoK}Cu}ef+;Um)O&Jm7^(( z!Zb09t1adXS6?yuf5{FB>YnvkGjN-a-7?S8X~uNkY+1wh$CGG{O?!1BX|gk&m~c^} zBEq{nGH5{MO&LUfc$}D)gJ#i}+fNHGEi&&EX*zb~esbp-$}VShzHFwrONU#*BpL8( zaf*9pV^K-ebJ>7dabxJvNN3NQXe-+<~})(ZMves@m|l8(_O^Xm2+ zMK|89lL(eOTF~%XSyIl2zeyT$*vfkCTfTg7D83d3e4eZ?wLCF|Vgrh&PPP9?L+D}TM^UhdmMxmvZU!Jh$CF;27 z`s)+*DUK23DM?;zzJ}?(<*XMXZ6_WkH_vz7-*;%hxe2CkMc=jRY_XnE7g-##-Pq29 zzWU`sCwuo+V{>~(yJpLT(sF_?mf=)-A*KBi&zylCpP!<>)ySocp58}Rd6^#qA?K&# ziCym@=SL0p$ij@L%-%thi=X4}(=L3A4QC$PL~Hf(ct@-jCpA@XN*CIPXLsHvgcyaf zRd$^8=(FDW^4R*(+?qlot>Av8bLI|O&YB9Bg@ZZQjU2XSrg3@SqS&V>YW^qs2L-qQvkx5d+@|M%blU=&a7g!5u!i zjL`;4kRP%qxSZBim{t>UnFwW9pfRclDS7{_U9pDDQj=39y7Br%wYB}I+U3{de?y50 z=ols?m1^b3zjQUFug^s33B`q|1#j4DwB{z7Y(=%``kr#|^}Y9f3d zyj(X~JXQKn6kVhZAGOdcV&Mo)oD>Gvn~DnWm!MUyV^ux=Yl>-JCd>-AzX5$d$Qe;~ zy{>V+$C||$JLO@HR8gM=;-m8d^VkTZ-GZ|o%Z2{CARrdkiXH=QZ(^35Z>D~7KTK1g z)h*Y!C?nS-tDG`7Y9RJ;aPWcS`lt z$7DtLL)6<*c9R^@jo4aLk1x2G04rzAAv93Mv2lU0S-Rb2`33AQRe#%!q7#RnuxQ;t z6s|0culloxB@yO?2x#%qp4Ld7dXVd^|7yRMyn*tO>qDMB^%lHq%#FQFB12EI2-?rC zZ5naeIIz;bJQwe-3z?uz^ilI=wcUX?uNg!Jsw$Kdm+QEf%%AI=U$Rt?fGjUd7h2e| zUy{WMqd_8*K3BvS4vF0A6lXJMZM!Q0^fQ{*2S(_3)K7qxyHG*hKS%%%L#HZ@i4J&1 zjyBH}WSj6@$4H?bp`N9H0feBKk|lQUD)Ys7#Tn+3Pknx@FR##a*a^s80u-9|hkupQ z2+SHHZDK;=HskC)$$~H!&WTQy7)7WU8h<^)D9hfg6m?Dp(*!aU zQ}ck5GT@&9zKHf~%)2a_Cc$YsRrm3o2IQUwv;S>`-Fq4Mz-3Xmjxvm;xwmC^Pt zy#}fK&s0HU%yK!mKKbQP+x*OrqmB}?_dy0Ks16%7&o0kC-8R2)N3k#c`5IUl$QcYG zd6O~7s&Nvp*A@&sFpN(B-8Ml4Q$|;-Vw8|z$neCIZ{#h7GM;VERADf0HO`wLZmg@T zkGbskT7xg1wA4?@nB(0RcDkr&Ml%jI!B~|Al|}+4B~)!rVCXWA0+B;&7N`GMWi3W5 z4E0Hc#|feV&+e`+!@cSX?Sp)!%6u%rj{J(of#I_~3YG2#J%RSneOd!ovE81nAqQ&A zYti%70OvySpu)!=wAi8gh%U;hO3yS74w_%~`bsW*DfHiN+iQa!u}_v0wWxPm)8H)`3^ z5`1v%__sgTXpVEizcMh+M=Z1Hj-LTZSzJf>D#* zU9QM$ZYh3gzUbrj_>TYnump=$J=vbsG9?}F?@&r$P+;FXKGTpK%CkX=e;lp-rCi4# zE!zI>4*povRZ5mJc`sWY8$0QsJ@pd23s*T#%o0~FDekD<1(Dgt}=-qqn#lZn+Xw?j0!Pmo>Hl7?iH24s%R%V>4TA5v@5O3fp?!I`D%} zup`{hjj{$3U)DFErEiZrjSGDnh|8PhQ4!8StbL}tolJ)9FcMonffJ#jzkK|nj`NT_ zz~io=6De)v5M5H)$Wg1qN5c@5cRzlDzcIQ(`zr&X<>O;$?H4Hw23jqX(pSHI;hsCD z^j$`W1@6mWJfZlgB8hLpwJ&CMaCd*y*}!EBukVEYiru#OU`B%9hw27lh*;{W030AN zU(XmwiK&N=P>DL(%rxXO@OVSi76L;%Xt#cTx%&PgMzuCbEb{8cJkLydm<+k-qb9}^ zdWg=QPkLE_bO*9Vdmw8Hp}Y8JI041$&M4&s7k5;QCZ(=sNY>$ieW|(^v-VH44iOd{ zLl{)6mk*NuAT5~s!L^U4x|JPZ0%^m%Ps3-+W}80%G9g;f=eRkgUHUHF)zFAfZ^Ggx zg!tcaMu`_JKJ;paj2yU}DAt!6EYK9)KyhCWeWZa*yoAY_VSLv*h%k=ym4%Kn6ZYiL ztZ)VNH|ej+S2J==iEn*p+RZ{~b(DzmtI5Au#Xfwz4!#$tdEB_{b6L~~>&F?4aXB!o z+Ki>yVQOEycKo5}wz8VFN~5lgCeCIUX+(3Dc^I6EP{p1iPt29P^mv zgrEBq^7R}Z8dVasu{*e#ix0}f1m2xpVkeN4xdYz9{wx3(=^ApP2u=Zds=S;K4_!GT z%5O0f^?_){u|KBIS zX!t++WHl?q3>3PZ@H&UrKgw|oE&L3NF|RzGzW_A9SL(6XrC*enzn&Y@FA9W#Hxj{) zlP09AJd#cdATM+odDc+$qscmF`-XyMR)S;9I8hsZa;a3LxQPLmkrzMnY5Xx;w3a*fC ze<|NRNOx$J7}k{VT5CeMgJZa5JswZkzI0;HoI2Z4S}E^<-}VK5AYHVO3=r2mdnej_ z3aR%^2TQSJ6(`Fu>W-mt;!$8T>gPrgL2H5t2>m%DMM2iH!zo5~hDGP&H^E7EH#HV= zL1}&}s7itD!0;{qK8AJ|9|*vV_?~#|2Z0?J9`siG{#y;xq5O6lUm@XgyJY404{d>D zzc?mPcbfhxp$avF9&>M-Tp)`Fj)k+*u|y6YA`5dU!ir= zNx7i3Ke4KO-%6^x)T0T+ zl6UU6$=Zr&z_@LQed9^`aeT?JfhORK+GX$q16sIx-}$OAO=Y8juwV*uupDh9KM<8tjov3O)Mlpl5nVz{!xcM=JJn8646A7>vUrVLiY+VP7=)r7( z(v{`U3{?}K)h}9Ay_o2UeGTExJizw>-_P7=)zm;QmO`%BL5Ayx94nt`yB>W-%YRs! zM8#i=lM!0A!10+zvUd}oO&OO+Vxr`sQ9z3*Fn9{AM5r5fx-)$a{~;4J%mW&&H5wZ| zVFi-E;kVC#wFg!UCj$KbK^F=_w5F=Y$3_@auk`8B1*H+-M}Jdm*<4xEyA`S(^BBMV zUDvo8SPtA4?TuGxv}Vh&R0-=hQJXG5Hs7!ixpW@=j0n)dcXTjIpTleCB+%00t*zwc z;brS_!l)g9=t2T)Lg9#LV)j_jb2?`)OoPDx;vFR9-A61 zXn_S(z#JnCVhFMO_Mze1+7ZJznOaG=aAe9nhK63tG7aI6*+WC=qjiOG#y8JYF)Rn; zlbZ{Jo!)_jxj*zp@DoecnFd)RPE0^UY#*@n{nx{QER&|~f!7bI4)(O^@rR>p`SuTc zbW5_nDf{%fVKfLc?`b46J4Khh#$xsA zL}F(^d@VcBpR%cF6w*NPA$_$4#EXTKgFXhiFslpA_}F#N0C^n#n#w*r!^hZT9)-I| zOkhg^H9t#Ko;sV%yhD%Ic+O+?;BCb88Q_5eBQqhRj{4@qWI|@;vALfsE}Al+O6a%! z?4t>^L>(M0F?&sk;M*jc5>K1~re8cc#~r??wMEZZC_>#z_s}1D8^wXdU+jFwfUml7UCN_ zaVH%J$4fM`N{W{o3rnW*ijHcx9-KRlP6g4|nR#1rF2wtXy(O5b+OV{4jOAu_1;hGO zJS`G9JZ_y}iB)ZmE1wY`;ICs z53s;Qba8hLaof}7X{IGi?vLO zFE&APIi)oZ+Is%JEl5oBzEF|nmvy2FkrI#gj}v=;3`TA;XZ z=@Caj`s8!(F!^D!*eFZXby9QaOmT-$%ebXYOn{TpM(^6wD0Coy7(xJ>Fdff4X6%U{sk#9R3IO@!0kKRnCTeyO@U=&%G~^;f+n(jzN=f@ZNUQ!krpcR?3B z$*!#EG6bmM?&k=q#-d5z8gF99r8jwU^*}r6z=FS-8{@ftFN`-D6PZ^8T&SQfY&h%6LT&X#aawFA+S`Mnb1+rHTV zEABG6ERY_ib3X=4=J1pE@tQ>H8_6tnXMnS02kglP?EB&EMTM2_5 za=7i0Q~5a8{#{RG-NWtoorJSQtE{)eApGXSdvxMdG->5ega*p&80O4RtnVsc?(04a z$*34mAPKq8#}F?s0y*l3&t!i^ycg=17bPUB>=n3>nb zMHqC2mCb`VIUtfXnu>h3qXE}*9)1z^@F}#9%YdNpRCiUwNQ_SkxNc;f>8nW$XE*cHY*+1N)+_bjU2QV0Aj zZUC(dIdpaG+&;%}@G|2}`&n>c)@B@XkT029SfqS-@M%eR{C2L+`YB`d7ZdWa#?Zb^ z(VrIqZiHznO;mq(Kc(2qHf;JXSK5>qXpco$}4O+W%1CGOOu$`AM0J5=qnxnhU z+K+}>JN7|L==hd4FQZMwb_{`}ww^CA@$}4=zTf2$B01+pEo=_YLgnawM>r8nolhVK zvX+_pGJ*DTLqCHtLTZ7pkDC&a@&PZb){gXY{nol~};@^7#e0a|>@f6~S%t?&? zg%pxSfjAa`kg%tgRAC2_f1m?xSZGW3rG)A4t`x)CI^?>C3Lf!5yM_Csn^d}Qv5FKb zM}>Y6$f+DlgWjz}^DYj~ObS%a;Zv88FzlZCh`vACC(O3=v#qRyZ0#3_)AiX~g(+0n z8>34gR{;pi{vQN88UtqRscMfTlYh>4Cg7F`&fM0MH}srvTp@K`rVUh@cx*oT6$#&h zbq>oczRQ8;4G4vGJV^e4 zLV(K93UDwClB|^_;;RyZ3uyypjnYav~SDqIt2NB*Z|XqDhMDwJ2Zh35Yz1ub3hE25zytIp+Go z9Mi!-S`UAwln+S)2P+ zJFm(i9%SU}bW%BwE%v&Pd^d)iISy>oxVV*4ZEe@JYx>#{1x-}+@Q+Q*07Gf+>_>;`Vp+2MMWx_XLRmO@x}NC5t5 z!2!hF81^_U@Ith*8Y)x;jF+zM40ACtoAXg78I9omv@yxZrJE2d+?oQ+>EIUUQy>I@H8vlHW2zHTl`bFR=3 z5V=wWVLWvqu%QEQd63(4yzY|v{9{|aVV@K=+9)X!4JM8z;%n<1XQ2Zq~~!X0u7+mo3( z7ApUbVZ4|RFEx8D`X)Wo{I5ZXc_$fU`^}#$+~`SiUyS>bGu&Z)N2O(vahG`FJmk5_ zoD`?`6c<~&C1WL$FBzBM8k@^bcKQUgA_^I_D=sw>VkeJpJB+r@WiPGZ@)umm(d9fi zFgqibFVm%klg)rTB#-TH;Xvi~_3rn75HY}_cUy4}A35mn>-2B_!~x)4x_;P3|9%$m zX`MJY&Aj!E-Fu*)7DYyRUYpe+N|T>5XaCXVN|-O58S z<3G!z5I~sk^7B8dsYyH=Q2t11B*JKO(QRi5OXhQ(F~U!Ez2Sn}lBB1@oAlI>w;tQ| zrOJ4EeR^zq@V$uh0c)F&IfnZvE50;iG5N-B=cqoIdDYIRA65#^HwY|QPXhbn51;R} z%(N{pvtDA)%s5NdDKxbeEsJp5-2i#vHX~gwpl}FLW0nF5&H&E%MWEpC^%sOz7y4>q#VGbM#_RWtV^0N))?Zc*lNuabeGogx9q{ zk1@?p3_{{}xN+ytNo3DEoGCufNEhVVKef>R++(Z0$xv7C(-rxI?7u$0$WLf8=f(f{ ztV^DA7>dCB5TYU6TZ7^5s zG+RnH7RNuxAmz3Y&Nt(yXf6GcIJgl$@ZjC^Po$&^^D9}CSQ~wVBpWwlA?Msjz@3Yo zV?xQmW;&%aFI&R@*3+a|Rc5@}fWQGWC90K&GEL{O^`R&h4{jXO*#ed~>g$zm<=2P{ z=ANxg5%)(qcl?dB>>Z#sD^+O~J(-bnb22=~hDp}6xtK9q2Su{^<#a|cE~yrC?!`fU zb(ewVYq{GL6&MuYJZoc6s3*r@GhJjgHPIII?EDn6hohc(Z{>=zW8c}L*-1S)b@fY3 zm-T(4fR7slvot#t3`2ayP|I_UsnQ`lJy*J0L%{j^+{o_ER$cI8Hl_x(d-%K=Z>~=Auc+168zLpVCN4Pt8 zKb+4-GIH$8Nk|1g{&mb|`lN3r8NLc>e{UC!XFyD_fTv7xMP!rq#xV7AG&wJ2k7gcx zp;#}xHn2JF7~uB%?xb-nI%Lg(ALE5z;X2VOs?n3hMgs+Z>! zDvEgdnS=FvlLU-u9(WY9P44}3Yyf29eI0{y^(D$vBAmzda4rVdAG~OgvVJzD%D}v1 zTLcue$AXF)|DnJ4%yw(a-FiRiF^A1Jy$T;2%7d+~0yEdG+5?90mV=*bKGubzbicvm za)SWSs$~=;MPL%tzd*DdKUlATVL3^})8S>!H^r(1nlzGPx_p0RK@6UW0f%_J6p*A_ zU;DbduN*9m1cSMT*_n({La&DeEraM+-Sgg5m)V5pnK_b4Q^sRH_x6`pJX^|dG<>Oe z7Hc<`-&My~w%5jz>Z(K_kBy4%?QFacA1xJc6IzB-*ZUTAl?0kBk#|Kt`-Co-{LBkB za}&xKlIrpIKe)TBZ?+qtdS3d{dD+g>w`sBt-$ON5Sp2DLu36J0Ca-D8Z?!*t7cWt&88y}Wq4)7cgaz@S=LoCtUXy$i9Z+_ z;Un)G*7gaPsIlhOo&2&AJqrry$_%S65nAF(6BBFn3kZyOH#1MS{8S>9# z5F1gl3Cm?0H=fEAC~Krzv2Hg$pZG)lhGV1o7Fo>y(W^7F%9iYJ*&%VoR{EAcJtzod zD$W)xDj#KyhxKOxxh4aCnb~6H5z@cylw9WFC z#;BmKF53d;V4^5)YBI+?`P3D&Hq34m{|_2q6*cQ;p~>A zRrF_yYwg6VZS`krWIy&=-B=#G$*|RlYN-mZi>|i5aCxas}d&@_hhX|1{L04uZlx^XVt@) zxo-=&RF#0?^YX$no)_H0CTk>dsLOu!SEHs9*y8wt(zP}Ey9ckZ$QQTDm3s7?@T>;= zgf}OwB~Fc~`Ajw!^=an_MRg?0X*t(E4SCCR`^pyFXD^;(EP}!bvElAS-kgaC$t=Cf<7i%OvAV|ryG#vx==dWFKptIz>B~Rb zLU7oHDzBUqvu2ku&j*Yb7os;X^D*w&Z%Z=}g@088*QO8wt%wwan5V7w=NLkGkO_H;! z8dP9h!mw$Aly&&SBKH`=E2UzXgc)G&51V$DcPD>djxxLro4&)f^3%@Lvz`$3Ej(gF zBvELGg*%Qv)lg^=I(iwukB0v+^V>fxUOsZAAlZkdjZv)ja$?y+9AG*|4pkbyy&z+v z=eV=nTM>mTBICd7jV#Y^U^z!H$qXhJULa4)do&+1gb%4!_)R^x*7Oozk)=90vS-qm zMHWC3o6Uxadq^gk$rtKw#p~lEW#GB(s-S_ORQ9B5QmLm+>3S*;&hIIC1$9j<>U#ry zE1QaK2pwi)F3O(BuvpZhGgEqiCme7?&vBi1QZVy(&w~)KU2?Dik+$KpiX^h^x_+AL zL*ZS?t9E~$?ug5QW7SKqc4t@%Ogz?0KmpYYNa(`Y1;jYQZ7n7w-k8FI4k+~B+3K@A zoSBn@L75)zW3KSeGeu~~!@9!6Eryr+@1k#T$Kjl$urqv)GcYSF)tZgIG3*1lYZ{$= z^~b|xq9;#uZBI;VKC4eD{r$b&fv&RnQ?^VCQ1A9KpxmxvFDnyd?rZ4!M%16Vuak7J zZYB@cV(^M{SzQYFfP&c4Sj7r|xc-QOl!g-dHROnJl@FJjPA@W!mLLX{w)Qnr@%4e& zae<9KJ&lrczO_F4OkN8tE-k@}-l_Thfx+)0nIwES9%_I+c!Y?a5@#1i)GDV~FI4Dl zW5|!@W_@L;K-%V|h%V6T=FgHA(CVu^_HxPxqJKeZ9x`smBLH4Nk3>w}^enIcnd-3T zpj4kThh`S6-E&4lMlojcJ0Xc!wr;R0%sw|{Z^SCl5AY1M3p(6ZN<(2XG5weCd28=u zj5_n2q$|x|f|%}NoM@^wcoe(CpV&XSu`0iPY# zuv4;!zr_i}LM%1ZQk6H#PwrdM*ea36Hh`1b;R(M&OO}{ze@^gUS{W-f1n+HzltBH; z>Q~K3j+08&>`8roM&>C@Z-@$vRW%zL&hQykIlgBQik3`Q?!YKxikkxT>j%IqX`ZO{ z8Bv4m^k%zUK8p-HSqr4ZTmYTPp`2kr?jC1`ex=Ar&XOOp$ddK0mK_^Wf%O_V7Vi(# z9<1MWrgb5$s2Tb5-14~Nw=(GHwN)Cn9A~x}zzAY|SF|lTYgTK*?;Xw^V-P6$Ol~b( zP1VPsiAI>$dH90`bo-r7_(V}E5GVYVAOFYgCmhR1xF^c)T3~ocus7TmQW}o%`{A0J8A}zcFk2GlofA*j= zJSCc~ZMwMt-B+P%mq2W_V-$_QGzGc|$wN)s(zxK$Fz;sCBnmFSVpjFcL3fp=W+tzv z(LU?79T=K-SD_p}cs}paqj;E69Nn)GgtH~*T2CippC>RK-6MymsyYuj5S!amZm4Ie zo)uvTPja=fERhmfqR{M}FgV#jkv8QqxwO@%XS;*<wwb1jURXN5bp^% zZ5Su^k8YG5tU4vXUbiUPExIxM+~?{;;WSAl`?+J*f! zi;XgkosAW$+cq8q#2nL^nxA3zJ2FWbfq122DQ`#K1T}oAp9}H+mq_TAoD2R4M>LeR z8ky=>wqS3O*Sz1xav`o@)W@zkd=H`D9>bx}ucuNLfMyvhx>VkNUjd0K<&{ubm)Euc z5@C7;zaLQjE=Ie|)DKLe^&$(+=@(MYzUXof~(Shg1l)i#pJ1AJ(&32mHpN#8j zJw!z5gfm6L(um5}n6QyUh|~&X$J@LianrJy5BwUkJ6PF|ffiD)cV0O6J>A$TI668y zJwwm*C>Q^3qPAu+NZhe;e9{Rsxv6o%x05MpQN~r(Z>#&l@S7{Ccs655+dEzn!|Py3 zUepDJC7-g^=9}1FqTGcl(6ig86-4Uh`9X)wTfRwxjJ|e0@19@tk@qMy$*HO5eT`rj zHq<19ua#Kb><~e$9u}GsWYRv-dnf(}#;c^OyX=7Slw)(fzr<-|J)y*-`{E;Z-*v0K z;Uh}+?6aFUoN5I^L<62nrlEwcnhH4P~{;2oeu|NMcWA-#eJa$!7P-xuFU5ezRJ z`DA0fzu1@NEp#)_&a98p4BaX8m>d7j+M={Or847z9Wlmktegk7K0+lBQp+gfVu9gD z6fpYl41>z9W{+&d#B~*480F8)-ly1UgqAjl816vF0}?`)xgm=-Fx=xp2amPN2FQiD1FI9{{D_< zave0#79&|o5wwIkM?BwOX1pB;e-tb#Cu2}3VtK0`0({$*u0-@&s9{Qf8 z>Ofgw0H@!VX|AiCsOh)$<=hYos=lGodwEidM0#o;wZ6m&4#Z>Koh7Inz`EN?gk#SLq4t?qx*SGZ($u*^U_@{x|gu&{5Z!0ZQS!@5cL z&+}EI`!oCs-iSnA^!OTp6r7gzP;deIN2$MmZR6q4JfM0Qk8kP)<7e^TQU?|%l)*2rNDk#*kv1*4wkA-vmPPlzOd|&dEy91KELP-=J!HBN zDLdJpLztX^wE3^UJthvZ|8(NH$dgEZ4X;AwGH{V~&?)vOkEl3~@9F0C%I<}$?dus` zujRUX)OhCtV7ue(rx;U%hJuwJ7$CE}RNywLNlwuh$e?$g##!*xK^?tK^m$ zoeKtLi^N4f(~?83j1Mle#VQhcQoppyR8TRXzm0X^(WCRd=kzdl;xl7_Ctot&B3~PV z8<*I<^DKE+He39iQWjDCz{!qDpZqN4HZ&ZbsZa zFJ4~zWDW*tyRO1s9xXal3W1l75PK#J87vPsv~Cf_Z!(zm6&3Y4oOSIpv@O!vNnGX~)$^OY%lmfm3-6dkDL(?q z*wLzYy9lQafkkTgZYDTG{lNu!K5|(?Z#`qO?9&Eq;?&8c!cfyPHU;R;LT4m2)oU?z zG-;Stc%-O|tUMr9mm)eOXj|_{ywJ_oi=>Qc3F=&SlG*P#*esH1q{Rfqm_zF128-ME zh|Xr7St)&b&Xp6fnad3ad#a>9S!$|mSyYib!RyqfDR74PIo_dL8e)wS;}(7EYF^8lQuR!L+!Ag|+hk7krD@@D93$hlh&xzUk11?V~ zsy?IeI~Unf0QByqzU}0n=-s1ZkdgO;ryHEN+lN+`+e2MEybsjWIu>Azmt=hBqgP#| zeTL}s{SSJLix29aavZ`Bo{72$N`oFA#XFevW%pLsek^)S>Xgm`BcK5Om^@t%$_tlD zf+yp-nJ~m-^k`=`ZT*4nxh=P~Pf(R&Qt1X(b}vj{Y*vfc2+L}m76(IjB}(=nO>Jqc zlQVOo9(vw-RERy5=un>F_|K4UJbsa#P)2Hu7_+@gr4R5DkTg>%dGBXb9E6~UvA_tm!)ktrI=38PeXm8*MsI<=J3f%h^k1X=hnBEfNj`4d6Zmvy*$yC5Venc z$>0$><#I4yG#Q(ey4hUHff!?;=iWn0t-2HN3xbkTopZp99?q18m|J8^1jDJni1d{CS!DrR9@mWnk4 z<*XoynHgxp%Qu|a((7R2^UA(o1kX;9g$Rzw*pC|nb#|A8sB4=Qf08vG* zfavVOpXsb-Q&Ps`J9%}hL5OSk#A5%-{J=Q_Oz8k|aX}e@ZD>k=aAILe(&uA;EKAQB zVT%R7$1ADh<@koa^Y4k-wyIo0Hd7%(&}48nld?u^L7sq~m;-1c2A0 z8}4c(ff9X1qB;>Wx7Ny|kc$O4#X3HwM`#J%!|mda*?H~qKE0=^wp6;R{JX)PCc#-K zb#cqhI(3I2HkF{%WS*oT4tH=cT)GWXnczn)0m9mAxpJtMoPGn6~E_JabUWU_=e?T;=FxS&8f% zCHAEWalPIr%q%*(J38qYyBcP-5SHtYi_ML?|FHfPc3W9rY!M>4-oK%qC{CdJG^C8g zU{x-Rypn}+l?bp_I$IGY%j*yCw6sK1y0i=Gt^T$M;5-gxZo*nJTJA?VK3K`j;p7#) zY*>=oEXJDB70lzGNl654QC8!zQUxdj&;Z>h!#bjnlXMGmKuo>Hxqet_+^8_jZuvgX znYVxt)9F01KJ$kSus<$A$|v!HK`zD7vj^g8K}PyViY8K@*@kuYdpA|%K+g(r(DVFn zx%m!n>L+ea7$p;q3z0Xw#l0zieNQOlqsK@5C)_ZahbDUBHJH_(TULGu8bjJi2!S3%}h zc&aA_9U=1+7)UkYiWWHbsR`m|mrmk6Ey^h^?XKSGqG-0!nzfOK9&0}v4;4WldNt@W zinp0prf<(mfiF}$*;Lv!?BJev&T|fBbQ0p=qvio}ee0Rk*=EYd{i7~{_DaTmd=^8C z@fm)5xySCc9eHB~VAlfxb}f@FngK$u$W(0N(hv>>N21`+sXksqb5DKN(jY6!OkE(f z49kP08Iv-6M(hl7{}PwTv(hjzOoe@YtWbdG^N+V@cr-)w z06}C@@kXED0MaayC!xr;ug%|sCdp>|nu*MiM2akcCQCPOCpPJ~0ZKjo{yQ%xmt!Y* zd@Y97DU~d5km`N9go>HMBR3f9-_`_TV{+-3jIQ4$u?r?AJ2j;=>ZK%eH&Fdc#TvTL zcUQo))$WKyCBO0RW1dAeoV@Huz65LS*K@aj0O`%UTub~}YoxEpNDEL+jLedBiEdM^ z_;K{O+%B~^%lG8biONC)VpNlQ(o-e-Y?x*2RmMA&KS?j;&lry`n?oDZo7CG-)enbb z`{VPcxzQJ)nWZ!Y4El`kLPFw1vEA6IIY(UyuHiCW!e8NNy=VMXi2M-ds!luXGc&Nb{kMXEO_TIiMp6zIR)LC?KowUziYJ ziAg@kY*K4WI3Wj#O{Q@EVqH^|f%r|Y-s`OjK=8rmD3oGYY)F16!P@~%FXk&!Tl=Dt z^(ZUv4gJg{wPmN9i`K%IgWbxf7n6pL=~hnx&Aj=JGLE}1f3$-a@{(Nz!#$L;7lL~* z!pgmy{g{F5xhOYp$}Rch&W^;Idl^E10NMlu9M=8J6kaabFsgaD$GcYfR(^=F@vCXD z{MJsXGi~I^PaDRp$1t*E2I|1W{?ma+6#cue5`&;bwCm`ZqGTaqq#q)Er$WzDsAwGG z@w2)4i%PjqVjU$hWvJG_}B`ANuul2?^r0p)#$i%HzT< z6bqS7hIxC$u3rEx*fx8@PZa!$z>aDLZ)+Z}Xlv+U&7Rs(*hBW?CCfvH{raZFeVgkh z=^CybOt!2BBA?p|r5bu0G8`WrLGxF<{Lo>`|2u+v@e^zSp|r4a`mVE#Aq*hABfZI- zFW`NWp}xX3rd@r-ZMIhnDgeJDGF--ciGb~B3EZ>^6+05tm3s5BpwZ3^e7?qsTu%>X zpZzI3v>7DxY5*`mfurnc%+&@}AIO@C_NzaHxF$WjYQH=CjtA7ZZ6Nj0N(NQt*qv&n z9Sv#t4U1vgRvv_Rm_el^xEPa(^tPP?@WE!l4LjXmjk&aO6buV{QVsxZ|9=JCrHh(i z-HS@WsE*V7-^Eg3#?~2?TWZD|Ei8WP=-ti0v9{XXrI5*sH?vFzi=R&5tEoXjzk}6A z>H(#QxhvUQIDGF^x!)9R+&S{_X49g=rHA3!i^GM?kuC>26K=DQE*VgAr{btE}Z7d2!E zWYPV#PAZ+9N=>+%h^(>Sf3gwHR8_l5s$BRvt993!kkOM9yiD~dmmW~PXiG;8z|SA+ z<&|(6Fu<#_; zhhBJvZ_kMhLe1f($uV^57tEY$9FQf__1X*T8{P2VM$4|v8*MEftZ;DiY}5!% zV{u)Omp#ELzW4Rdsm(trUOvtTD&1RU8+8e>p*t=%_NS8!R2W}b3fwRgScDDdo~%JpFsJi22(Dxy{gNfo0v>sTbbGd!qvH(uw|q_ z;vTBjJFq=PeiWMLwylbU^rsao%)YHju4FaNQthvQXU_`W!Vt zj%#CC8SyKG+7ynUcv{>j<*&^aaxkhS^e$P1=t_u`SCn|n!6SBAJtMpe79t``u6#@$D#VIK(PZ0Dy8aRC%lE5 zvtRxp|AR2j8~60ds5lYgdL3fjG%!+ z$U$XDw$2n*El(>q1A{#QYcy7#`^e zPlJTj9r;@eEY8pd;&Jvm%){O7oUAGKI*6u`o~jU|$8+x5UIjq%WG8qh$9{c2kDJw{c;!ju;GQVza81LF4kY*TWv z!7E21NIB46s(C+CV{95zi)m4J!b6`PD66P|c@vP|xHH_6C)Xc>j){&0RFpofU79It z_4M1!Ahvp^tD{Q%;XFR6bn(W;$QJp%Ch6ADJn8a*Aomqks=iSIY9Hp@`v< z6CqV7u7j2F9%JuvW=Y>Vk-lK1T^$A@Fa;2km{yy!{LH{^(8ODF4*kA~eu z1ffQiqZ$AGu@?>1^|zrRb(_*JX11*%bKrZQS@a^3df;&-nDI%Ok? ztyDF)KN=eq7mqiRF>e}4-W#7xM675S<%u=qi1P!uJWK3pJdiN4ng8(7vdzmb(vi$* zJ&@Aiw%X_rFKdyZdR9i30@sUA3bop09rPLh;A{rhwo4vRuC2j|LzWH|#dJS^ccAJ9 z5t-tyWNAGGzB+CKtu~f(`4)WxOK}=g9qqhv@o4mV;zex&|BIVX-baU^&@M!eFBZrPv&6_H`MxT?C{%HVc&p!DlV)`EjX#e4u{sBwQlmZjK z53U-frZE03vimQ7jqm}if}2Y_+y7>CpQtPPIW#Wu&z8LZZdz{CzTbtg3flk0=rsGO z9x3dnoBMw;h5vu5Kzkfe@mhUD|DVj-fB)`{5wNwNL`|Kk?T7#U$A5pMxf)pU3hC+N z|C`ZiQTvkmzqWNyV}t+cg%un?t?cqH9F#VR%{}ZO)`t3}qqL`x6Cjs8Qea{67 zAzEha27&LvfGdXySX{drXg?yVJ^F__RkWp5$`0N`-$IXXw2yXZ0mD%Ie)I}R!Kl%) zXTE4L`QppVTwzIkq{pOI`)Dc85JfcIX;=m>u0N({|BA=K0?eD`da9!Ieo z-gB?Fk2E+J)*12XnyoW!U^C+pZf;PLXlCYmK|jpzNcd%zBZ}FydK~m?S7A2UPpSY9 zg-$raNT09?uoHy%hnNF7bK08w#VJ7Ff9v@lZjQfjjL(cf*A;k9t?0~?2l;XidngV< z4B>*42AXf%;_xvbTvGga+~bKpVTI7RDta~ypjg<;c<=JBA=0?eJy%+rp0;&xey4K( zAVgbN?n#2xnrN569`ESr;d4~WE`I7L-*8NfzKTih1ZUTU*w|MS+NRDUOi!h|Q(C-k z@g>kIA6r#|S}>7Z@#$Mry|(KXk=v>IUnk*Mjcl3ownIDCu^lEz`5 ziSx`Y&mL=q&p@(Cj@>DCxh6dm+jTTVTDrm~BSkpj)AWOrud5U_H8@`|eQx4Zf0VzF z79A5ar@#$%L5#|cD;!0$K01eUmbxKTA;_QS(6`v+QEWu=&QIvu3cirm2w|^>beAx&LNlA0@xtqYwL_ z=dsbuM7?0{NNelPqlSufg5E8DHWW%VpD$s-ASD!|_zCs45^kg7V3tFY9u16+)xFR4 zs`4Sw+{$q+Z&oO~^L)gcSX+({)5LM_9u7sC5p&1N-ffE==7cL#Y?2Giv%w;1&#&YG zNG&u_JNeA8=8by^B-Xvc8oKmTukn;iQ@2@b8lK00YFl@I}DH7>8vE?cv-2PVz1u>AQtbL2LMgRQ6{?$YU4Q*G;pzhD<=)((5an}cz99JMPe5F& z4Hblp^iQL~N54McIi<*ZFEA_1c0j#Q>h4nV*&lPabx$2VlFB%M>c@&_rJkdf7M#2q z_<+k`U{S&8=8>@wEv)~7$K~M8fhhM0VUyDI_F5*aPWxb)O zHxGW@?}|ChMkSOUT5`!K`b{-)gG4{DcC$C1X20k(z_Y;A%mpl98l73CzW6tYBxMgAHZ(7ig( zf*`NHZh}2XkvcTb|9h9~UM?!s?0nLmmaFb!T+778oIq{#zuy4*u`G@s3Capt{Bm#S z-NRj(4_(o zDvcD-f}r=}#J4oxJTV}zrkmqVz2f&m0i;v2nvs>BWBZ%okU-Z7K-ltV>xE^xWUpQX z7y`J=2Lhn-g2@6u0m^_g>4%8=99B8g3n|aH#Xa@M+kulC;7`kL(Eo#Q91T$GaM!w> zS(Q72IOR9>ZupmpV|Asxz&nbcjcOgiVLfeub)5Oy^T&a=z7k=7q}wp|6gvYj)t+OU z_$@7uy8H0E^>s?`N7&1#Sw;`xZqC5A4YQ_uRgou;%3nSLT?zR;{4^_?ZC()OI~$;T zEor#@mSN}LD55Xz4?q34dM>JsTtywCDgTQh>z~c|(J-Y&oZvRGk^WS{wnG8iQrRfCKf6J}mX@8bKQawmU zg|)r!dz$0o`2I$aip^|*wt$dGkH4YWbJpK8(d-7?j!PXz`gm!KJ$Ohsn{1Tf4-0wA zt;fe^6M`+y$VhW}-~Rj75iyFxq*mMa?rS0NsiAH5b049t?}L;xt4dMrB-*NTzvdT6 z%TWl=#KnabvER-@6!N3RFxuqiqPuMw8oHmrIpCsSZC>_NydvS##cSqK^S!BuccNRL3&e=SJmTBA%`W0%F1MyJJH?AI#BA2$GdEt?GEqGXuA zTDJxA4f&uA`A!JMZAA&%On(qZinV1vfB#wa@$Fw>Z(1`zCm~zFnz+v61Yetyg6caD zfJ!nl@3E_*A{?w*ZR#_Q&*ibEe3*P<{j!S!w35^8?}k%Jz5dy=o>PT}o*3GMjw&J| zV~8$=B6BKg!tRGQvHO!ybt5b$t~`H)aE)uD(R!_$KK^=@Nvt^Ou*Tn90ICHM=Li?I zwL0gkW_^vyb%2Azn*d1Rnn6jY_Q3YjPO%My0=cfWF=CEKI< zJ3W`|JnloC^o_=czUy?j?LvmvI`DHZvZH1aMskTxnAlcQ*pxWzNriBV-gmt-rG5s+ z7sKk%mU}N;&5oX_1%QLEX5WSZyy`CzQ@jT_WY5i~lbNy#i1=#^r4tixnNLg1DOOh7 zKE8lOHd$`pyPI#Qjvs0jyJ1*ct;ILXyR$R>1UuxV%l0XVKIELY@VV5&37}gNklB|) z=4)xXFGB(->>M5#=Z8_od-KuGaoXASg!P2Xa{0n0BQ=r&)1*9STy>MxawkgL%i^wO z#NPBv-Cq4Ny&|mm*<0pIyx*Jre|byCb?R_2MhAx6gBdjTJMX012o%l$xNz)=erABA zKwpYn`v57#1zXyi-zqmYTty~_i95Ph2q$aJ5n>S#Ni8HVcnQOV=HO$h_|3CWckBp7 zdB*|vajmlEBc;`IL$c)*=y+r{KiDE=_C?6|Q2rAjX$7+nBwNZ~WA&QY44 z%_T|;e|DpBun|BMqJ_2!0abaCT1x#W^ZuWlLd}|W^v58v*p5OU6}$A>qI500p|9XN z!%#SUkc_|Gt5rNOzxS|9qV3p-qfGT41quI0iB-oqAISy zJ0}ZqU7S{zDn0T@0~o1hOwiM!Gzgb$A-0>;)*0F1p@0mUw}WM(N4(#Sp$a_n(nqQa zjlMC9z+g7_&oz*f&n;7FWgY4wR7Ypq!j3r3qQne>lHp{@Rd} z-ayK&florNWI+e)bApvqXIo5(&{S@Pt}{%__$u-(TQBI+UxT7OS?Jw^WcSt%Dutb1 zs>BlS1>ybct+P;6)(8TLI0I{Xv&WwR`=k@(JY>k{X*&9lFZ^1RJZLCKTmSK|=B$^O z0NCbZ^qDPcLB(I1DUFqeTv({iWIUCR7g-#=#0A5TRQe=(z-}@r_RS!5GGtmhb6ix~ z?FXa@dMPZs9}QEC^u^-HvB_<=dM)FAnc{_6jnI*E`MbTzPOiegk9YOeG+iUh^$L9O z8fax0lQEiZ45_Kkd%R9=wOs>sJHs+@{v(gs-`eUzE`3JcHy!D}=CTNhJZIHrlgV5h z&aKUN7sl0A6OE~(Yi_+I83>S)EK@+?V*R0V;&hx zM4{)2K9Ecal`w4BOcWu@q_jwDT0=0-X&F?A->JT)~9{nv>?A@D#UqbjyaC}>} z)Y++9YSIu)(+^KAgiom_Ct0s$MtThceO6`6pIzimvJzkQ ziR?IwkrZ~`Lmly*5#AKGqNyJj%!tBWwZtoNs@DeUgO10`=bC(D5sNAo$LO};<2Qa8 zsL2Ou_sPmYM!{cJRK_E~yaXTj5UN=Xdy!U2V?XESkX@`4^nm-bKf)=!s`O@-PJu8XY^84xzGF!TJIny4Qtg(Xr;7>3o(Oxf@WjH{L<1Lqo=b1DesrLn5HolD z^$g&_0hHc7RoCdnscdwAtH+Ha`F#zbMQH4ZDU&?$X@=qYTvr1Kh&@0nqHo`YuU8!J zbP&o+&U3;mt(SLpJ4GIA@Nh+-=`+GaqHa&LW`+|@Gw&%6S!T*zto^lotC0aXLp?Ji zhW#7dPsUJ61RswyKxwEuC??RQ4c$;0Vb{Z~Mz`=T(TANGgFUfpPJP?5L}MckW*}tG zrPc3-u!$QmFt@^P_qZ|r_|;ShU;->Od558;#jp7C@+3CrT}$TWINgORlWZZG={Dz(eXZONSxi~)VW&|wc1UW1{P zXlKc4Fux=P<7WWX=NZtI7cpojBwLgTi3HW(rf@Q#)wUY3wj=SM`srMa5w|^kE8gPS z$_=&9#$Q|N0XG(S0L`a0TJgAlMFhY$od`5PROgqHH!$Q1l0i(Qd;rqxt?Ef({C1-+ zpCVN|Pn}pG9y(|tltYFKg655tZI+we3?-0v*h$I`d)u?~vf#~^P??*jrq2FSkq|m< za%=g1vHsz%(Vl=gFi6u?m8@(m(*JG_X+2P^aA>#ZUY zg|4JB|EEJ)G!A#mV(h_&qAL=vL_Pm=j(IhM&p*}Ij<)j-P~(`Buag4uj;$%^Jv2O+ zv7q>jq)}R-Ms`M(%Wu^o#WOxa6Mw!w`7dp`^r&uIGQZ*DV}@gnsr!i9{oW z-p6tpTvGxmS%DDMxvHMoc}B_N3!bn$DH23N^D@Jok=i*2!n|R>7-9kxMUMEG0aX`UnS4WVlv zr+AepiNa8A0hDmjoYgeU#>}a{!$dKN_8Y&#-(E1 z?E$EZo3RB!P)thEyucdW+T?r8`ig6@HYq7Z{ z(z|r==<`YH_Qwpxo?HAiP}_m zWFSpR4%M;V&I7G|2lleCb&4|b`h-!LG1!^X+E~3Lu^xS+oXY$nnZNp31T_JGPVBNE$ zm)E_W$r(k^9`%&amX*aT^n`c0p%Rs--zWR18gCY?(|u8*nM~vw(Ziuq`bolX zIXmd_(6tz4WDO^mO6FsROOUA#*GZNf*Rq2cq!JMg3-m)?<&XE5JN?{3n9+aVN~ew% zlWOE?D#iV}LWz8x7z6ZxAfJPCW2Oy}efRX2F>o6LSSGRtVYE?UT6tBp5=QQFXN+$L2^Afp^n6Ei8Rmdj^%LPTMj? z8Y)NwxZ58Y?GaXaSqA)mXSugxYn8$E?wbMPU|6QAf2^_@ICEY#8OnwSB8CV=9`od4 zkBAY3ts9~jRiwnEZ{|3f2umAT2>MGC4ASZ;5FC@FJ}=PYOR56s9Q8>7+aE64i2E7D zpP-lt;y~qo^QxANq$onla6WA6BKWEdF%Yh=;phjoaW&rhN@$|SoPeacUF`1eC!ipd zzV>xac3)dWfu8<5&L@0(JJF92>CSf%d_{?kotJ?Q!uk&=$1E9hYpq&3igIW|3EU|KBIHIVJ!X!tMc|Y8Q-`sksx>qnxSj z&jR?p{P;QE9Vu=Dvo+J8{Gr}!cCcO@;9Ar|$ja|RnUh(-l^E8q{qLvTw%#hoi+?hq zM-v76XuH-gxWsyHNVT{O47AB3RKe$L1zbgeAUzHU)V3mLuz&j@R1ijmcju!6m+)Ca zf$9Rb%81Wl=3yywZrmKnJ}2j%f0_FGmpX#hRjAnYS~HLs85SXzFVd`C@ReF<5A2HX zna@4jPg~G}6_&P{ohD`21S#5m39J9Wb=`e*O)JL%H{E#Qmzbu{k+;O5xmqdwR+$!@@_8Pyq`rnGCr zT7L1QEB4a~dc33lu&)93Au34nPFl71PEIqYepFlRA5P$=AK5ySu*UBF!Cx?Qd+ z?{haywLjix{rulCGkalfC+S;Gy5J8NWB_-YlQOOI%%2Y`-iQVC!Y$(<%tyk-+e|f_|aU@vI!PE9;e=y6ns1~G9 zppo2%$Hf7q@}Rn!XGSldkdDe7o{#_h^u({7*+1fKY8Ol9-D>;}6hUhmmXTzf^w*K< zCt8V)>3^x8H@bC%Ak53PtE_^N6OA z2igeQ$Gt`_vr1bMNxjl>FZB!A%lJ-KVA1GTv?o-N3$TFsf? zXBry+@*{_h+qt55@8&Y|Z%Nx_oy`jzNDq#FY~!`DqL&NpvAHQ>ZyYrA`xzWZ&*TN* z+*l>-iL`}2+7lh;!3%tJaW}ySJ7x`;J1KW={dT3T-i|z8z zgY#?qpr|g)#+6@IidVCp_O66$Wp-&YR9Y|R<{-7L0634pw;*k$6oY!a#nH}^8OSAV z(=4I|htSCl;OPv&M*@zY=xeEyqdX`*gyEz#WEM}R1M>co`q+z$aXeoCxJ<7j6ye}C zshz59ZeF-e;tK!-K8hIj-qO=Zl^6N>OqO3C>g*hQ)2?%JaySWSy#ZQJQ7I=D8lM;V z5Wc(IPD>Ywn6w+Dtw43T8FB$iX~z&J2vAReFP0kdH@EW`*L3p6>}kHbAiAP}PJ zluobZ$(E$#MApjzicgP4+f^{ZLZpOqL_*0ozxRzy7R#rD=X4&=NQ=oCDBi9;Hw`33 zsT2PF(v1jQQ4{G+J21i?=xwBA8H7qmZcL8Im76n<_6`nS^WQ1+CwKaOWz%mClOFxh zql+&v6APDH{~!ae3!quX75M2MUdApQSFTQ}u8>(&>loy6sk#_0ziq#qW78Jfq9#2O zy<45fV2pvhXW)63*3gifn`_;jrX=o!7dEVVRH4Yts-N7eYamq~sbm18YJ1yw(szjKXkzIvhuuhwt~eZDuLux}+k zyMZA(N41Go3SWNJPafiOuHAzhqV2UFEHW_{lMhxD4oZ?hFu`ZaHT!D?Dliyqd83mD zT~I>2U~FnCdYFMZI)s(KHS(&8b^m8A3d=Fd#v<^2ZktOM=cJvJd`UW~$|=$VYlfoa zACqd-R=_uR>Ig4pIFVq7HShrCQ-31HvoYa5r`1oMQWc%9n64&#&8=rkX>WPtjO2HQ zx@@CkSozhP_cq2-W!F{Awj)n)gCw?m>{y6(T;#}MMy%@N}RZ~X`XCAb)+V(k2= z&mxl_WjR}n(z-I!)i$;_GFGvRGAO!(s$9TYj}9P>`D?d2Tr$w;C-Un>;Vr`z?okCc zJ9W?NJGxpCz)@PR=t795`vpP@+5qz6G*>!xyd!v^Ahf?(!E!0ti zNKQ85!KXs@8uS>96FtAZJ(nXEyuF!|of0YOgfDmZTS*){BO5J>os+)8p-uU3z%(2{;r5!tJf}OQM^z5I3Ot>_?si$D_@!?ARUdjOTMg zGc1;<&rpbEcH8*Lo%y}>Q792(vpYJj$k=@x<)XmF$@x4^eA|KhM9!ki!65xT`26Mp z(WmQr#k&>7GQKX}pw~x~%nzMbHD^A3RqKpEBD*j;Q1A98<ZaRONp_^{VxWkT&r9Keo>Gf06ZcVb#&wI#Dn|0|$h zg!0fo9mGlp(|h<;D~(2(X7%K1Q-#U}Lo_8u&O>)D9N~PQ<+)_IlgoI=D|N~lGh$cZ zI+8^i^U6QQZ?47Lwc%pQNV{Y4-@f!S7&7Zy|8p%e7f}W|EnR{(c+s_`1YuBpld-zG za;cUi4H>O#7Z7=|80&j>l)axFoB2vOWoNF(zj~q7r}TzFJp8nP$}qlnpci>r-%e}y z3%cyACI!)7eYlQ5s@2E(nVqG|~muqs*vHjtahleNj{Naa$Mr3!0j0D8}V}_8! z(-En=eS_w^EDR=|0UQRa>?ofN`H(({j|D?ZgCiTRdPnFy+jfMHX+hKMNsfMU|2|i> zSGQE7``%PX-quLq;~BeG(|e&w)9IJUwW^Cmo82UZv9_=vO87nPTNyOs*dPoh22`B4 z=FbUdrd>rO8XXu)&EQmXC8KD^vPW~iA`8vzVz|~$iZwrs*G!r{3K+`Pswj>lW?RCS zqplzzVfz`LrO%AZM>AE3)Dxw)%#MueVX?hb-K)e>CYjgNic(jNBi~~D_dym9w-+Q7 zxwmpKz3NYs#m7M9DhM$&F&*gfzTedc`iuC!(8&`a9N<^w6Ul5!BG)u$ntXZe4RjkT z|H6Y3R?C%q!?N(~Qx(c4UOb+UYlLNqdxjRMrs%y32Gu(*(818pr>+hG6^^au*pOD<5~TBn(&p&hTGrCBm|q;f4Lf#+Y-_42AY9(J$S}WCTJ(QmKE>xzzyu3USuL2m@&kVe0FdD4oz&G@d zHaBjl>$IY{&Nm!+Eyg~Y+YbH*i|NN)o)E++UwE-;3~OtAI0?D2Q>WIJhNf`v;EYV% z5ZB5}p8F8`d-IRBzwgs_f2o(;Yif8GngSF0Q=c^Jf7Z=-!Xu&Z?*;zfWu>XWFi^N! zsYYK-OhR8Roh-|(Enuf=?UkC}w!_0hgkNPZ%#+|TglH+#`63Yq^Y*2^qB1CyB|UUr zE5RL4D^v#jJHM{5(oEAt1X~Peijw7WP?@K8f%W@Z*T^ccMetONJk@s!S73I2|@_O+oGM;)5`v<LJ{ItIw2^ktk_oB97YeB0pun=j?y9=;kAN;vnBcOEl{cNNABP8MGC^GkY{c?pyU5oY(6Pv6R=w62Cy z|8bu-(f3&X;8ub!ru`q&y&nscsrZBB2IUCVOzNH$B8x*Y zTALaTGjB39zx99D5LnKfRUGq~naeSm>*;y?Z%S)VJu(maY=b5dZyC*_P-+(MLjRBl z1hC!jL*%Ny_V2rvR*OkiP_cghku=Gg>GUQ~r>qO5_XKJ+Fjkc$hD#T4y;|wG+~zjO zX?D|3#CjJupp!zbSGT2etkFC65h19OL{uY08#vY=f+ReZ#(|;j06>0%b?2Xu;3f#DWLbA5sTAywo#o@vp;g8xy-cH2Ta5F zGm4x?iD${J4`ZDu&A_`%3DHuFBgB=3-aBpgxd3!yh(^rD%;TNbXt!*Bz3NEttvUOU zeu^{Sm}}`X;SY4JiUn%^sDPt#$Z?i9!MBt=#%%O@+_d2)u~&`}fB2!#(YXKWR@KHv@5`tu>2FZWnK;QNbbAaTNW$C24$Y9 zD>Y~?Ko=EN)m0Cr&*Zfot?)+;sl}m`t{$QP1SzucFoWh zCBvn~$dV)+?^veMFLCsBW-Bvb6)Dpz6SC1NnT=f15DLn)~%zPc_%p*B6o50VTqCrrGm| zx69(ZkOCGFQsS7V`!M47v1j` z>z0fad-nURT~n5Iu93H2E8IFbMNyJNjBWJL)_970i|^r^2oIdW4`_nKd>Cm5F{j7+ z1Q(%2d2%c9cn%grV^gEg2Y#>Uuwr`dL2{2He0~0ez_ny%N!RUOQ56w7mhtClU0$cz zK$Kk-6kW3Ato^sdmzA$%!L|iy`aEr}F^Y%I8q6K`HNGC9Ayol;Q)N4h>PNr%q)3iK zx!&jz3JiyzTFMnNaI#nb@m*&fAsI;FCLlNSbM7BAeD}lgwp<57l!zh@-YYk0e&69` zXJ^O3sDkpx&h#vuNj&0g!=D+NdbW}Y^i`MV5+R-Rvu|f9%gIJiy>$a^k$#)k^Pihv zB_$x5UJT#_u^MDA6O5=#y08mJ&jS_j_luxXCjPmr09Q7N8gB+;mrq6pRlk^%Y6@!% z0uz&Blqh|)-P)unRrT}jTn6p!z~jTuJ~m9lW1qm@qE%i$>Q?yZDj3YrdwXtWd9tJ_ zVZx_d58Xj@MuU9){?GmEmK-En<4@qf;RmDWaRo3P2&%f+&d%q! z&G)ARN|V#@4ayAjw!FhIi7!O57qH>6%kk_-fdh@>yCi*6!&~>;Nc)z(^5*wxCwVlR z6y@Sg7W(@7v!K~urlK5F?Xew^Ofs56O>2@P=e^%L$hwu!%x+!Fwj|87k0J@i4-tjE4^!(JPIH%reu4Px z{@%IC&opTvZjBc`rjZFfd6yv+g)x=Dewe7OuI$4cvC#90KmOfAI2upFkF6lrXnfSz{t7mp!=L?cRC9_m98ZDsxIi zMRDn349D;<6pCbPLd`GDljs6#Km9$4BpT2zv0zywC}}w@b|;-Ne#GeQ{D;!POuf#> z$@MJ-c~#J^6(#9gEf)$xu#g@Dm8^M;#Od2zaq7;U{3SdrsUG?dw(Od?FM+bHAD^2e zLO_E#m_lrSze>?~*tFMAbr{i0woLLj7X4`HqTG|LUFC?^;~ogKt9HON=bS91Z@2Sz z2yD9PAK>(~fIQtyXd?7?FE@^N%_iDuJN zsGH%SvyxGa$OqvGdz<-8`Qw!Nio`=59ARq5qjJy$lbt_L&Ds;mj>}rOYVcgs?5>h9 zmBS=Tl$XZ9l)jt+_02}`de!eR6fIRnS;vg3012c)9oOLH)URki&<7tW# zbax#z3khj-et+K_cy-i|S&(3r-1ve&B5JP<=#`JV=pT8W5cr+v)7M6-62%@dYl6_W z6Kf}WV}rrY-p7q?$H&Q!JoWkP{R4%^ZnzTjzD@=e;Oz9>q!SvEm?=sYPMi-tjVfJ@ zDlA%u+1nhG7OL>ujBl_ZCJh zIi)o(wBhxt?t$xKu5!x?-2MR5BpL5qUPBzY7Lu2X)H{+JVV?booZX1DIiMl|>3J9F}Wx8J!f3(g6Pp)bCyuu~03EnvU7TSndm z#$e=m+!`A zFtRsfWTCbHC^E!HlcYuU71~}N{9xRF_;5EPhkt2n@HK_3Q);4+-r~cy%5~A)Cwv(bRBU8?e~G0juhrbx9lB<#%1+$O^tJs#^A2UqLJ?iRp42zX`Z7!*KEU{;x&6F=yMSTmQx8+~$JYmn zJm{U*uqL~TR-q@goxwKWxdTojtZ1KSBk#AA8ag+I90Odh|FTNOMK4!c$fZB!OLB#c zyqp{y@dqBcADaR6A;XT14Ng(ikK0M2a(oqrBTNzGvMNae>(5>dsJKz`xkXPoVe(^rkYc&wo#U;YkxDoH zk*TOZ7`zVdlu!9Pd;fUVE-fj&j^fsk8cPY?)eTCk=HQ&Xq zA?nbTb4~+q5Vb-p0u3q_h};Jr!ti6jxI@A7wPLD;(yW-@r%xq53D#Rm!$N0&>F04S z1H2^86G|C)l}m3DD;Nu9A6261o&tguOb&2QbB?paQl#45nemsd8e_0}Nq+(iC z0oBY1EtA+Qoq1n%wfW6-JA2<~ZUz!EI@*f^LtghvL+q<`@`IT9lAjET`uflosw1PYLxhZJdyQrt+Wx84Ut4J4dFfY0+`zWu={ z{VAr4iYJ^R1D8c)$aX6`Fo17@eBfv08$3FUNQ=rGZoXcNoK$;8ITazA6&>Wi(h}Ff ze`{?I&3m{oL%Ki3WDLQN6bl;TVz>j6uEP)KL|j``b7zB@W-85fw)*9L1$D`8^BufonEQ z9vGz!rsj{HuU^QUz!~+tUv5eDE`w8F%^UZKVnH@UNj~Zfp1=Rh?7CEcP6l%E^z?Ko zP3Zo1_t9g4{Hj2n>B{kgM>%Dh9nPcafE0pD$W;pPM413{~12p_Bz|ox-X& zccIju-$D^)$P2SBv-@tdpTyJN?LS)9S{?ybFbn*=6nly#|I=r0d%hM1{Wv3vW8`?? ztp-_=B%;CcH~*KBQ&$uyK9)EtdgY4@JXss$s`PzW+}TSf0@1h4`_O?u^UIHy617e} zlr}#RY0{k2>_4K6S*s!M(1?FYqPn$CHHiGud(U#=`m;LaPC|l<6@gh71?x@k>n}{4 zEVIL0V1b(UC2>tYTt^PegWBucF3NWNnUOg~464`xZ#?BcC)GY{1}3KPORzFWo-6fThgN-Iik>G{|VGDFjvDi?`c=H6U=Yu`?V0x;2fXa^2Td_R~6y1%mrz7fIxX!nB-JCY+}ODK%&nbDs=*Zr5Q^?0GJ zyn^@9sRvdgkE>9SKYTh}`>)2w4n{1CYX)Wsgn$c3P%PAfD%|aNCp7}42Nd~85mAC8 zFXznCH3f7H@m)C+l%1(;8-5xkwVhe43sePPCD75v*gP;NsY^O5s4sI=&nNs|EOuIpv>+DWAB z?DL+(!$T>SEPv^QD%k;uq4%_2U=;zT>0%Mi1LSqvYd3xQmPM zES$}jw))3qw+m-2_FFmix*-%h6Co7Df}+rK>Z{>>^CCBj02$a9IpY&+WrgO1EEY*!^?G0-^*lGK}Mu)=~M20+kS@^N~^Mk6)t?i2$;Ge zJ&XY@FOCxoN>h{Dw6;MF#+7JKv@3nSMH%MW$*wN5=!I$~A-oP{r?}|*s&{Mu*C3M{ zbynn9G&F}Vle8wUl%`BTH2RUVyy2x)_B*Vb7z`#&aAyUS z)vp1hLgvM)%<@$9M(l}~43S$XZeuiqf^b)v!!c;)Ys}mYsp>-|79pB>kt$AGF!PY1 z-12#$FfG|vJqbvMAH?Bv<3{q&kh9V*#~{7Lqz%Z63C;*6f9~L0l2rN&6}LZqLhG;A zikTnzh5MHaw2F+MZ!i~>UUl1lOx%=LKU@$ey}Xidbukx_qe@=x>mS$@@e;Qzz}(_B z#0Sx4Uo-)M?=5Xn)inb7udYg%0;h4tjw{X@dJ^072qKFmAm1 z((%gO2YV1@eCj;NS?51bp}^DN;g6-;k(_O;mf|ZExbz|u&ZJyMqr}5T^Uu|#e+zP@ zNz(-NT-9)n(7onY6uVy9)#&NoQD|wor6(cBNiFk@-HM*+QE9XBtTEoI$zPJ-_A070-sS5a;pqK0Ji&y|euX(z8!8<_B7r%u9rlsuRz>{Lz;h;Qq%|T6*!*Qc7Au`6RQ0eVzgjn%z0U6r< z*cTXl{CE|_I<&KAmlVC69zddXK9lalji)i%Ad zmuP%dYAjHypLh1+Pkd&i3!&jGdDd=>q%`4Y|6kk^EsfGNd0-pT_k$RHXVoG*xW`FKBqyr&QqoO`qn z6U3WU+#IQzV@@%SM}yB`8Kz}ZPH^_-ZF&-?`k-;mC=4@<7whgpO=oGux4*m8`+6IR zR5ZUTHCiIx%-K^gR9@bWfGJP$<8>4G5`-QP-1+{YhwR4$+lKIZ`5G)sM7Ct2d})wk z*{LHDaqGtygQuDF7=qREh_j{>+L-1wwpA_KmQj#v~&i!q5&;)L7sI^as_cr z?uFlxE>|;faJMKvkFio^ZKkrvu2VYN62*~B;2?|aeaQvktQ1X|o1`3PsCjv_<4^Ax z{9h{F{}q|u=EUK+{dGw;Ip|W1{j@bpibvxro-TImy>4+Y{}PCkFxCtEEj zl;R4ie2O#q20TS@p)azgPcqwR5~$U`XWy^0!eIi5G-nj6u8dY*_|cY6$V*W77I|Lh zz!Ekv7~k0Tq-Wycb$KzP>H>w9I*qSaorMsu2scX9!_%|q3iP1;`y@mPn*zo@htrY2 zKVuNJZ{OGoaMO&zA1O>=)*%TvVYrVn1A-wDl(88)B~xcbiTEwCllvwV2CN}RE;!BVCYk{ufelhuor zKJGx@2{0L4j}Q5YbKjjSPKHMbyIC1zjV+#mmV%A6@Sk6?`;P>ObUU}jMkO&oe+aA_ z4u|UX&1~pJ#Rz_u5{{O8ozQAkipfgzB$FD|9{8gr3ANElND=+V`M^8J9mDXP&tFiB zv&Ib&|0OpIq4ND0ifDR)fBeblend1U1>*L`_$SdtKmXdFLhr00Wxc|~HR;Cr#F@SC zrTgJN8{N*xXZe1TGrl5#2(fgW(R9*K}^%oVQEUtXR=BghItfzQgBzc z{&JO`a>WX;`prV>*+KoX)S@?{?rpi6T z3cT8E!E>)$hhLc2*^PKrVxBWo>PEv4Zm@_kPD!D&G4DCZ96fGUJL z{8o55$!-!hu-2H%9c>dflm>&di%O%xuaCZSEzeBeJXSpJR>VJ$C3EqWgNHYUyBsbU z%^5yC%*<8)w+jE?tm3CPK@8>HaM5T~R3%SlJOpO%s~GlWZqZuF%<95d$!elv=pSsQ z`$U{0zr9RmGMXS>@-@_V8D1u>Zd;(`FNouVB>5%@(cBM^*Z)jBPom?WysLUjYn9sb zspcQ?WmP0YNI85&qo((cfr_&o=l!25bs72?9##}t7{b(XV&wc&dXmJ8=vdy?;b)` zlH{BmsD*=>3;N0>iWvJBqL0S`e%>VJf|=DOL{iBa~ih1S7RDQlPaZ3dT3 z@4fp1M027MtW@9i5^Vsn)>}o;cj&>D*5${rirAKz^I~;Z5Ht(?b)&wk`pI;%bVK|} zbcb93J!XOKn`!@lQV*=|30!peO@W+$Qj(UX0GOF_^tHh>-Rp2vtHctPknNAC4*C84 zULEtVchsV9vX`f+1Ca7&GmVZsPd@8Y#fCy6O4T|)lPGCTGgdEv2-iukn&DK-B=$+} zT89})LT-wD-{Y;m2GtmU2Cj1iqvi{KG3}Fpmde*tb3S`|w;5Xc70(5I(Jc>ulWSnR zehM1zZAA;|fYLu6hk4&3+KTm~BVe>lJPsIhd7tYA^b%)6Ow;nZd$OPM5&54_B$cfu zQH%X)A_GrFr+rs~bMBlNc#G0VcsM84QiG=8Ms$(SvgmeHlQ=Nf^30*Xbm!jVj5cVN z#Lb_S>O)7b05bnv>l&U7v<&%eBBw6~RHW2vO@Bx*4setKzeOqndJXu*K-%49Y}^$s zr~Ra{iVW1Q0mq$Km)HDR?OSE3WO*OQTwdV$?XF3a{^9q*&M!UkV=hdS>7+=Gs}`|>|#tL7yg z)_rFPmbNuIl!%b)1n9f!i=D$k*QP+*9VA1_od`9h%0i4B7Mmj(n+IcNjbBHj&ZlI- zRJDL6#%#aX zB=|1IcTpqoc09bWzqK-5M&PiFHQUd{rQFpXU%pE#8xLMEYsQfd6A>rE){QFt+03Pl zZBTMPlV?BB8^d1Ed;^jorZ zWw41^3@A+aQaXocM`LaGwfFSO03zHFqLc4Qwn24jnHT)g+kSma{*EHb;xOX~2g;e8 zPj`^o(khT@+RB%3qKh`JfkM5jtoq@P_WT_u1lLfm?~6xwPjo}W&c^;6nWyFqkw4GU zpRNq<-AGi2vQ;Bb8e9S?e!|;d2tx~_=2Ah-Yyz9tWyi7Xiw9oQoH$}7I>Li1)oN+@ zt$Z;<#>H`y-iL_R=XbsM>MI5XQ3@lCon%?@Pr&s{K6b_G!Two;wiugdVNq*Md*!On zLe8Aq)Cq!>f8mYuTPP*{he*_6&luDhprE!9t(6}y)bg}K)R-h>bl8AaKLS)%AqzSu z*%S`5%YOl$kEp6yCuz0FLE92*Vzxcd|I5W6MHQyVQj5!Q)y9j$$i+qgHs24{CIWD^b`CqG&voj>V*~`&7VjpVr**m z3I29#$s-CGGoJ+4QaeOm>`h}mI+bcezAcZpUM*#)6cUM?6ma{q`)%@P|K9PA6e6!z zH5Ve5QPg_mOp^L>)XNWN%z$mW%oW!i7pLMln7Z{(=fSyy5nB~@&wL5!@RwW9%x9`R z?{OVy!ZOXM(Pus}*V{Qu75I~e)N%Hic0XjaE7AK4Jc&&bGf}8T4pje zR_VjMm1aezK4qzSsD=BKK9+vLMzkC_gI|i-j+9ZT1E)=W9hx+@26HijfXM=G7IV6- z3R6~#(Pw2}Rg;spgBx|2C{a+PSC!>ub+!vh91IJ=qVEjHKL~CD;jLRD&4;)cKk%4I ziM~2jF6~?MF^o}?X?eqO9of@+w)DI#)}P;+DisLTTyh_P3kpGDGiGR2w)YTuS{frm)`*>U)Uz9X7G_F$uZ&GRqbd~MLl{#c76@Me!^1Gt0)RZ62@Hg4Ho*5H0NS=ND<_M?4!XYP+NU_I$JF5N(!oxr)7~ zSD$wy3{5TdYIvL8_R6Efez5)>%R4cb61MDeipkFm%@?#`r?x8(e3%Z*`qInx`=+kG zwQSAhJwnLMf1a1=!we=>EUi%j}OJ!H>t8zfVj-m*lKSBoBc*&j`}j+%20eR0O6pdZiD4j zAd5g;7~a!)|KMU7VC8Gm8}>ybT=}~UxUzfB)#R1kGBJgh{an3z#h0`)Ly!LHMnoUO zPpxEeTTJ<+lOMxnt++U-R8!>W;)piSfgY^V^WGpK`Su60(%=VIUcKZ5wNG!{Jg!=f z(3x$FX_;bD5(ZO(V1}a5NM-?-12A};{(c2(HQDI1vJW}x%;dJ@b`nhga6GZ-O}Qpj z;uPDn%+zPl_---7dZTJzgmJ+dO~Qcb8{nl=HCoa)l2O+ zZIwIV#bk{=7doP#sF$#bzt9>np+Hi(2PgPg-DJant`=6!teX6@TQ+GfXdqkRrcA*w zlGX!IM7y(#8R=|Y%Y zOc#xuQlnxr+80vSf!VcF8#7?t=ZIybUZuG=8WQvS1LuK-PxW>`64f~Z zge-ka?}(d8PCZ>yh*I|>QkbllP$x7$LvKeCQfk#$qvt&7%Y5eG=tz$e(`2^3q&j0SxMK0RDOZoA@ zG0>Okt>t=z))Hw5@Qsx+WsLpeO-el-iK4h})fhUCc<1Od4m(;0Y){SufAHF~SX`p<0v{00DvGDG(xV09+O~<}<>WI8_%*QmY;x@E%Da-`ybCk z%)~}Mv?H&a5K+Q~iAOiWI&(^Gb@r@0LjaQklmOYWlIPmVKK3uZ`@Php!t#=&YUNXm z?ZiS zUzOssHe{5R!|G3{WIuabJpyhss?W^_by@^BHzR=%@bSMu$hVBoO}1cZ6>T7RV5V_O zZ1biOGlu4^dKwu3E;P;1O7g4yv?ey9fAQtxhte=&m%S3}*n@A=JGTm0Bk4T9HgvU6 z$ON3QI|q|s37I2IlP_KzW|inylz&VHKQU}{v6>_AbxbBAqowiehn@l^XL_8kFSS1~ zI=>t+0yIZ4W(aElfO(>Qe6AKf&8~+%+Y;Jraw)`iz*NBphF5ax|D~J$inh%}Y5jKkbL${CMfO?u_4xX9C#_DqP)V=wZWo)OsO$@v>XIBL?t(QJ38UskHDL*(B; zUVrzP@e%w>=fk=2yO;V~j^Jjiy#*pP=$C#nvOujKXzm6gV3ifo&y9y%pF$IDo}X*1 zUwTCa9n%;@)6#j1eu$+Q;omMxoR+U2j{5d)JAM1-KU2%uCdl+`O>{l5K-7fX>1>wh8&kpS%2RjUK&lyek51@o>$hD~Gxga1P4$sHN>90+Bxp;>(%zgu_D zki8ttceXjCnx-~G&fG{G_PJ_#p9j|C(4VQR9a=D>-@4=){V%zpeFYZiB zI*Epp((p-2*0@OQGV<|i8mvMO2;@p*wItsar}zy(r7%z+;9!^dv|m!u)|6L;BPOwx z={nX1F1i7%9vL)BH)3m*tPOZre^t3#6TjeBv_Ia)ne*XN-B9t7tBjl&_0LYac+=pr z3ZMzA;bd!G$6)NLn12=+tuR@Nr`ZA#$eVlO&e8c6DF;SKl*<^0CZyESv2VQZTS|cF zP107biBjijNM20Hu#~IJgZWxl{z~usOc>JyP!oe=P3V3fDpu*3qDSn|lg|QQl5o~7 z?;aoS#_Rs2u~x7)0ffnp^OyUESe7B{QfkH2w8f@>I2qI0fK zsE|<%?EH2T;)w&l$W^e9D$5smYzm^b+H$tKk(cLlroq`s)9?8!@DVIR zGaOnGk6KNqvf|E6$rj=7V((GoQnC2}4yg}+nBINBk~$8K4T*|-vaP%wzx^IY3Vs$5 z=%k)+Hx}zS%Py7Xt%)hdFZ=lg$q~?%xV2J5RKU4Pb{>=_hOgG&ZJ@29NCqjIfQ< z*~B@is|D9yso#vtFFDz5-Z6g1YrH=0B4C~U(ekrJw9^{HfnVy(d%AhaeSMqo*X#If zMi`3f=q>+&s|%~&5R-0yqEbXE(Rnkmw_?07 zai7#BqgTh#Ox)f7IV}5*%#%k!Jgl7P2T$PReRx<;Ri~&E9*#uUjx1yY4%NG1i1#+1mV36KGNYC52ft0!pcx#(`u7th??<Suls!g63YDNz2;8CR}1@G6*XpGN>jMaAcu$rZot-1 z9|Ok!{gVJ~#KoHQ@Fa?2B4Qi~qQ5iXn*!mE1ZJB3RRh-~5(KSL9XY{J{3j(3X+#IOcIXCf*goi!HGgv(F7{wA`eF-u|=p;lWxLu

=wCR@#ht-IRpuxcl|!FfcQ#^I}q8Fpi9xI>tZ>hgBovWZAy3%b|jmQwO0za23r5!DhN=X_qU^JDQ1c zeQWK>{cImZf6(nV!3!*Rf;xZ@^POVIm1AP*yDyD{uplbg@wwr0GE9#a!cjHZl;#5R zuKHKww{5Ok&BDmD3!Ry_+q!81u2q^2H8NiJ_!1`5a)!OF84&2jgmp3TFy zJxzsO71IBman^v*Y0m=dT_$1kLOE4Uk@?N zJBLNMMoq910V8lop{AwnKjv7XTo)Pv*<4iPc~41B#f7u zv^aR+b{5ymknkbhyKED3XGXMBjfy7fcfV}-C2!(e=GKI462hCc@1CH7J&n)upM8V+ zV&8TR?)eS(rv|E~{a+3HzYct^!oda1ve#9;*r&CVwl9OP&;Go6%1>lH1^l0{$XqR7 zy%UVwK2MH`JU&A~f!;&od^)wTs!Wu?3}jz{^=S64b5?afL@FHWgt38cQ=pKduK zKYZEWD0nNeL{Fe0o;r(J1O^#S)c-aX+SUR^t-Tq9rYlX~EZk5;i1sRG`p^9&9kl3f% zCY}=2`E}+eab#X=ufYg48kQWtLBhRV@ej{MTDlTpUgyzYrfJ;opg$cBR(ZJqhsfmv z0`L1#A#8&fqlBt%0fwR|5O$D6CG9Ksw$}l7BJ&b!rd{; z@mr#s6J7t}__Id2>H2mj?Q~&S)UXZ;C}PrL`HX7!yUk<%aZ_A#1rG9`R6bqb>clA; zCL0sT#Q$wK`DBYvsx#lx5YgITWSH&vc}A^}Pw(1)nwfDLM%5k&2gVw+ zfu8-qO$^Sc=_a{Q;zK=~;#$dBP4>|Ogt*izMQgtGz9;?AiFBf+~23gjF z97Rk`fE=5QNaBc1=z2cYAH6dmESwQyz;17hPzs5>;S~Vrl!!=^4TJV9K~<(jxTd_s z?Tc>u9)JYO1OT{T88$C{`c{txwV#;Ki;}=2`w>;?)id&ZjXr^{b05EVoF#N!x46lh zr@vZ7zhu2~cPZjjFt<8U9RRA5`-mv)j#SsEJJavpu?1>Djw9zvOSOu>_-mOeXU`mu3Sa0_XPz*tJ(lyn%9>%iApUGtzq~6|$2DUG;>Ak^k(b3B5(V7%cgq~Z?-owN z8p*8t-epD&$Qh=+G|>zX@ViHvB9*a;9~5oeU?4MD4**3S{yx-~7evm=^5O#V_|=n< zpxwkov(s7P!gxSbUdFr1)pA23nLcfeZ@<$f-a@6oMQCrq=8FnYkN@kwnQ3qPgFK)w z5Ktn4PdiX3{$OQlZ~@dnK4ZYwc`TR}kw{_-DB$@$9Sz3= zxR#26w7CCCk_u?*vDm`&#O1iI>G&y4-X0x5L;z)C3GLx5U+blB>>|9VIP_lqwFL9#j@9D7!q52!)Q#d9%oI-u4 zZ>3J_{pwd-n_y!Q9@c?z$pQ~hoB`h#0hDF7sPAMjbi!OS4}tR_VBLpZHJb=^he56( z*FF2m2?PV;{MqKP;9RBXqH#n9#ziHpGE|gL-vIYJLPu~oi>1x1z^sGXAH+iwKhy;ivZkJX2 z)r>5G4}%Inh05`F9E27|_;3EI#R^AnzIktj@1C|SE-pq2+$I9l3Vd$D(3oYd;l+-N zv&)!l?^z=xLtR2=63+&aVp4-3m?&bK8||H+=9ll`t}YxvW5F{0#b577IY_+{;47Y5 z@+FmK{(iG`s&B#B1GLiOU_HQh^nNof+8*0a&$t#qt4<#f#uwLZxOohPv9Dy;>Yc+_n2Fw;}$ zLl{3SjJ5krtgJosM4oE!Vg_I&qb)>XD&W{i3#L)dW<8Nf3TVae=a0RXqk|(~M=utN zBPxZyBd&0-J2IF}U~;NyVImWmF<265GXm-^z^LxTRPWMSGBbeTLa6}2Gi{G_T-hV< zFGbHdn{rAF8ZVt^U>LXsXB?f104NKf)_*Fh8qn9|GME|^j|Pxbh?y1uC^RX)Y5wT; zc#`J3lUSD5#Ys3|2WMdQ7lw3wV&Zy2=xuZK4$U25n1y5QYD0}L&13W*(gnPpq#>ju z3u^ilPV(LFc?SPNX;SmPYE_XN0ALL=w~e1byV6PBh!plbWi@&%ltF!8aQ_`Or+nhKG+I`QrE=s&F9t zd(^bKX?DdPi~6{ zjF4~trRAiovqT$Wom!M>yx;ziwzY$B1 zsYx5QisVgLi;$Xng$zp4;#mvTDbWO_FIXPUdfu&2=#+(pJ;sD@iJhONdS5%x1EaXc z%sFG3lYjLmJ&TEdY7J@_zK&1pP*VqVQkkN&SS@%P{T>*d0~u(0W65d!0Etb3qh29Trd0J)w+_e) zsW-W9j+*H|rx+D*;*Q?a@z2yWtmnIOm^y8DHgY}dU`hA936VO}h}0LIS)6`bLO^JH zyEzeTUzogY1{$EvxVVA^ zRA`|9b?~Hm5|(6BYvVmeKZy>D{8h_F1lo<`7sGKUSPa{VA~(f&2&C%JRY+i@;<$u> zz;(6e7KocJP0kjpU!bp6-`Jj=6qo#nHhDNrnTHad-Ho5#(76-y)Q^k%i%B!?Chh&72`I5nTBDe%L}$x)GLsRcjG zkB%V*_21LfvlV3&p9lS2*-+e1%gNYlonO<>R&IddC2cZ0$`4ljjPme+etH5S;50HGc&7a+}+vx&nN(ofz!ifRpV8hqH*X~ zE_QRZXzZ@-%k0L+#@tdSzRyL>0Kv6;lOTt$1l)u}ukuv|NTi!Nx&gdY2d)to88*7- z_CnP{EbQ*w<#6?4C_`ZgV;3R7`5FGR)pr`wAmv%b$T?|1Plgq!ZVNbn9n*~nd5;Rj zYjO1NyLQtqv-UQ}b{A+l@_uMl@4V`8g=XE)q_3A*Sr#{~I5d^;QYfxZOic=X<37s-^~PCZG<> z+FK5(O;zi(ZBxAEM63;wy_{`eYqK85a5ER7<~^iYozM`gaReI$wkIP)Xe|cs4TtviaRj^ z#2pbfZB@$I4@`x??Fk<+E5(bkh@b#JIUFeRQR|X2K=2Z=12-wdZM*V|0Q->gm+r}H zkkiM@KO9Bt-p8;YnIN+}#QE_}8@(Y%6i|l%gM0e+5nt}x%gXH1krw|UM!~8~K9>oO zOGIj;=JEtQ=%Eop4aXz5KlV5-)H3FnI=Tk}seSj-Pfk1Vp&%?GYB6EUl z@7qo%AD`#rwe}zNY*In}+%4aW>~bZuvHw~pj)#b7>GfbjmwQ1R?%W`RIn9bqE@0Sr zC@``vmVJX)@CcaM9FoTdmvP;w6v&Obs5#WvCF_EN;;(=7k{%GX$^2(f#<0@ChVUCe zf-B1x+8j+3eM#!8&~*Lz0eVaUeXh2;0YH~7Ro|b9>O>oMKnZd*i741;jrhLs&f2|W zk=@id;}ha%P)U9JH4)`cJH$NKH#GGr0K>!P?$tFiU7hzGj*K0puDe z{69-fMNG)17)>YE{%20xxY!@h#6gneoblUNY(OheS{T9Eqg|c>eZx2Rrah@tjR*{< zicZ?`wAxD6T6t@Ydp=4*b1z8sJrpyuVM~e$1RG%d3U06a3c&$v=A*cin51mxyrhnY zg;9bvJ{^d-9B(>CS{>^X;crhTECn9M>2e_0W@-lOrdP&joZhDKG!T|Hy4F?|+fPXH z&q2mC?hqm3wa`iD-^l33MiNirbWxEL%IW#sI4JKxtbVA5zMmJolFGQ3^i%@^&MT*U z^V@<_|K>3ig&C=6c{iIwRUvrAHb3@PGD2&z`PXj~AP~3i!~#=#LChGtH0QCfz$`G7 zyd#6(UoSH_rw3Tw3PS(@Ftb7mNa%n(NEXf?(zg*nLqgYCwW$T12D-j*3jD=@7Dfc^YN@7*u>Hv?Qm8{w4i?tKO0|kj_Dt+z|30(5 zTBitnZE}!|xX71)8+tz=l|`Ds*2}9R-h zI1?a42yE?%6!J?B8URzij2a1i>Y`E30KwkNv`4^&48?-{6ydB#BGm=zJ$-$WA2t<^ zVik8n=>i4$ew^ecFjaM5S-{Ki0;)%KIBVT84J=lTu_5#FniNsf?kUtEj>W#;ikbfi z74Q!++j$$^tnr2bsHrOXJ+&?0RvB>8r~X<(a{Zw}JGoH+maWJ58axi002^pAa`vV| zbbpX|aZCz4$nZeSy_$D6r|peFCs$yf8r1Ma_?lvM@!h;<+m^dTk+ooRDQgf9=`0O{ zuo(*Udbhlkx}#Y>-3_Ew$Yq%*M4R}jA_oX(MGSKOt$~%y58C8f&hSU$El}_yWl)&H z27MzKkp3`eFF#Mt?lef^T_yKx7+lk8(3BVTXZ}0b=jLDL4E^@HuU+{Ry*LO=*uE(m z271r#7A+P=-BINhO_@PKncH&+EBn)oLMU$Z6DE^VV&*V|O3>cFJe-?lkvxF4Ni;FO zRMkX92W4zYdwigREJpaK&eEVS6zXL#TcoxJK?5QY;Cv^R~n?wzN3H=^B{{pN>hNwd*rBG=16E0?1TTDEMoMJ+HlV^trAaJVRCO{~ziZ&3~I+O-V~eI|gs8+#SvTM(gg|JCLNy z?LS$B2s)U}Vf~|TPUuekS+iZDN|28VLctPfag^)LsdZa4iv$G@&FPD|hRtgXHl^8Y zwg~O@NGhARxZ%}J3ygyPo=LIo=l^(OLot~Bsum9gWXPpweq>UVM;C2heFHluXmD(L zU+G;ZGY+$5B(3nyXp&Ddq#4_;(at^LmhJvTiqx4qI81F~x-d_MNP8%*`|+PDO&JioIURKowv8_+lnPA`ya)ZYgT&&-mNbIAP@AjWO82nJ2234k>Y@YhK&u~Q0%0LhIz}g_JH3)0Rg03S99oR=LqoFb z-)zrv^Jr4KH%V%Eh!c{NO-)SP5lUS=C7i$7i~Jm4FDfV_Oc6fsWZ2YHl6nUYmG@~2 zrH{zO3HJ21_}b$g&9P#vg(mSYD@oQKN2CTM>>V$9hrw)b$Y8J*;-3k$@(WwK|pa)M1sYvkW7dF zHvwH;KT_0J>n!4;zU+{!w0S=PsK0kai^*(}j(X`O5}t|K+r^}m_pe@ecVExF_WsvW z8TfKQt7-lKl((?6)$_Z`d$~V_*UiRr;Im1uA{D!5i*RzmchaGUV>1nDF4reIAZ1&jX6XH}IH3+vsp z8yg!&_A(DRsGEu>MF#QW9?-U?%e6|xn-r;izH+eRHxUQY40)kI;T~Ozr*na`P$b@1 zpd8vyje~B{Qh{hl;QdT3Q2Zb>T{Kh@po6z>VXM{$9?NO}d%)&>wYyBD9306~-`r&+OIfUvmrt{F=FxQiFga z(v=zkXne~c{-;$~!>=#5%5jMWFlqkI)GP51D=WnDL`|>J#se~@HhO)|=v%^%GXU&M zkrO^E=iQ9m!x;aQ7~2q$-AN_g0!*mu$0bqoap2P@PTC)bq#UF1=~WW#h^kvM(P^5G zyEaD!ZmI>GYay(gLJ?!ttlc93|HDb^+?3IxWeLellA)pnQU}OEkdU3dKw^*~qPKDN z66;K;Y;D(0GPZ9y$tU4|mY-K#`fr?Hf$8$HxZr&3b|5tRC)kHxd3r}oAc6w;_2zG@n zW?+gx9^-=SxW>aqCt zLi|Si02roCU;G?4Almy4agdD-43ic{1?`8Qf*D{!E+Pe>0iQ>&b^T{}r1h|_QKq)l zKT)S8#q?6SwVI@U0EU|5Wa|8faA_!x8LO6L(4U()L^YUyDKyA&fbX!b@ZLK)1$Cj& zb6iD)7`I<<&fmUbYL{!xjO?L)T7o|J^g~svZH^AyICTG(3|hxw%a+XT+!`${1c@R* zb!nS}gGV0$_U^+UVI%Xf8sO;? z;^xq@1zMD#%^NBGm$ftWyDcc($QcdZc_CA7>JoZGi(8k2Z96cEegTFJPZJ)R?DIhY zAZP&RcpC-*eLmH^?U}c4za0Q8`<;PWJo)bk039AROAa|s(FaxoZnLQ$ynH5IrJ`V& zB19B(Z5X_q#_p)Ld#($=PKq*UOM;C>EBc3jBU%m|jAl?!Ygy<0sk@m9VqQGVd2v~? z%?D+Cr(pUEOimtm*Ct~E#(&QW3Lrj!288L>Uv(EXF@ICg=BI3UwJ))?YeIVIyzHafsD-;g2Q4QgzehO;Df zplPM}D=7hs4`QB$1Nr5jwyOa#`aqKob8<--AX5tsiY@w_2elDsJQ7@%8_q1x3DG^|`C8Ij_IAx?uF3A%GZy4kKweO3 z3P=!;-UO5qRH{_zok$J66FR|0FVdweNDaN$M5)rHgx(Y(KqvyC1xWVc{q4P5{X_woz^%P#+(G4~@1X{G&(!jPcf{xQU$HTL6B;A099! z53vmZzx=4Jrf^$Ja6G?9R0-I}czvH%%>C7#n<~qMY-ySOu>d$NfILhY>TXRJm`;;? zRbF1lx)%_%4tffM!9;G+1+c5}XUtEqun1a#7#J8po+YD3osqe|>R9@Kq@i|po&3?R zFa@3E$_uMvtn+8CE2|GPGI{H=jPHwP8)3AN&$uH0LDpVP&RABW6CE(rjCwuY)fDGTBzrCR9s}J_8(O$sUmhV1{GWuEU ziCha|%X+v5_UvS5@?p8_{1#00TUMTo64RAwf35ao! zg;mhpKmc7wnuQ1=JAo*sQvuG;f}j}6Lu%hz3A};u+?&vFyQiel2dTdiq~YNR^D#=8WlAf@idi5)W9F` zaj@Oi0rF0yKLjG(^MN=Mum+8;ueR3xw>O)8IcHtD-u@>c6A zDc7qmgWO7iTM^Frq{9@szb}Cx5H5&csv@$z>hR<8Z41}5e&Je*n3OP^6Vu#mp{v$6 zq{;75NK=4508Z~;@B`G1^|nNT8$ZU?Nn6gR1lr(J%fOrqS_zYLwO#$;T6htdXRU%D ztzWgJGI-Q~@pzR+z=u#Sp-}ZSrMN}Wz#Whkm(2dVRGn>P+;XpQfkdfEBx|$z3d9fz zmKeeY`$ze^h^zkf*vzd;L0jPHhd*$6`G|Nt`7eOX`Y4T}rvwM2a!^PhEiSYv_INKG zwUJ~4cAB6dN?SMdl~$eG#Ym2kYA4nB%_$A1{ue1(k0f#m;4L$lg48eD8x9%EHA5Jh%orjxOj~LbY;MM=N#V^a^IKc`sMqu z#N%Lds_f~S-2wBvA6Z$>a<9MO z>R&aoqvl=TM`Vd%iul#(&n-;hznk(_ER7O`fve;?S?FgoxR^!42a@U{-@u9sR(8t} z$ii2!_qJmPFP^`R!iPNxDhiRyQ@W@>$LwrqbaN0x+F(ku0YpCuKD(^d@u_-QhGwjyEMMb1 z2hGshI*-}gc)^urW*lb)A9*7ATRJ#Q@Lr?az;Zxo&yicagjf#8JeBR|-<(#dM9#>o zN!qz}ogF-ZyRBsX9~RGjm*6a_6~zS7^UT_Vx3Z_Z4o8-~v$B45FZ)8@8iX4b z-G5d-U+nJlBu_A4<;(dLv*TQ7?aPPR8>~1xS2ave>Ro!z_uqhMQ3$G9OMXG-$-SH8 zlC#>APWKqH`&J^Q>g&s@tMd36uS1X1($cdvHHRw-szxycYmxe2^jZR`cbS+?!`-$Ihp^m@a|m;MXtO;iwa{WLNP?= zl&VOR5`=F%+{UWHJl|c+U?(yr>ZJV#kRkl8YI&=_fx}hM+|^(-8YoCT(`HC}gDgL( z3@#`ggLqUrKW@WlCQldDY-qmBu=e}+xB=>Lo*pVwUG4tLcjE}Phv$d^?*%~p2x!^! zJj^5FQ6T~kJo+ERT25cRMgarG4=ipO)6f#qL5OzIf8{%$OjnbaS=4o7Y|&Q(UGYM(q>6eKbhK zFaIv*t72<4GZe4>bbQ24keqOWbozc2M|o(`0_Az|K%-!S_OB;qt} zKO0R&)pi?fkjY^s>Lnx={}|SndE)Zf(~zPG?x}?-<+WwgLI$G8erdhCKSL$0hPStM z&-=6#@7yH#xa>4GYSm6iHF$f~Nto7X$$Qsx__*K&a~b$~&8>h30&R7CA;I#X9Lth8C? zg+IXklJcL`p^w`OefyeMd}0yB#{#X^hD^3k@|sZ!bu2$6%)~2K@QeD`2cXq)%73jmAT64-6~o zY|8XBlDYEDomd7;`PH_SBrH}yEzq0{p`PViFn*BoiMhwTF_uQY16uM8djv>#E=R(;seD82#{FypI`vGC%Iab@L0xS$1@ z8c}iO>|w-mQN8FM*DYv|G1(2<9ZZ~XWR1Sd<|-cB{lvrni_ao^mEmT(l4LoZ)~bz< z5$a2hl!GIG+4be@ZB?alGyW(t<~?3qb=6^i)|RaEV79%zirMK)S6+-W+Fxz*q!rRK zb7$~P@3q80&n%CQD>F^8Hc#1^=R5JZUqB{cV0o;rq3z#73oe$gZ4WPy_Sr&X^{nzr zD$oRBiZJsAtbx=cH}7EJ7BTYuhd2 zOVT*mE@xN&Z!*FKQ8vuf+y?!V!G=uAE%70Qyx}l-U3!nAKw!35XG~jGw=5~DJ)Z68 zG;Ave-Q)i%>WMfqS=q6D%~|M}Z}!~{oAbj@ykP$87=0ft-C;h5o;x&*gN+{UY7J(7 ztCh|zl6Q2doma58K`wp`gKwfK$Yc^mJfE1|O{srJgBp7FqLhV|G%56?0^#9cA9pmJ zDUftv*R7`#e8L9hd#Em^7|@p9AGNIV=)M?``6wso2Z?-9P-77Hx>m;%_1(gDq&F4M zyoF?-@7n5uk1KcIi8uW5p*bo$GK_M}joDxlTK`w{^7ps}Zb3SmfG8kUcF1f9J)N~_ z;XrPy_Yg$-cdX5vnnqv!c98_6a=y8+Cp^sBnWMAu283Ag88PZ@plg3EXv2W@5aE<_ zM+%*$^}XUbJ?Y2%q$A|u{U67|P1T9lPh@_&IE!C)G`)7Mp*3;2d{Nts+p3Ac!ITuY zDGc*Wh&J;w;Jf{M*&aDqQcL4HNm-}YJD zu%uGh7D{n>WFB>NkXCiyD2N?6b)G@RlQ%kA2BvK8#+tXo`mO{`EZ*Z;tPK8{@>8^K zF(HA!Jf$HlbJ=Pr3{K!d%&e#o(mGb;-fp7=_7i;sw=xeVo}9*+KNKXn`}!$Jfj|;r z6C$apZ_f`khb0sDwI2#%90V$W#1VK?jhobs=ZX2OZC1f3}bDrg?|(bmJ)P zl9!_#Ci1^GlZa81DsrMOE3O?O8~`iP4T+gIL5TNwuM8_1_z_feFATxa*6;>uMThC_0lv`G+O=my8j$X+rK7=#nGz}YgPT#Dfky%+-)4UmuRQUZ|Q6kAJKRT>Fy~TL25?WBbw|D(COlj*I z{(VHCjnXjRngrn>o(-m&w>#1{k=ODf!?E+dN$O|DhJ+D?-{bH0N8L~3nN77#)t%*K zsymo6;fqXerUvpU@B}TDjl4ZU2vbqa-#;!biR0^Fh`HG$!mnl4Qn*t2&+*1Y-bM~= zZp@IH+zf3u(RpH9KaaDNl0)T%V^$!+d{NG%ELC%AnAtnDkhj=8Dtvh(@Wqfu(_+vV z@sEp2@MO4JlR)a`b4g(Yv&oT#XvT2=H&G3_n33$4K-PBLI@vj~3}qM&wE?*W5YnW4 zCKT8)+g)`Wc_p0IYQwm1j9L8Q5A9VMs9hW2kC4sbTG!X^s6;F#9KQ44gDeuuCgK*o z%^&NtWMRsA+0f(fIF{%W6W(~=FR19Rx6Nz#_E>a1Qu%uY1QmyiaAZ(!Sq$nAhE2KG zeWA}B$femU?yDV<4XQfZc!8aiT|zg6EjobQTKoccwjF#d^e8zlsftY&TbouZtUC)G z)iT!6*f1F^xEpd`Hyn-EMf;+w5ROX-7We{7WiqXteOII)POj9HUanV`R76^<%;5hX z)K(DZ_WmekXoK}$6;v8k>-O?H7qv?b#IIpR6mvv9jW{&657Y;8eKaVL$wx&XIPr<1?9lvnd~#%cCX2`lJLIv$L=LhTLPO1=+5u|;2bSRAQ+6rI)I%vgI!s}PJXh7wDfTTWkHAVm` zYmmR{ejaTTx+l}<$9dxRaVvZ-s@_U)mSAceH`uH$7P{5<^V!iu(LzjV6 zmAtg3W@X=%cmt=~@uIhM6nMP`gPV=Ha!UjpZ0YdR_A+8vx-C`3i52{p0D-NWnz&z) zF*Ce+Sz>I{lUuXsE~ip0l}>Mkhg9agD$q-SYH4g%ZhR>K9rbl^+MAZ^5uu1eI^F9X zD0jM-m3F=%DtPa)_hC9|Gjd^TMU;VKC`?t^Qoe38J@#mXc~GRAJf*Z+RG#2}MtmG1!94 zbuw-<>FRQMN;!v)>cS3xwE;x0g8 z17Nz+TzA`PJ;y7wFdukj%a2xm zD%PpXe}5hmxukL5|{6PnP2@8Xc30-V zu3pA$hlhVZo14o%w5toa1Zu3akJp|1&bN_J#(eZV0QF^9ZLKQWp-jSfLznM9xY{t` zXEelZ1|qp>O_56O6%iKAJQ(_6Y|^2o2On@$o7#U^7uUi~nK!a?q}M`kSmtaN!!bN3 zT~Y;Ef_?J_eQkaiAJ9BdT*{){l9nluQrD6j$YC&A*qu4T2=MJ5zf#!2&u0M;#O@bu zj$qZUH`Q&^Q|Lh_&5sKV6m1;9VqDTg?P*;q=|U~8UYU~yh0I3M*_Y^1iP`!;yH=Q~ zrm@E{>232QQjqRIDvxm1yiQNstW8^~3ofY_zk77_k@+ChLV21l>b>~{Fl8XuJBaV@ zP~(BtoxS4FkK;-My`?NF{Z1P6{lDyjx|%+R(xN#cK8{SjnwxZl7gEZ-Ks^|v*!~k8 z>_+hXybTrm zrJAxg)kYG>!t4Q+wQ|g7nX=SAtQNmWIuGJ=H%%LAP9rOhDvI^bgK)CtIFu*r4rKN< zvx_DedDW0fRj5_C=;It`*GR%G)-%GBi~R zH|58s27XJY8GG&BJ77cloiS`nzbC`2o^#qsuAplh%$8haL`vftO+yE+CJPexdL|J( zAdvnGZli304Tm(NA2m^MutV)Q0u3iwSzJX8g9jRPw95Uj5JAuQKa9K!NKP+jfpPV-L)$? zS!xgn`9*hOJ*ycz#`sZ`AxXM0KALQz~vCG_IZ!T)ZB2(O~VmSP7U^Eyr_ zbi!1e4ZXS4Q`DiNmXehMVk3WwJ=t68y&$`PWWalIq>f3>LfDny#X=UuT1D`m8F|T? zvX-p{sfM6)6Img3tI3ISuAC`ZNb<>;s|>R4-5a7{@V1p}o9F8Ibo{QDt%WctLTT}6 zv)@%1qb(NQ#o; zf>&-~P}cx4en;Dc?7x>5AK{n9uUr*qVNXAqjDM3o*c1`|2*abbG2VDO@g(3uUjL8+ z*KQgQOG5p-s*15GOn39Tqmml=#0oux@W&@RV9STNoU`DAtsf5jeNG}!pz-*{)?;eO zX$||W^^ToGQqST^pwQB(1me$P@DT-?Ax@EqE<_{NvP6Tj_|Ye6J&u;~sW(}I)|Y5F zctp{E1W81gx!>{>5Q2MvAi>a*k5Y(3o)zbW#5i@0;mGe&h2&^_7u3O3;~a`OOT~tA zp$-OkbAl~Nu?vS+!Zcfh&YbFiyz@)-w1=wDyy=7=d5mcvy;mukYb~wk;Rn|GoU1l& z-A^P|eyh^A)zkg}bYz>^YBrpm=|OG2XXWs1`XaXHw)_5 zbl!4_dG|JPtOgdwMZ`5^--@qx#zy7G_pZAVxB83a2LWEzROE5eO^-~nfg{q_05h;d zXu|;v;5mDKzeZjLc&f;2NB9arKV}YXt~t}k2T28x2w}2K)2*4bhHk-k!v^07A7IPymn|5Nq*e(;U0R^IPd*leh)}rqo z%wc)N%tSIShf!OluinfLFpR)rRc-;E8&Nr@ABSOCjQ1Q%D}pwph7pIiSU2rm2{=~b z7t?whg~9CR(F!qdcXg;V6C;aegBXWqHZy7uatkP7@vai;&!j6i$B>igHIdc-0u{K< z6}PDEO@lG7fH$Pinhc~1jVVe1g((eF;{ul!7D;)o;+(kdLR(^QMNlXrP~Tsq z69N>2>9hoJ?CQF^pr!7}YkICP(;WtPjeI;7vW6X{1XYr3Hhh)6Ib*V%SBL2G!~?sk zI7_qoRy-U7@`srl=F(!es9di6?*Ij-o0*TTKt@pnV<;Qg7^mHI@6V|Li_XIoI*$|U z`O)Vw`Pxk{d3y4GiUqb1KkxcJx=j^U9d%McTJa?PLIil6yMB{W@t&asM^;v&^IhAA_nhpS-Oro&lWk#ro;8!w zYGUFg1Ok$^#%;|zfH+gwtamR%P@0eiH6CWHCX<0+V{P$2YLo7@<@ZiaTaGJ?Gb#R{ zCLW)3&gnj8Fx5n~_NsizZP}R#*)JvC{>lks==LDe(d3VF0HENTO9&yLRKd>C$vLVn zsKv7Q3_+TN5hP7CsH2H(nvnYXL=dR*TGgjWvcc@6sd8~D8%E6b59gSVH|H$j_VsPoI^^KL-fH?Z`JWN1e?99;&1=AKe^0j3-%`=W{k2qYZ_ z)b#{~sI%qtQ&Upvw$Q8E!mt}gTpK0-oLw31^}erGF<9h$6h|2Z`j7>A0b~MIT|kxG zo$Wsjefdq`pLmJ@qx+((i|;|c7sY!)pg;F6Y*oyFJLM$l=>L_1|Iyt4L(>00YKWX+{?PuM6D{PN zN)ftFp}5h$o1|Sbla&zR8Ab=|am*mlss~}VfoeD)@hpdKyd^}+e`CjEUx)YZ`8PY< z1bO?eZk;~|IKTb5|1mQSHy&*iu?}x~IWQO%F8KE@IlJB_kkS$){!+f;sg6T7Tw z->*v+C>_jj$TB3ODqAT9}4=rKVr0IaWqA*nv%Sb%tY%KAdA_ zgDD`{{U-_3+8pk$pQ5a)a8!m ztkK~jYBfzq3w=5#@6*yo62Y|L;}1-H<{DLjT^TZKj zhlKCz*wiL&#$#1aPJo)H15;=CSJ)wH{RG0m)OwXDc}fmH-@t%+mg9C?MR~D-ye-#| z!xrIk{L07djGU6&R; zNatBoCGQx_rZX@Yscbfu^6HDe{3_?Lo;haR^N6bP6+qc3`4asM^T{bI!ewv*^dscF z`zSOPou0)wQXbH2!Cpu#Psr{Nqbfd}|7_P5&Nrl?7RN##uWez`N}>X|z&p~)G17_= zQY$;~CEZPqOvfMt>GLV&%>31vB)5xRcZnxtr_+Aslc&Y^y-m07 z@eXnXCa`oN6Afpd%cWSDWe)ry8E`WgL`EowrSj~vH7-`ZzrHU$`hC~*`+<+)?<{+_ zrC9F7Tc4KexCRajtccu#B{)loE*I9p6~0vA%H=(C!jI*4cZm|FlSbd8G2(}%DA_tH z8Fd+-v=q`_As_;j{Sp%18uEa|=YQ%__SVRhdH|kI8__-8*0%VpU39D1zV__%f%%0N zb#lR&eh_9TUvJunt&Y6~hM|LT$#)8@^bDiYvKZH=4#F)yHl6hL#lL>(1d%cHb2@no z=Qikrne_|}7V^ea$$PFmb1oUfQb#J^*Y+{XDt4WRcSRG;179mYOzDFa?oHR430N1V zc8{m_%mb=9L<~thRNS>zlAhKQ5U}MW6e=xvGY7c4fMvW&KYPZR8JGN(lO-)B<#(7s zFA49hic0QDakU4{;>mtVAXmTCf9B1}qK)4iSbJ`(E1x9t3p?p{6)Jd=oy9PkvYOh|g7tDYo$J^Arb{u$n5P?Mf9+vcSCt zx4C?QA~ZcCSS6wOi}nS8pep z-7rTUs4>wEmvY39p5vzH9n-w>Grhe%I{db0np^8L`5RF+iEowUzm!)=q}XYNer7gk z8QOEKo49)*z=sero~`}BXp+fXQ9J%XN@C+TFxb>Ui9TLCaU+vaiW~fPr}d-5;udum zbIW5fuhKoIhsIpsDtY7z^>qGOp#Q61rnLp1RU^_>snz1{0pVK8kF!f&z=zbc@5Jxc zgrx^;>xVv>ASv3sYC+j#gdd;%v`yArq(Fj<$-k}iDc zkPM&`0>#Y?GnplceSL+LV-9O`?BGy~di=cX@pe?MxBs!B|MAqGa9V`uPhXxCy9f^B z8d3HWr)JWhn*L!$oH3w(z_glhH7e#i@ql8O9GiXGsK_nn=;QA43!i})n+wp)^+Set z(mMAE@#Y;=$!A#TqbCqo28UtkjtBK+^A`YuG9(AFH5hzh5==cPq2pEWU-a$?gLP}z zc56bXJavl=%~Y)>7rvf&q#EYtpT<(h<;Wxr3-E7P65br}&+^>a0*}@QkvgXvfc?f} zWW;D|rUL~U^wsnpn#Gy{9PuVPm7!jF+2T774kzgUk~mlpbw3_cu z`m9Gxs`3LMKul-LJUsmPJh;EPjCcbuiW+Yu_peO&MZUJTA`akW{asq$0OI<-RT zUGl{spbnQc;qvVE`IqMPF+Q{K>6d2kWISVv72kyhR@Ms*d?#vC!myJaLIcTI_szXuo5$?~m4KF)a;X7~ z4FFT!lUGCE0R%7N3Zf4GUOOUXOKWpcL6(2am+n$URHgd+b zxPWPT!4jTN*>S5S9Uz`BlQ8q$0e726%a8+=llMMO22gy`XKN+oi6ix?PXjkv!5BD* zd^J10oW6MJBTiqPOgC&zXQIxG4En1quux*$8L?~?#)@~f7ZI2(x&aId2LAi0*YAiI zVM16pQmP1F+x_qW-2Gf@m@gbz7jDO2k?BP!j6|QhlVT@$U4Z~fP-QR8Se3Ao0g7mO z5))jwXY=Uc(V?N{f~&K$2yfS#j?wwR?4f8_U*i4@1^{1(NY~q>{R*;KTv`t$phvhZ zCB?f*zGY`~S|;*I30Y;Uu8?2onvTEYPg=7F*LCNVyT&V|e+O8%oRnQTTx8*CF{XF0VLykhOIax%VZkkYf zWbxvO@27l?2xZcj({bcVQ|9d0D`IyC%I#_0xaRWk@AY(|OuFC%{Va$pn**^}VhBD3xl;gcfN2krzq`bEOqgO21R9c|qEEgqM}?qcg~Y&qPj;3E;J#A%0;#W6 zuzz&_JW<0|3$hPcj6rw;VhOx6^^_c|`31(M3G&}*4p;tO=+Bg7ACC04dFD0tw_}&c z8jNc`C)2$f=G{B-ET?wzF11Tlx?`$2a0T4YHP6yWzEcEqB}lb``UE408Wsd^B7nnD zoXb*D-?8e`kecMFWQ+=&IPq;S?fTI^E8YY<=GSX}V-D=G6um!=%&%teH(}0V@LZI} zo=t>ZWpm)Ozdp73qo@cFJp%yD0an@<>%HC|Q{3m+ zH32+bzg4yG>hpW6N{mv+Jt?0^E#0k7SdOGKt3A-vcv8;`9%Ybd)(PEG`Km3(#y$P3 z`Io4kQrnZZPf8+cY-AUGDhL`zHCGxxTzhTbAOOCUv$-y0E-3)^aF{#6cJ}PYH~LBBjKybR8mkUq|=Jg2efMhI}lr-j_`Dq{@#Hzj!HoB?>OE zs-u)U_!8ZhIpp>f@c5>GoQ!j7kpCUeJkfA7sRvdUezr@|W%v?O*|C>q02!Xb7t zZt!W|Ys6{UbJT8q)|k1tjdDGHIE=| z{rVF4o@t9y6dgWas{{p~!ub_G8`i$jspPl@c(f_V#1gQS6GXRCmV^en8>@^2)eW|SI{)Uzq znu~6|*$S-M6h#TZiPN`SroOS$wphXN0w9VZtEJ=w?!DFFpJ-mN1v=0L@pV`kuui@< zU-Z=$(_JnuC%?T}yX}OH11C=8o_@I{06aM Date: Wed, 2 Feb 2022 16:12:09 +0100 Subject: [PATCH 075/132] Fix README --- .../data-solutions/dp-foundation/README.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 12a88631..1eefa6b2 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -2,16 +2,16 @@ This module implements an opinionated Data Platform (DP) Architecture that creates and setup projects (and related resources) to be used to create your DP. -The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Cloud Storage Buckets, BigQuery datasets, etc.), and then allow easy customizations to complete the implementation of the intended design. +The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Cloud Storage Buckets, BigQuery datasets, etc.) and then allow easy customizations to complete the implementation of the intended design. The following diagram is a high-level reference of the resources created and managed here: ![Data Platform architecture overview](./images/overview_diagram.png "Data Platform architecture overview") -A demo pipeline is also part of this example: it can be built and run on top of the foundational infrastructure to quickly verify or test the setup. +A demo pipeline is also part of this example: it can be built and run on top of the foundational infrastructure to verify or test the setup quickly. ## Design overview and choices -Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. +Despite its simplicity, this stage implements the basics of a design that we've seen working well for various customers. The approach adapts to different high-level requirements: - boundaries for each step @@ -19,7 +19,7 @@ The approach adapts to different high-level requirements: - least privilege principle - rely on service account impersonification -The code in this example doesn't address Organization level configuration (Organization policy, VPC-SC, centralized logs). Those are aspects that we expect to be addressed on stages external to this script. +The code in this example doesn't address Organization level configuration (Organization policy, VPC-SC, centralized logs). We expect to address those aspects on stages external to this script. ### Project structure The DP is designed to rely on several projects, one project per data stage. The stages identified are: @@ -30,19 +30,19 @@ The DP is designed to rely on several projects, one project per data stage. The - transformation - exposure -This is done to better separate different stages of the data journey and rely on project-level roles. +This separation into projects allows adhering the least-privilege principle relying on project-level roles. -The following projects will be created: -- **Landing** This project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. -- **Load** This project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization Personally Identifiable Information can be applied at this stage or in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. -- **Data Lake** Those projects are intended to store your data. It represents where data will be persisted within 3 Layers. These layers represent different stages where data is processed and progressively refined - - **L0 - Raw data** Structured Data, stored in the adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in BigQuery). +The script will create the following projects: +- **Landing** This project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery, or Cloud PubSub. Resource configured with 3-months lifecycle policy. +- **Load** This project is intended to load data from `landing` to the `data lake`. The load is made with minimal to zero transformation logic (mainly `cast`). This stage can anonymization/tokenization Personally Identifiable Information (PII). Alternatively, it can be done in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is recommended. +- **Data Lake** projects where data are stored. itìs composed of 3 layers that progressively process and define data: + - **L0 - Raw data** Structured Data, stored in the adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the images for Cloud Vision API stored in BigQuery). - **L1 - Cleansed, aggregated and standardized data** - **L2 - Curated layer** - **Playground** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers - **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. -- **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you suggest using Cloud Dataflow. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. Anonymization/tokenization Personally Identifiable Information can be applied at this stage or in the transformation stage depending on your requirements. -- **Exposure** This project is intended to host resources to expose your data. To expose BigQuery data, we strongly suggest relying on Authorized views. Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. For the porpuse of this example, no resources will be deployed on this project, please customize the exple as needed. +- **Transformation** This project is used to move data between layers of the Data Lake. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you recommend using Cloud Dataflow together with [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates). This stage can optionally be used to anonymiza/tokenize PII. +- **Exposure** This project is intended to host resources to expose your data. For BigQuery data, we strongly suggest relying on [Authorized views](https://cloud.google.com/bigquery/docs/authorized-views). Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. For the porpuse of this example, no resources will be deployed on this project, please customize the exple as needed. ### Roles We assigned roles on resources at project-level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. From f1ee12e20c33357b0ca694352c43f410e9a54e7c Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 16:13:54 +0100 Subject: [PATCH 076/132] Fix README --- .../data-solutions/dp-foundation/README.md | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 1eefa6b2..825ca6bf 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -42,34 +42,30 @@ The script will create the following projects: - **Playground** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers - **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. - **Transformation** This project is used to move data between layers of the Data Lake. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you recommend using Cloud Dataflow together with [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates). This stage can optionally be used to anonymiza/tokenize PII. -- **Exposure** This project is intended to host resources to expose your data. For BigQuery data, we strongly suggest relying on [Authorized views](https://cloud.google.com/bigquery/docs/authorized-views). Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. For the porpuse of this example, no resources will be deployed on this project, please customize the exple as needed. +- **Exposure** This project is intended to host resources to share your processed data with external systems your data. For the porpuse of this example we leace this project empty. Depending on the access pattern, data can be presented on Cloud SQL, BigQuery, or Bigtable. For BigQuery data, we strongly suggest relying on [Authorized views](https://cloud.google.com/bigquery/docs/authorized-views). ### Roles -We assigned roles on resources at project-level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. +We assign roles on resources at project level setting the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. ### Service accounts Service Account creation follows the following principles: -- Each service account perform a single task having access to the minimum number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) +- Each service account perform a single task having access to the minimum number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and the Data Lake L0 project) - Each Service Account has the least privilege on each project. #### Service Account Keys -Service Account Keys (SAK) are out of scope for this example. The example implemented relies on Service Account Impersonification avoiding the creation of SAK. - -The use of SAK within a data pipeline incurs several security risks, as these are physical credentials, matched to an automated system, that can be distributed without oversight or control. - -Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identifying a structured process to mitigate risks associated with the use of service account keys. +The use of SAK within a data pipeline incurs several security risks, as these credentials, that could be leaked without oversight or control. This example relies on Service Account Impersonation to avoid the creation of private keys. ### Groups -As default groups, we identified the following actors: -- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issues with the pipeline. The team also can impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. +We use thress groups based on the required access: +- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to troubleshoot possible issues with the pipeline. The team also can impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. - *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `playground` project. Default value: `gcp-data-analyst@DOMAIN.COM` -- *Data Security*: the project that handles security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` +- *Data Security*: the group handling security configurations related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` ### Virtual Private Cloud (VPC) design -The DP except as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) to run resources. You can configure subsets for DP resources specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. +The DP accepts as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) to run resources. You can configure subnets for DP resources specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to host your resources if your pipelines may need to reach on-premise resources. -If `network_config` variable is not configured, the script will create a VPC on each project that requires a VPC: *load*, *transformation*, and *orchestration* projects with the default configuration. +If `network_config` variable is not provided, the script will create a VPC on each project that requires a VPC: *load*, *transformation*, and *orchestration* projects with the default configuration. ### IP ranges, subnetting -To run your DP resources you need the following ranges: +To deploy your DP you need the following ranges: - Load project VPC for Cloud Dataflow workers. Range: '/24'. - Transformation VPC for Cloud Dataflow workers. Range: '/24'. - Orchestration VPC for Cloud Composer: @@ -123,7 +119,7 @@ service_encryption_keys = { We consider this step optional, it depends on customer policy and security best practices. ## Data Anonymization -We suggest the use of Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. The implementation of the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to let you implement the data loss prevention strategy. We expect you will use [Cloud Data Loss Prevention templates](https://cloud.google.com/dlp/docs/concepts-templates) in one of the following ways: +We suggest using Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. Implementing the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to implement the data loss prevention strategy. We expect you will use [Cloud Data Loss Prevention templates](https://cloud.google.com/dlp/docs/concepts-templates) in one of the following ways: - During the ingestion phase, from Dataflow - During the transformation phase, from [BigQuery](https://cloud.google.com/bigquery/docs/scan-with-dlp) or [Cloud Dataflow](https://cloud.google.com/architecture/running-automated-dataflow-pipeline-de-identify-pii-dataset) @@ -132,10 +128,10 @@ We implemented a centralized model for Cloud Data Loss Prevention resources. Tem ![Centralized Cloud Data Loss Prevention high-level diagram](./images/dlp_diagram.png "Centralized Cloud Data Loss Prevention high-level diagram") ## How to run this script -In order to bring up this example, you will need +To deploy this example on your GCP organization, you will need - a folder or organization where new projects will be created -- a billing account that will be associated to new projects +- a billing account that will be associated with the new projects The DP is meant to be executed by a Service Account (or a regular user) having this minimal set of permission: * **Org level**: @@ -172,11 +168,11 @@ For a more fine grained configuration, check variables on [`variables.tf`](./var ## Customizations ### Create Cloud Key Management keys as part of the DP -To create Cloud Key Management keys within the DP you can uncomment the Cloud Key Management resources configured in the [`06-sec-main.tf`](./06-sec-main.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. +To create Cloud Key Management keys in the DP you can uncomment the Cloud Key Management resources configured in the [`06-sec-main.tf`](./06-sec-main.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. ### Assign roles at BQ Dataset level To handle multiple groups of `data-analysts` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project-level. -To do this, you need to remove IAM binging at project-level for the `data-analysts` group and assign roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. +To do this, you need to remove IAM binging at project-level for the `data-analysts` group and give roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. ## Demo pipeline The application layer is out of scope of this script, but as a demo, it is provided with a Cloud Composer DAG to mode data from the `landing` area to the `DataLake L2` dataset. From 7704212b3cfa0529b15aeb77f06032cf6bab51f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 16:20:27 +0100 Subject: [PATCH 077/132] Fix test error --- examples/data-solutions/dp-foundation/README.md | 12 ++++++------ examples/data-solutions/dp-foundation/outputs.tf | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 825ca6bf..9a2b1762 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -196,22 +196,22 @@ Description of commands: | [composer_config](variables.tf#L17) | | object({…}) | | {…} | | [data_force_destroy](variables.tf#L40) | Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage. | bool | | false | | [groups](variables.tf#L46) | Groups. | map(string) | | {…} | -| [location_config](variables.tf#L128) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | +| [location_config](variables.tf#L136) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | | [network_config](variables.tf#L56) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | | [project_create](variables.tf#L88) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | -| [project_id](variables.tf#L97) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | -| [project_services](variables.tf#L117) | List of core services enabled on all projects. | list(string) | | […] | +| [project_id](variables.tf#L97) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | +| [project_services](variables.tf#L125) | List of core services enabled on all projects. | list(string) | | […] | ## Outputs | name | description | sensitive | |---|---|:---:| -| [VPC](outputs.tf#L61) | VPC networks. | | +| [VPC](outputs.tf#L75) | VPC networks. | | | [bigquery-datasets](outputs.tf#L17) | BigQuery datasets. | | -| [demo_commands](outputs.tf#L70) | Demo commands. | | +| [demo_commands](outputs.tf#L84) | Demo commands. | | | [gcs-buckets](outputs.tf#L28) | GCS buckets. | | | [kms_keys](outputs.tf#L42) | Cloud MKS keys. | | -| [projects](outputs.tf#L47) | GCP Projects. | | +| [projects](outputs.tf#L47) | GCP Projects informations. | | ## TODOs diff --git a/examples/data-solutions/dp-foundation/outputs.tf b/examples/data-solutions/dp-foundation/outputs.tf index 4f173ff2..ec9a15dd 100644 --- a/examples/data-solutions/dp-foundation/outputs.tf +++ b/examples/data-solutions/dp-foundation/outputs.tf @@ -45,7 +45,7 @@ output "kms_keys" { } output "projects" { - description = "GCP Projects." + description = "GCP Projects informations." value = { project_number = { dtl-0-prj = module.dtl-0-prj.number, @@ -87,7 +87,7 @@ output "demo_commands" { 01 = "gsutil -i ${module.lnd-sa-cs-0.email} cp demo/data/*.csv gs://${module.lnd-cs-0.name}" 02 = "gsutil -i ${module.orc-sa-cmp-0.email} cp demo/data/*.j* gs://${module.orc-cs-0.name}" 03 = "gsutil -i ${module.orc-sa-cmp-0.email} cp demo/*.py ${google_composer_environment.orc-cmp-0.config[0].dag_gcs_prefix}/" - 04 = "Open: ${google_composer_environment.orc-cmp-0.config.0.airflow_uri}" + 04 = "Open ${google_composer_environment.orc-cmp-0.config.0.airflow_uri} and run uploaded DAG." 05 = < Date: Wed, 2 Feb 2022 16:41:28 +0100 Subject: [PATCH 078/132] Fix test error --- tests/examples/data_solutions/dp-foundation/test_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/examples/data_solutions/dp-foundation/test_plan.py b/tests/examples/data_solutions/dp-foundation/test_plan.py index 11d4fd88..6d50117a 100644 --- a/tests/examples/data_solutions/dp-foundation/test_plan.py +++ b/tests/examples/data_solutions/dp-foundation/test_plan.py @@ -23,5 +23,5 @@ 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) == 39 + assert len(modules) == 40 assert len(resources) == 281 From e51722d6153f7f66c130a4e3d0280f66a0d97a85 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 16:51:14 +0100 Subject: [PATCH 079/132] Add datacatalog --- .../data-solutions/dp-foundation/06-common.tf | 13 +++++++------ .../data-solutions/dp-foundation/README.md | 2 +- .../dp-foundation/images/overview_diagram.png | Bin 80324 -> 71315 bytes 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/data-solutions/dp-foundation/06-common.tf b/examples/data-solutions/dp-foundation/06-common.tf index 9cfdc7a5..5c88bccb 100644 --- a/examples/data-solutions/dp-foundation/06-common.tf +++ b/examples/data-solutions/dp-foundation/06-common.tf @@ -48,6 +48,7 @@ module "cmn-prj" { iam_additive = var.project_create == null ? local.iam_cmn : {} group_iam = local.group_iam_cmn services = concat(var.project_services, [ + "datacatalog.googleapis.com", "dlp.googleapis.com", ]) } @@ -55,9 +56,9 @@ module "cmn-prj" { # Uncomment this section and assigne key links accondingly in local. variable # if you want to create KMS keys in the common projet -# module "sec-kms-0" { +# module "cmn-kms-0" { # source = "../../../modules/kms" -# project_id = module.sec-prj.project_id +# project_id = module.cmn-prj.project_id # keyring = { # name = "${var.prefix}-kr-global", # location = var.location_config.region @@ -67,9 +68,9 @@ module "cmn-prj" { # } # } -# module "sec-kms-1" { +# module "cmn-kms-1" { # source = "../../../modules/kms" -# project_id = module.sec-prj.project_id +# project_id = module.cmn-prj.project_id # keyring = { # name = "${var.prefix}-kr-mregional", # location = var.location_config.region @@ -80,9 +81,9 @@ module "cmn-prj" { # } # } -# module "sec-kms-2" { +# module "cmn-kms-2" { # source = "../../../modules/kms" -# project_id = module.sec-prj.project_id +# project_id = module.cmn-prj.project_id # keyring = { # name = "${var.prefix}-kr-regional", # location = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 9a2b1762..1e68878e 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -168,7 +168,7 @@ For a more fine grained configuration, check variables on [`variables.tf`](./var ## Customizations ### Create Cloud Key Management keys as part of the DP -To create Cloud Key Management keys in the DP you can uncomment the Cloud Key Management resources configured in the [`06-sec-main.tf`](./06-sec-main.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. +To create Cloud Key Management keys in the DP you can uncomment the Cloud Key Management resources configured in the [`06-common.tf`](./06-common.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. ### Assign roles at BQ Dataset level To handle multiple groups of `data-analysts` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project-level. diff --git a/examples/data-solutions/dp-foundation/images/overview_diagram.png b/examples/data-solutions/dp-foundation/images/overview_diagram.png index 4fe42c015a233e72a802af07b4ce53c556b32c76..b1a0f7887c8659ade3b0923d1b10bb9f4b85f9cc 100644 GIT binary patch literal 71315 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�vFdnPqmqduyul_L(!)?|xZI7FGc^Ha-n>8_gNdS`!bb6gZpCB77deDJ?CxT?ZR1d?UzK~UM-^A z?mmyhF6KraUKUX1HM!8mfMUuDR>S^@Dw6_O=&HGo@5K!eU;0?2x)v6zz<>6QO99rs z>Mwz{=eE!xYL(b<+&*i8AQjqse;nH3SfP1EaJTL=&D3^x6+P!mGZ^~iSLt^0T}LqA zi)+<~zleUkB)^>>fX+A@n8PUrT^!G5Z>B0e&ZZ}1(wk2Xerueav99&bRa>q*D@bH# znRpjP(;FBzs423-k?yiL%_6z7VC`tWc;=S_GjQW-5==_Xk=hyk7)gs>OFZFa&Kx#c z5$#w8-%uX4g3{E0S#?4Tv?@_!_8x_sd7mWi`m;nC zuFbN6IoF$EMV;d+_no0R*QN5gt&2d9+gvdHRk9$^{qaWVX1Og%;FSe>-#a#sqrfqb zV=4vhYR=QCyRAMv>6N>Sx!bk7^DUvdMWDR%?m~i@--$MnQPs?lHE~K{_$dEwS>kSY zWC@YX-6y+K_}k+Whg>GZw(*+$_5`Whfz-a#k85$%RW3O|F}jC_6l{ESmT_ScI} zL!|&;*JfcWSxFrK8E)CyOF%>Yu2;rpCO8T3ROk3Qv5`C~RI?~3xHhqiRI|47; z)!BFr>bUPr2{$5?3k{CMD2cW=;IEyz`v)iRiyW*AObchrkiou_nve9t4{G#J)7!}2 zrAfBW5nH$@>byf?kB7NE3O_hsKOP>|s%OMyit4Hnp%@+AwXWfXLXBSeeH(A`A`IT0 ziD!;3Qjq6fvaag-SUA#BS30yX<6;(N=Vl{9hqqtB@3fCCI#zXdJR6e`lMv9R(gD1R z60HU`mbeH70oV1Ba2k27`j>%V&co67(AYPFZ!ff()=c4a@j$2QUtvI|@tzq_$vtZ8 z*cNSgO|#zpc+XhpZibZm(s$YrGIut;gvvP8z{`5lo4J7GyA-_b)*EZfx?2Px`b))s zK@)MqyDZnu(P6~#yL-E|5uGZw9cL%`kG}S7(P*Eq$1U{snakif17T{q0`cH`jCMc6(%Kr7A z#rZQKy8`8W9t=Gv(e^&~Mbu4?jb~L%w*o{aSIg++xv{H@GY{gh?w17fobk9@=DIl1=Drr(;J7e3uElzEU_?GumA-d(*~}cI z&@QHkSUM2)@gi7|>b102MN=i+3Hz#_M; zjWTqzP;Y8Vik7v8G|V>fBhKFlH7?{HVC5J zyZOEeH*I*2<=zhY_J#?Z0tM^gKT}hk31Tng`rh6avG+&%?JK{90ddH|n*{P?UKak- z?FJY!6yxCOQ=*6WgMY#akAriQ1f7^Oo!?q>ll&PSulXVvEyTs?WL%6G@cKaYZ|((s z1F$Jfj1w0U=%T=*X|(&1P<~xdz!r9;1Aj3pz!LBKs{;SF{!JgH*z(_nEn5rf z+srBjg>1I}La6|QyHSU4j4|uTtOoqOND%uFxV4~fA@z1=AZQd{=aE@^G$=jyXWNSD zCyW5;Na+HkfVp|$#YX@NgYvb|H&#Xs2-^UWHXA}9_J&bEawyVDLLF#uu5tU+xL^8x(UtD6q5cl*b=eM_*zRt$y+9dmg@;srh6C zopn+75paUf5fI9R!Hf@4G>HFtDdBoqs-bsRQb*T6=*MFGT;&G{uswG)GYlxi#~vL! zieU8RUhuDhTt9a*sZFT=NqSAhuTC7Vpq-ouG+vomwK9P-_P_r({?Ax{wc9_0N5?r9 zlWzf!e>qjqA_quEdQDMeyrI-hixbLa*FpsJ-6#B+USH*+M1G?rSDH_Tc_aro@gg@N$@XSUFON?rjYO?Tl91 zNk*4Y5};FN%O^g-J}@Z>Ij(DUKul#4VfE!(B~`g^v&?KYMI49SxXtW5@KAUQbgG}_ z8_M#F03PpV@P|p&k-r0>Q<5}W=y8pWY-YyJ+Pja{11V5t*^cj*rv~}Dudp%7yRgXn zuc(yAES(5o=|v%qmIzBa=cPOY5U+rtz%c0S%osDnJD!rUK~j_;uz?^;E}ZTp5j1)s zrIuG}f`g-G3g}^o{KvTlbD=VcyLDkC+gFvB4qwTi0lOU6`c zs;A%XJ^_?8lO9YnY%DUu;Cz!;Q7Fm_&&ccPQD=``+|#P)4V-GFk3a1pj*0P&?c^wm zxz5B{HH$7VIyGh99!nR7|C#&i5PleaSE_vTFFg#YP$k=Slq+wM=djbqUj zRTchS(`_+#=JI@tqZKaD+wt(Kn5Qc%Glp7u^8|ES^Sn;+b5|Cg3@O^H^7l=$kwids z)tFWO7}G@G?nv@fzJzG|pxJ%*asaX7pF4Xb4UDri#UTn!^y>XNWd*A;!xt(M^>DxQ zSN*hJj(|R$rc_ysYi&NFCyKCa^UNpTjqZp5mgi&GzG8;bCu#_vxceL`XQDT2W}G`cIbxU60_?RHQRIEnMm(DA4o9Mj`lU*!PBOj*VF%Kd&aF z7BAdn2=1-ZV7w7`o+=SaMlkOO2#EWaZitQjT$oAEWPh^&maC+oA%2~ojkh%xiyfNc zWxQg~K%%FaAOE1TG!d4_)a>i!37|zV;c~~~`VwEWhxij$Z2|@4OK7NrcLuJYnf7cS z#-xOq5(SIp_=bV{+veLv>!Z$}ureUD-{|8izhM2$3-X)wAHz#MQ))~Jv062u)LnJ- zKElIv1kMKG6_4k{qX&$2R2~dru$mbK=LRnmD|8SK)869zh1^M%D45LW_2$cgH&Vh^ z&gDoJx+gQ6Q*=*wFr0O>?STMOC*2VrWLr7V&gp336X8EXXULdZqf;u&=6)|VuU%^D zi=|8^s_-*bkOh!zt?bE5m)G!sfMVIU)px%2@&Hn@z=n93AhgPf<^7PBI?{PUkVki{ zAfAwD_P%{PN(v7Td~+XNF03%ebfCQ+0FJf7y6FPqhk`15AV#VJ;u4kK4BJ=VftWjS6xH)g zT%=PEk+Cf|+!V{#->wT2Lf5{Oar%|rqL#mK@SPzg#eKGs$X5{v1HH|sHt;uRWA@es zQnnY@KisG+t@ipaDV5QbWiyG8uCv#@cy$`EiVTSxH3}d)i*=%F`kw)Z4!KP{5S(F( z{u{?RH!YYP<4g{4$)QD#?5M4}q8IkYn1Q7R?i=-LKsU%8*1_*cNK^)T>xFA72=}-q zA+Tu0UYBBEcWm(^fE0s=T2xTVM(4rpn;Nj!ED{f|Gj0x_l<5{}<0^zNv$pi+oE|e- zK(kxSKJok|O4f?KuP5^G1VpK}oF}Hk|M|8%JV-KNe5eq6sLn;(lWf7ygvk?4&3z+* zBUBmu_A!vwJ>q_khg@EboY-5?7rv0ICJ~XI#2XPT?>parpyv=R4z3lq^elY`%<<&ZZmLx3rNv%I zT^bQldi2(dC%)}lTFBfbnLVwX?Aba(erq@Q$UG3(zP=4W%=XNmoL{ds9-WeIkx1=J zis0oJNC99cD$mPr+KkJQwUxC-&KD%*)E!H>SI);C*X2h>Nj5;%FQyf&jI^KN7Y0%^>Ye4gK(K0DRvMq0Iu#E)%n zS*zu+VyiJWyf;zEj_k|*Ok^{FQ&%ZeVs=&6=CRR>eV)Z@2ZoI6YtA4{jQIQ>H)NfCexvb`bY1<O4`3 zR7ZKtlz@CB-yC=mn1|n5wt4RBd{rCSjnJ-mgufsGM9)($o(s=>zDPw<65$%Pjc5Sd zK`X~5RT?y<+hL-}PJ0IL*e!lh+bXlhAQM`+p2z}4yIfu>2j%euLS2c*KV{%5GwjS4 zT6%Ak{MZa`5BmCLQ#S{8TGfHIGvb${Ym?Q|<}(5v+twG(HDJb|kc`eSiwV*G{x=@0 z2G(p@nmU0|_~NmXaa>K_{Y(jnR4QyB+YXO0@jStLL~=EZ-0qe>jKn z@y)Q(#rS@FX?=q;A9J>jlnNi0|>wjp&S;D359?FQkPnZ|9I69)5_g zW=(H!n~=vJ&NJA15vZx9n*tN$%-H#h#}poX#mUt%{X`Eh{l^hM@7&_+h+CL>i!H;tS}_qggas7_8#u&o<+KcwsBqx;>F zU1rLgd5Bl^5yZ)$HQSPEw(Q_H>+1?voWhGF%g%{o4_~Mu;lr4m@v^%6%RngAo*EkN zmxMl|RKYmV?^e_Y1#ul;#%XFrS(o2sQ#_s0hkTIF9D!@%<*9N&*oDo$KP1CO@1hc& zOk&!w^UISe``EylAA~hk69yh$$UF z@LxI~oHTV!iT_GC_zO=&H z)E^tuNlME~GXLF2fGzW&>Y0;RLw#|b3v49qAS13vnZ3Ov#N0Z(jlx{)fFbzZ&QkN{ zq z-rT@#F;;*V{N~*S3|O2?ZN(!2&3N*%0hxdPE{j1vD4NM2tm7|kUp`yl$3!PaM#G2d zxk3X^Evz9gH0GxU)OjEIdHa^6oHr6ICP0QMUMXLpaIfVgCIAd#c831#8S+0_7|#AF z;$Bo@SnKzkwly1GV%U=c>UUlMd&j;2(mRT&aTU06jPb*zVi{Wxc!_WUO!qgk!Wrhb zvm}LoO_WbKL+p$sFF1g7)?;?5&xrjxG4?lEYzYiGuCcN%C-fxlRqJSeGq6cfq-Gs? z*N-{Oe~z+xstnrIk2yxxcP!y6`r0d<1?I27g3;@kU5QFg#gNLY%p#83Q4FlVd*_qc z0Ed6MZwMsAUzfU2S!|YY#Cvk(w|gPm8+iAJInsYbEtCu&9;MWD(`4-f7$5SIkz~vB zU&z2tN?7C1F7kIgCO~|YuXdvM^5DLJgxwDKWv*Lk!G_Q-D=2}JZal1fT;C7KNzRhc zgM$D)DvQv{zwf!j&}C}!Yjxm zdy#@`NdDI!*#6@yW%b}lovd;|!^`UerfwFsZH^t4k=CtD>g{W<>T2Ke?CGSU#N+(4 zDs{8sfQ;2vSRQ`-sXz}o#8fp$u~k1lb`Ro*5>NP-?gc3T*})DPlm35R(L*&mdoUB( zwIEMEfd4WFRN6sf(`;p4D^{h|t9+ZaB}ks|R0@b?z+K0+y9by&+5L))Q7HeadB#kn z829MqN*_3YMRSaH9sA_tTCqaN8(m5O=Wg;W!}u%jzwhbO7mjXs(yB(^T24mLeyP); zU3C;6=*cw}rGnLEZ{a?O`)d^c!hh8_b8-30zXPzWD_i_cczaAs(wl{2{GV$4O(&v& z#{HFBGu1c5EIn04(H@S6{H0D@IPNkl?L2nvTZBsWPnS5U(>l6AO1XF~JE|NP-9IjO0V1*YQ;W{JY_|9s zjl{ClX{Be|Zz=2!KD0cx`=#!4&kOwt3}Sx#I&qbDQni1w+!S!y^`6@qYT?n>^HEzs z>vY5(nrr_hMRA9Y z3A-mDf;nCf|2w>wVZsT#n1|4sDV&-j0Tp@8^4iS0)RMIIq>hAi~{g*Q9OQNITkJ)D2=&!}m6>g-Zi zR|}MO{Zjvz_4YG~%QDeS&qo>e@4tTn@QnY29RH^np#7QvPCVO`3hT#>A7+s(^F1LYS{C!9js8pjR$Iv;saUhtAxIyd!ed4M`0IO zIGVXd0VTS=kNi*;s*LqxR-pbrf^L2{^?n{eAoTx5^vUU*Sl80h8;a8Zefi^Gt}Rb_ z$6o7@ru)$C?|KxGxSoB}EG0;%Sf}DCp!a8i>(kB-O>uN@@jB;{v(u^rxztCD0qLQ zz{091U(Z@^f(I&W8e#&WhH67EqM4TuBz!~OrkKQXs>$_6@~AB8fAd)z?!e}QEh`7! zL(KADRC_(Lx<~!Q_1;{cSVAi{%&gYJaJoGsX2Bxd7z(b8#=~Z-P zlp4A>D3i%503S3}_yVY2t%91JEF``KTh z9+NB?bkvH=78)`Zpms{$6Yl3{3WyK6joTJu!^kA0JXpkcH&Fs3+s8M<5ROUK`HrSg zvnC{|qC3x$+poRSG&eTCbvwaA&8~R`T}(7_Dsy-fD>AM|y_d!>$96w-xVu8c*?Z!& z96afKCX-8(&Ac`=)Y7?Q7I?g!FO;GNYLn=DsU%YkoROqF1j&o6x2$d&*kN4Is0opk zhZRL0)i&d7zuvDpWkB!=a3v_n$5w<5!xdyQD$KHXzl>QiWEhnm>4Aqw%c>$2^w%UT z@>h=^PdcCSXf<3N*8isO2I?6{xz*-lU#tP3)C@D+FDgbKyxp3XaBJTl{0d)#YbKIa ztlF3}W*V2`v8f0UHG;&LKNWc>nbT?3Ta`#djJCh}h-l9k)FZs^Mg7&vZOghe1n2J-cV!`UuAAr+0_#&}TD%P{AQD2*wLA~sul_#bPklM(3WeyqzKaRWg< zR@=s(mTXC6LD@dglSu7+Y48?0gz^B+D>Ps+qFM2dGLHd9+OmE)>eMqC*;1`%Qo8dhod$ z9GscXAGbioGqTwsEX`J|G)_HY!#)?h#t$_feH0;z+Dh?R|* z0g>@#eOvBlCG~5PV&~ysnl5@3Q+cnHD4_|0X9kp|r_((Sw{3fz#9K%1hzhsTme6qK zY#Rw*-;Zk12ppx)*W2yGL*V7sK^ZOp%{uz!ar`}g&oJIOQfB`t zrB7ZK@y|vZ-jph&TJi2yGari{!Emwb$V$`de)Z=bvx=FB^rs--!rD#@toSXQ^`3)U zQQ8Pi#q4MgTPeGwQb62F4%3<{eu$>B$oZ^S1IN;%VX5`$kYNR}QfCS0a1fVoyC>?? z8%}ILjH?|CGf_%u%s`8o5YpJ(=)IRpbxN~j>7-oFdv@Vx+Fa+{Ld>ote+He0Zm+!N z*T`dvm_6xU_Q`JaC68tn4Eh)JE6Zuu5osr|?KBKe9!{z)`5cewz=2g}K-LSnR>rD& zlzZs2l%s-!^OV&y_3BS*JD%0Wr7?}AmCi;u{s>p%<;*jpInE7>Yqvd|#bjU!Chbvq zl0>L<|Gs4Qdg4XGk`qUnZ0_C4&t+UJntbtF8Rq9Xu7ppKzki=z3_;tV5Qj^Bo(=4| zcUXv)8VB=Rg!KIL_4C8{fRt za(AO~JE%4bhTH<(9)~>4n}WG`K7W*jLZoG)OzqP_{w8mm!=K2vIl64xvD3BscZ@8X zqW!dqWDA9~NsJhKvmIQunutjc7>mo{=_BcSieNJ-HqD&1X|JK>0MC=Kx*X;6z3Rin zbWRK;jgGRqH7ypj+HOIIst7)%`u%8C`%vBV&Zb2aG{MR7#LuwLSMif^u3_DSl~-Dl zEZ8Rxu~TE(GzU%$t~Om#$-~zAbHgsXn5)|)B6Vvk4Cm^xFCUPSs_SqUE@8XdYtp38 zHr8X;yNn&XIv>?KSx%BM(^GFgUG$0YW5XGlw5ie+TOZHqG$-bqE&QceC|Xg;4&<<` zxHE&6RlBF0vUUAymw6KWjvVC$gs6h3C4^j0?FBnd3=Pm#_S>e(fu7HV3|_;UJB*Z2Y}3SBCLs)}|!jVWFB zM#lCar6rY1^!$PprY9@WRoy^art%gIw4YiZ+0i-77zkw$4MwAJO|rSPcLzo@woAOL z)^!S2T3B;Pv)M$pIS*#7iIexQpcapHMqOiVjg<_r!<-|M@g zrT`&(q%BaLtXPIKIWT;%(j>R!6BcyP5mtJp$!(VPCIgyZxhACk)1Jd%kw0$Lj|9*} zv7H#P&?-`$f{+q@hodjpM6M90=2=)3OlOfSx>Tz(&sDjV|AiP}C;;1(^BJcq|16+3 zHR_1Z6Em@x%o}n2j3j~uWx$gM{%pw~Yps84mO0)O781vdj32C>IjIB=mh{auoV%Gd zop^O^p+{HsVkva%Iz%E@jFY-S*tHGj*=U_3+orouUukLvJhA6>p<`{zZo!mIGiElq!;Usx zmyTm6UB>nfhl{&%GMS2Xa6aS2x+MJ{s*T{2RMPSmOM8=ake=vfr0s5)R*>3I5Q`OGm^ccT# zN(t=eBbvCcKd0$ONU(16g29x_#_opF*-O*A9bj!IOh0ob;uCgq5PfSrlq`yC8=Mr| zF_aM-({8)onf9C^()% zNi>(1Z9rF!o^baq3YxH#-LXp2|hMiJ$WKuy#1KNym|JDRoDzxQl5gIH zQB;``qDy258S*5&PZVH2xyJONVS2A>F*Q<)hfR@Jo-r6!#0-fx=loqnO<{TpC>vqP z)KJJA`s%1fItZrY{0)9tIvO$89h(_fA<_^EH5=_TwEZb!Y74?K1mwV|@)!*$BG5|E zN#WQvw}MNC^>nu5L!Lzqta6VzE^xOu`c$sl8ndzK??V;7*P-xz87R_@^;(xGd5V$=foe%Hxjc_MASf-4}LrS4P5{ zF8;7t_RSH#7x1A$@6@a<|HRZ2->*2mL=bG&w~*6Cs#Bh(8>OLS43xi;hmm*E%sjf! z9L-a`5pQ(3;Es<7|V4Wx=>~mu6~?Xdk(Xj^Gk_y3L~HfTfk6Ogcw??)1^-lTtM&R%te> z&0LnmpuGam*jLz?uZDNSO9mUO;@*=jEU+gtYy!sCZ!)L*ku~l|9`Z*sIVmKVQCDE< zol;kb`X9><%A8gHJm-k&(Hz$*#;-LWoiEM0WN2r-?%%8Kdme6YZ)dz)@cizA#Q4?j zJ6ZZuZ(fY?C{#S^mHTeXp*@!3(JfBiYvs09i48stgnHPNo+q)#E_IfT>pLckk_ezca=U2{#{yrxwTxuzrdi~}dCbcbB-d#Lu8Ju@x z^W$AJ0WE0~$NFG<_?Bd@EB-jWQZ^_L9&T)veG{Bn#6RB|%xZ@>J@efr`5 zKSBfYy%SDy0h@(+PD_)0T&_=h`;@h~`0BM=n_@PvU01wlMIX<~plN*D>VR>1LmOn1 zllOc5ne*>+R5Pk7&GBEEGsn1a-kJaQXJ^k=zU*>1)2`ShHgd~n;PRKve*;eOO0JR8 zF<)|L!&g4bS+i$a<44+|8_vl7HeXv{Gq(U+;>GJYmAXj(E*2 zV7der`mymF^Y3y*+a|jDPrWkzoW;b$mznk672a7a?0S{EWVb%>ypz>knt~65rm26@ z`t1O`=>TT6$?i|h-icdXy#kdE-LE+*dsb$j;qA$;p=UO1Oi6vN>K=IIsbUIfEZ$%F zNQQ+$RG{JNV{JK$z4c`-2Uk9oJ#_wYKvTOA2XMDA!~Rmf#GFR|?wmRQzf^wzA`u_G z>Ac_Tl#+V0Z=l(^8_d9PPne_V%6;MJ_HdD}m~sQz?#@oMdZPgytHtJaF-mt=igkPTGc;O^Mdrmi$ye&5C) z2k*W*

%w{r>yt#+Chha<^FQ<39>ibYMAf9>+#-VnofWAD7uFYhHeCep<(Eh?gr_1)cg6~ z=U(gk=Ud-evu4iOXZJq)cU`~O6aGO_3iBDsGXMZEWuzrk000pl01(JfVc&ox@3%16 z4}!Cpj2bE`>cX1RD(sfnMN-Q})xq4w-Pp+tu&{TqGh=o(buu%vceZqJIY#IZ0)Ur* zjD)C~NBY6iO#EvZua{S&;%4uywHf60tp&?13JRr0pE3OqBaieuvI6L5@G#jyylbzoxQejz_vceHz?vIH_S*RalrC7!s!QP&h)H2x zC-f`Jmf{yl*XhA)BkLCi<3uBjzdyjDj_5oU`RUBe@Gs-ejmH^VCz~ae!w(HhkJ$Ig z;6%EC?IH#QW0AT*;h*5TxM{aHQ?mD%5_oTk{E|8!J}E~n zKNL=+vuscGS?~_o=x+r1Cmio9YJQi`Xk754{)CcJ|N1RRUbBKGP7j*5SfiI~0!u{XYLp~Ks1*;^6zpO^w3t8BMQ|zOAqE;_&0t8U7TBaB zi||ITHx`m7Mg5y$=Nk9K1dZ2+rAqE!;dr7Q1*(`n$iBnm#K-9I0sU-gxA!@oVeZIc z_5e9_(N6@Lch`>Um`Ex+-}vy5r5I}Ih4;#VY6@(adS(ym`;4keYxCOLhL6vmbvsA! zW`=pN6JtqnD(vssI@nqo%~|+8vvJX%an+x6?O`M8u400GV(MYLB!88l(!+*T((|L0 z4`9QHYITY7zb4Bs>VRGo({R*0<1Ubmlt`?ojOlS z%G?t;!2~#726_A_B$W9h{Ei0Uv8eCCh)p~7l`$szNz*99a2X3wAXRx zu-JNzWJvyZzuMW%u!+png?}>LV5#2uksBmQ%Y^hS`_n-RWbyb3X>tN{(HfKq1gLNL z!0nJ0QFO5Jjcfn(ENWjqGgMxKpqW;spxvy}-y(d}rXcpd*aSOJ#}>UjVICgt4@LI& zZoU`FGO8{rNrh}~Z4>rWD*aeTOH-)RVp8&pJ%)qLsTv6}d57oS$e`?3Q{KYKHCvaH z)@&=CYPG@5^9K7BeLWq&^}$jL#SA>jYHD>26GkQoUCG%mbS8)_6G5q3Y4a7C*nhT} z-T?vfhbEK7yYFZljo*H7$$S;3Nrw|D8seaJQX@l2%#%lFqn^?{D9>HLpQGgdHao+a zUcjgCzxfF-iKDJdGMIJ6yz1AqQ_=8+Z4a zGpEL;rs~?fhk=SyLFHn#V_~B9tIJDUmYJzpkNa682c?FU<66|uxg{y`Zr0{*wg<`r znM9=ii_`P-Bz#OM%75Rfos-!nA^V^Cd_CuswZhM8DUh^+%0}D8v(GsAp5D3|{UoO=&*grh1pY9cd>*#1J zFE0m~9gIup+Wb5|CpWTs{#SVCfW?W7e&%0%`|-%k%_+%a3W4Aiy|2R<@I%}*w!E#3 z>Jvs1s-nVud2;pAM-YyN_-iv(U|Q{zX_$MM^w=e0Y-jhciSF(=CdMwK)dNP0W%e%T%CdW6P3~UA_;4HCycm^&E#pp*bZb z1y%_vN$ZH2v>2v4e82(4x9=h2E|_#?+R@v*NlTBo1$sil0_+6g6)tXd9j=#yBL2tI z<9!;Xo3qtj6-SEE>k^+D;JTVCY*GuXQbl1Br2l+ta`ptrg*>3@U`r~0zqv_I+!u}x zlv+SvVy&qAh%CjQPX*Tk8sAB9Id=ff5rlGi|&Tu;;?ZH@_gp%xvFa zqdKgsoFGB{uAvGive#Ar`bNN4u#KgGBU73q~Jp|et8`I@MF8bHE ztr>@hIZ5U%OZBQ7Y}^f1tz^#;*M@fE1(!1Thk1US-ydB%$XbDQ6ke-D%0a3<^px?H z@pMzu_~#ek{jzvx7(jJu46IDdx`;WodF6ia8v5KCCZ~GstAF~XV}s3XNH@aDGBP@> zS*jk{J2AaHJqfiU>O0E%4-@!hhlxN(hxX?7iM^K;xGU*+F8*c$b<7SqzztbsRb-xr z4g9*NGBjr#sq*Tx9c3*teMFq1#bDfzPkM6Bc`F@F8s*lh1tkj<5H^g_bBrFf(zBt1 z#o+~a^X)Ch#E2YH7>U?M@Ow&`q-8baJ3jXsmKK@0lwfz=zgh}=mDi+aFv%%~+QY_` zRu0=7!`u}vuJto}D~Z;ZT|Kdt9Zk<8`_}CH4Fhmz! zgAUJOrOo|%0R;vUs_&j9nDJKj`>x$LMQ)GYKD3v|0w7LY{Q>h@JG*kb@|;2n1O<9# zyvSF1>)Z}Y;k3A+#sH3Nap!r?bkYp0d!E0(_nM4jz9I{MYxNqc=svi%O(=QvJcLr*W(CZ>dHTVuf{ua_sDRlW^2|rz zJ%3O>f0cWfNE4HK3BGLclD%`YUYf=lC}Ct|1O_XMS6W#!&OVl7>!wy?7T$eRA_O&UNC&zo7t@Sao*a$Uau7_3yPC2h%2ruDFL#J_NFam zlQt{cAGdCY7eFclCe^R_mQuucpDRbiLAFb4$S{xvj)p6b zvR z_#m~U5-2p55~Q-b-SN9{y!umQ@BGTl%*2H42UAaafp>!O@)bhQ{*pHr4_izGC%wJO zt|I$cX`(@tqC-5e+jDX1O=Xo7_`GMqg-48D+fP^~l;7;mv{Dxxi&6Ss;S78(E0-cj zm>3|EFBhpzJD8)&-fzw!1v<|K8fcEQRK6Q(-QGvCa@%pH^n5I>@p;%f+;se5U43!o zk|pe4PqkC2v00ol*K&PSIGM5*tK6}2{n+B5&X5?^lV^@GhJ&Hk?o(aZ61m+d?Xr25 z(ZW%Cz9#)!v*U5c@V+-omsyOn`K;8?Uynsjqx7=-rWS^ujxBn{^0(9F-s>^6Zq4M+ z*xtSKJ$~#lsorpFV$m1yzn{&fi&%5qIInDvmdNF{U$VEd5_0`DTks>(8eBs63^Zk5 zD|pFZ^hc`whOLsB3<=qe_i;wUypA|iKYfh=cf-jpq2cf_tB>oUm#)qJV;JFnv)}ge zL+tn`(bX02?eZDJwvV;p31Zb+_rFRjJKFsw+3K-aMgQU)Uz&QLXX^h`M&{CUd2#=qfpu)WaKb?vIkn>d@>uopKfy{5OT>M4`J!NB2uB zkDuh^YEzyjm#6pKTKpelp46D|!=&D`>I<;SZ0a@~Tc>V)YVrTw<-I-HX0r8E2M*N{ z&+)&ee0+nepOTW1m6AeELh}3ecG#p^8nwys@j%Icn|8}%@k0@)91OmHdbm8e@2-a$ zT936{Q*4}jh8i!odTl}>ga=%@AQk53NUW9&(>b4Zws(S)}YqdlcMSme#6j znRJu#<+(J=2Q~J+bOfk2X2&`UUZ=>vxij+k=w}>jsx7IUH;yl<+-fj7wkpDm3;=1v zT(dI^D+?U#Tn2Om+uMX5nK8ZVWc?vx%oc{cs-y^=Z1T>kJvNmFk4pa6*v$nB@`{(& z*MeRGZ~#neJ2_gobUyptx(rEXVG4T%wfjs!mk1fxl2@MU3pMgLj483Yri>Qw5_rqa zodww@4z;tjbeugLN?OK`eEI#|fh>p`60u~z^3Y_*Xvidy3uBU@Iyzp>EqjK)JljD{ z#g&EoYd>eZCnkiHq+#0H`M}2r>-lJmiY9^rPo6V9#7RanwpWJlur`f+i%`)r+O)^eBMOm|f+0NiYmgAul+k zCx}cffeYw0X>xz+8k7|v3s+Ya1X zYabWg0re&=rU;GFmE)yUW3y_l&t2ICR!u&BS+w~H=C#_g^uQ#E^<1+!-BdT5l0fZ? zzo>MpX!i^OBR84LmI~uJNx(`)U(+#B=hOyOci*m5DZ|d~avK~lHn%(tL$cwmtu4ZW z0t-^o`eK?Hx(JfUUQLrvNoH*ZN-tMAoUMk?{J9S01Imz*@nHHE7ot&18L8MbR$-`U z6m+Q}0~~LBJu)9&@dJiYF!eL+VQOHVHimM4clXLuP}gR=bo=A?zCPrJ1^jS{^>7Jq z-Tg`C*!O|(ff*DnH=H{L@jlZr#1#sNb%&P6$xh_lf24iRaZA)YMKP}72ka@J90-LU z5-PY_RIio=N8PNE)mFM90NG$LxS@fcEIdgT1QXBRf|;tcF*Bnd4fFPI7w@|tPzGfH*ao0u}Kw!w!G2PE_wNpz}0+<+uSAo=O5bWyw^r@8yecICHrP zG6P-)!$2=h!G-4lq{5pbpEPK;-PeUSo`~-o_NiJiENGn<7~?FbBI4oV^f>zwepUai z$xYNwyTUH!aQBGJ#+uAufLG|T@!izK#6-=ZU&4ee$eO6H;1UNA!9fgLVu3iZWN~Ad zs*>v~C(3g{YRVv+@Y$u#WXf#GVx{~YR5E7gPr*113ucePvGT5Nbv^H^Jut#h|50Hs zQeA5_hm&Da_iPEdxW(;J4`S_*H8Y*%?+nNFK~)`+=AN1S*MlitfkH`UJ+V9eS$=-| zBW*tI9}GJ<_uJQF-UvNjobRS+N1O8V&BtJ3@{mOr6ugOEce=S>?~mV=FL3*kONTBe z7eh;nMupYVR@gFmeC^prp$_ea#P+|42cN6TIyc>ObKAoK5cY?MHYUi`^(9=uYZ*g> zyZ4iI^Hwx6&A!{qinSm4GNQNokOQ~jR%ge??vC39IcK*WXVIu?m{#tt);6YGKsrlT zvs(oPIXR-0{TzIcfADPJqtkxEeZqu1*LAO^;nSIB2Mh+&+#H+IVY+NdiDVw*nq%D~ z%@W(h?$E_hx+SFUag%O6<$Z_PLipsPSUEvx91s;ba{O zw$vUa)<4#UG23xl+M)9ljw8k>zh^;O*gPX+Ht(1|-3W+h@(e6wtEv2yGChI*2u=A6 z7!?t-xN_Jpw2zoJERbP&vt*5atho_0_A{n$uA!Dmlm+Q95yTH{94y{P={9uS-g@zn zA&WIP^R4)uTT6M9_J0C6Go@dv?uu1^AJ{r@Bkixg@p*)C-%+6_hWG)#gDdClk#t_y zIAbDp)$J{UkCJFonq?ZLmSDOFzJ{vbTmV&i3R^340bzH$wlv17t|L>cpp7ABe4oxwAw*YN{r1d((R=Fj4diBv%B z+}uxd+lI^S-mC<2vl7)UrOswFTvt2QkWCo&SV8{Fz=(%XaB{JH zxkEl*VjltRq2yY6UwGu|v0%r1g@K$^H7Xf|IFyAkBDW|(C3?NNg>S|8Zus%h;CwbIX=}!? zC4G2PrFbYP>=g$`tzx07I@FfMuw0{DW4}{m1v+E+DtPv=K5~16nVDHQ^Lxa5&BrSX z|Km^I9+{9$^bj#P;NEZL_~FVS1v*zTGIG7_dA_dHQKV4{V~vyi?j7I=lVX_F<*cPL z&D+*;^=ExaWZVb zg?}Qhz3-UfLv(pr8}Zwr=GVsq@o2||g7Vr#(YKGuKV}QcXR@=2jfm3rdll@R1Q17P zt;o%_>sv~D)<40dp|A%}D{drAYL+li{U~!g7dXE3QY52n_IT7Q0ySwM)T4F}toIXz z-kd=kl3vC7+rqw3Rmp2=o5_F&|>u`!zAKs8J*%EL%IZr zIeF!mLDwtVSvFbQ%f8#pNt^9WejJ8$ujA;Ql?o@v=f;H|EpwAFdSH6+C$j#O$$&F~ zr$>GVUFIZy_v6Be-HrJ`jGnqW&W_W*eGA2`n($wWqg1QcZBNS!FgqZOc}i$#z@QfP z0W|s#&jeO*t^GEKcAYEJ(SG{|<8-qLjMg&QV4>G991@OXfD#NkK6!icw$|aj0q_FM z=3t_dvC^>JWi6f8ymQx4ukB2SI#x$d$BRdRkDX5u^||CeOb55i^?2B_OIim4fX4I@ z;hS~g`?*p7xBUG6$B#h{lmf32iMoTum>pm%Ck$J%Qs^F*joe?`T@HfCYnl0~K#~s` zriG6*s(yfo>&x?!^_2vr=n}u?a}R@hr$vH0&4p07gcW3u#?|A>xOE7JutYlAxhnJk>DK1O$2ggjA*@U+%*-Jib7i>m7BhhXhqqL~4o^c-3 zy!`wQ?z7E&^ZG2wo*rNwOsU-X_{nMyF=hPHUXo|;u(2>Wx!+CGzOri0lzt=izArhs zr=);4Bid9Pp-oV5&X$WT+^a2fT|CGJJ8BF4=Jd2*m7_LMw%4TE#nmlc;M1agZPeE& zn^d>cd)~-RZIsl%D4(-!A_o&tp+g%TCKgXp;@T-Qwommg))JSbEz`1dgKu>Y{AVqQ zI&r*Q18&ykUT=gH>SGYnftu}inhSQDjfpUfwUW$ulgz&Bn-}FLSf^H4rB>_*Q<3$5 zVrn`3n)Sy~z^O39nW$pq8Ev7RWX260!IuoDjl=AENtIo~fV9e+xjxHUssFTDDU@Kg z`A3<}5b}&OBj#WXozoKNVRO;fREM%*gc|c^avZOsQGTwlA^{;&ef1Q^fUuM1k@T<) z-KcO&^8YdR73?EJDz1oZ+nPm-E39Ca#paQ7vi@_Xp6{b!I4VyUbfB$6%hbLarqN24 zr?P#Ragp=+KR1-uX?p&Y|>?0T+1n4O=T zlyZhMWBX>S;7yWg%3%^y0Jtb%`cjQc|~9!D}}P zA&Y*`k;QnTIizrB`bmjkWDzFa$hr8ZMzzw`9&$$N!Y|~g-f2S%D6xz7ah+pe+?xWm5eZoD4vS z4*PkdxPyTkcA0VuhlQ=J;rFQ2(d>4}2bwZ}f#BECc^;xdpA3P`ixcltr0~NF=a9f4 z7W%)ONQ@L_w+8?uhW(O!^$91IN`lh)D`Oz9W!Cz-{zWJ|20qeWZJ_FFYHTKSR!LL< z5P^FZ(rsa=*W!C;U#&(R%oe;8DvD}lZGFFXcbv)RxoF~ z=nlS-NT2}?hmpYmRVS_o{ka4k$V^7ZAX;8tF_i;p&bI#QYPvmhxuEZ59}J^m<#!zC z9f$(la;#FB7*h+BWr;)Akd~=Y@3Kx%{%L@O7=@0WSVOy?u$uAymOwoutk?Rxm8e`c zjQ-#JprLU8GLrKWA^%;&|KAxk?CW+P`?*chV=ml&qiUex@afRK zhlllk%+Kf8(y`X#e_{dRKXaY(l#@X$o1dRV5FupK=Kf^(@C;}Tq5Zup8uq=yCd5`p zQ+@oXszm@rpA0sP6**R2qE#1E|KE9CVlr4Bb}0G;7a1Q)tcCQ|%n~<=S{9BRg&VkU z*TA3DHQ-hk7YfVVAm@CCf!`Ph4|YCR>}K@OXHeXU5^hU+rT&gmNmb~(S{zNzxy54N+WC#%Qcit*&ya`@SX#NL$P&H^agzKr8+=8O46Vue z7E!|b{#kDglTPEv^zg7zL`i=f4e+I;x88i_MMe{YO=PT?jEb#B#jA~J*VI8-0Ow+X zjBAbGlX^#{D1uA4J>hHqKKHzSeP%qlV&!_a>?Izj`XnAXz>~QdZ1F(Oi6LHG1awwO zR8{zW$U8r^rN##2UaYMrqhy}qt}rUa@rS~p$R|R_Nna#7xlUd=iITMNzV*l%C3T^c?#k>Q6f?16hX-)pl9#| za`!i}ck$Hp2LA4StVQ(O7iYVdAubj0fMUz4lNK(qjQZtfVt3SCYy%5;=9rf+<#q6h zKxqjd@H4{i&iKN8c^?7L=N9Uy){2Dlh*nk;jGwvI4%HvgOPXS}XD&&t_A<{@S6})H zH%9eVu+^v!_?&o5X6@W~D66iWX)sWT9x7URG@9YNkLa*=Ein50ImQi{eOkpn*kkpl zM=Akp()y@@XXTZtKn+n!7BbKvJj>zIT)(u&`+K0DVH3*L5&gKn(x2+0s+&ByKKX8V z^ODj<8^;SR(x1aH(Os@yoK>FQaO8~>AcD&4XVtld2Pfj9l_9kKlRIV* zNk$dCWcz2ldP3Xvnh6n! z^3+n8*6wpYQEASj7)w9joU7!B(c_bAI0S{Q&)t&Kf#xLI(~~iJaLPW@uHunX13Rg) z3LE(*AdT;8oTknZT!r5iwp`Uw0RLL+5cRwL6d z>ua?)nT*?-{BxY@a*`aaA?|k>iB4d>Z$N-b5hZE1&3mqtQoPaxmC~hOb|)vU+A=8~ zBV(8^aV)RYWufxag*MH@RJUnuVZ1jV^9p62Q3wA{69ZGqg{0q5cA1o~U;(>Y66!EX zVS70ATEnS0n0t1|#u6Fm8Jn!Oe4CYnjDLt_C|(P#f82;4H`AX%rHU%g*fbFW<4$uz z*UmWW>Ry=le5So|VNZ)FE$Kf0yg+$@Ofr19Y0~~3@-q?Iz@H^U+-&dOoMF=yt{1CP z?WoT1BGk3H?XiF$RQpNGUe`$}`|WdW&f}?>v2=xA=Yd91%m!{8OM4^$q$4Adyb`XJ>%%x5_2Nta)#95=H)L73v2IGuf(GiOG7vqf2nH|dbrV4 z&kG2T9dh^M{}wdSt%c}u8!5G<^S0J_Z8_7XWnjVV76#Q_4!RKpnG32Sqc7WPZV9|e z7tJw@Oo%2XmUtcRqIEUF%3kwQVR`?zx0Fbx( z{qr_&RSsIkCnW=m?n(Fqydb*v->@rysMS}7-4U?dQs}2 zE1_#mBk<0tvfkI&1P|c5jY%`^y#KMYm=_jmJmpq3Pgcm%p5A5%0vBoDqeEmxQt-EwD^}2h9^2Vhhand@nq9KOs<>AvVRj`{FfU6Guia39mWNJ?e z$P+TknGpl2k~!LVP7z$`%m@K1-T3PIZ#DcOZ_qcsj#6mt+s@FF56e_HcLV^dFB&<7 z!m6h8btf+FQsVN7FYkhFyU`jlaK5?rw0=u8BPlDLYbDku9ZnqX_CQq3n_BE2sitO3 zTZ@R?wj~p%7Z{X-#Oe8;y=U|vw6W;m9##+g8EGj+6?{VWB8sqbN&oRK=FQW(u;6W) z;nx{<$LaRbhOX+Nc_BO?ViOvpKXzRf-($fI|JZpUvrhXlXuEm_b zuW&#B4vdQB@vE9c7uV`r9u!}SV3K6&hcmx{>!g0xJ>O94d6vPRCwp_Y-ZE(pW5&ir z$bif6cVcbnelC;=x%lT|*;k19CNu&smagi;M$vCMjHdHp7A%#-LG1RMsj|Z6RzHWT zSR50gzU~bz4}0s$z}j_eG-EBdO9fVZC-V>rfI_&@tPgN2Y-!5t-*SW#5sD{izd-^V z1suS<)|mI7d#%B|9$oN&97l{r-%;9I0P@LvMnRon>FGAUq|S5I$albo6r}Q&!eF)W zve-#8X^UdOJ#%kEswfn!l_pG!;!NC6#|&{=tk=BoJbPtjt10&_DfMt?$5s( zU<8raNIpk&yUrL7{4i5f>jBxSp1%YesE=D;daJT+xO*Isjao$zq=dr(Fn?;VkkXib z;>P%X48Ha-4Z2FT%F4jl{KDcgB|JQOOmd#{C*UXO894w{@jRb?hXBAcx3_cXV;B?A z_N=Ag_po)>GWr&_M9ev<$H8^JaG~s&!UlTvJU7y$fNvOud|^7H9o}A^eAZac?X9ux z$*G5&Y*oF-OkUV3zQrH@o{S!P|4`|I&^hSiWAB3osEoU)N}WvYi-*V$kWt&Z&s&QJ zH>6S;as@5-)r(w*V~PDdb8+$Hdj_D#B>6^V-pU#~YnpCGd}4d$^bJu$LUf*)D($kg@9d9XAjsLEjzjWBV5{I0?naMTC6N^e-xXTGs3sc(V>T z{_4i(FqTnGoU2CJ8i>j8E(g_W&P>JX8iLl971Yi9C^H)!%?jgY9EANIFOx3HT{j;n`(=Hx3T&o03aWj9dFrW?mUUL{d`@I!5HhgTVorTs#{k9a|IL% z2jq;jcWJV~QJu#~ZBbw&bWZ(TGLqAMpmi=R48vjfrGPlhpZV2>(fk->U&7jVsTPBK zgN`3m;pXAaU#8(Eo)Ka3(=)4w*)h(1QHHRzIfs~&XS;VpP_YF7{2yZ0!B(DEGFmp`13Luhw=g~5ky}V2rG1OZm>KrZrOj$D} zs(|#q9k8TLns%Hl-?B{#US{oHy(`}$Y-$}Thz`A7aH+Tqqf>WX=xG4`w5C!;1iHVC zm>!yQ_~CbPyvK@hF<-!L%>b`m#_*3V(5%YXF!! zn-YA44iPssHwBiIyZ^KuhvC>;#v0cFHblThL7|trD~T&<-Qce4I3;UA|I9$8`b$AE zuzH0w#6%KJh&-y-{5UQQh)~x^k9HUs##gX5F;sGuRFln2%-K+o+S+sU1oTZSwm>j} zWsy9|x)i&o8of2+fXrPN{kNCICyXJj!2H(tk2ZK_3n*a{-r~RNRpR?I>&l7kW1ASAZG6 z4@g35zMe~zToxoLA|Pj1orLR9Z~ zMBqkV?eY^OUv&E;76 z&{njA3?xij>t#0Qn1D~aXcSO@hE873`l3DjrN&GqE$uf(Youj9L6=qqXQlzDu99W= zFKJ2}R7-uQ&gPSq%;YfP4tG66;ml;3xSMONOuZu`4ey9TG@mWm*(VwI6~uw7;i`_0 zPbxI7^LFuoAtrM>>J_4@%Ga`^W_S+nEiguW6w^-!f~}xTf@AkskGR&|ZIQ-?0;~Fr z6CxT_t6Bf3Gqa9SQ{nO3vM9mq1OoPjGN=g=GNc+tJGD~3{z!_O%65_rXD$2%p-)pK z6PRJNw{oA$D6r1selwKQgVCcU5vWok=($r`x$+n)U97P-dAe{;TomDS3^7c5rvo!{ zaPfnt^fs@MMz0TE{U)6Ct%HK$N0IzCXbB zgDvwi>iGCfVblVq;Tw-R^s`M~jD7MVPtn2xoWgy)pF+{dY6qYNA6gdr4g_3{mgFaTP^__%+@ke06bbNA!lYrrnnZ55V zgjNunDnIW@%X{Sm7rs(|UiispHHMNJ0k`e_QsJ9C1@JwIfmRgr=HBfIzicseoqI!y@&=fI+L^7QlMUbl=v?FDpjXAi zjDs8--xsEjaVKk1ORTF)a!Km)ck_66os8~eny-tw-tc^UK~k31f&0m*Q`O^n%#it} z|AmH9kUy9<)%a)lqmhN>J7uP%MLVPeh2_TC>6zW~OGnq>MMA}+q^W8p460+p-93yZ zn^QZ@(n9*i`6xw$sl`{8v|R)pDT{hTiSe=Wb}cIELyNwErR90laO|X#9 z(DS@e`?rd{YkS`Ow$-RlsEONULtb%S7lxP{@5kiig9<27u}a0X2J@KO5*ZuNID1k% zP^g(EtmAzPJ0nv#s5St45!2pgtQfP7&UKl=b2pFex98{$Co%)pIUCZjf3jxzz(7!8 zw)GL0F|-0909r3(I_g|kscUeTeSF55PLY+e{3`Jo*n3K?7gj$hM+T1AD0Qjj)@xz- z9xWJ2*IJ!OKe)PA`JO+Xj5Rl7nVZ6gMRyCg&@Jq zMN@glrCVj$j*7bLETW&BNgp)wP?OdE=<+d-65D>hyLu?`+>TJGA%RA{&S{Ge=)axb z_cutsWPLoDQca(;UR!QntSAaq(-u5m@#WO^wz_T+UwIcvSLee0-rN*mdvIjQ8srzi zlGOMWDO2mYu!Tpu;D(McMANVlL5ahZHTYVS%#!7@?b!3H6=)W>HQ z%(+#naI;`m@DC^1I$d>1eUG|U&n)mtfoyTAmLi(QG8uw?jB!e+hQBR68W3}=&~P0_ z6dFF%JETXiG-d}jPTQa4*zW>x^uL37^&<2#DilsSjy(d_k(_sbx%l)Rx$rI;G^td8 z=CqjjH8$G<-?vQavutffT%7c6+btoKP%FhOaT~rvITLcfYcg|Vt}fIYk|H!`${k*r zt=Sd8fYiHOe0Tneweq|5Zk9O7jIfX*X7-(9*a|=c* z4=2rQfdEA-tvI;s5>nbWF7%!RFf<(#9RdU-8f%HU$hyi>Zi3!38cu&rc^IZ4snIr% zP>-5O&s~yBoL?B_VXQYzfWHLfL4vm zjr`C3dJ<&c6FLLBL7=lne(b3Xf|qSE^LADzapOWZNyXZHVaA~5OZV!Hf%(j`h?w+o z@raarK?B9|{EVJ%g1eHPFLQ`X&Nuqq)OMcd75>HFn41tP3a*T((-}4g=RQ6j(PE=f zfeXg!AEA67>@I_3oA^FC6coSO;c4Di%wNZO-pG(>?{|jY&E`^W)lpn7=W;L`uL!fVt_?OA`sVNJ@DUUn`VJkUyIoNF z|GJwCdswVl+I<^FPueWxcigvxFK(Qv8OMmu7L%cxRJml$E{-dL8K zBDF)KQJOGg)1a&I5tO|Ltgg;#q#*%GgHEDQ^b!gdh#e{ItTvrG;U4b0djb~AO*oN{ zDWb{4>HL<}Osq*WQv0raWm}DF8fi4t^q%T+1ABzk0#y4^RDz0(01o_cu2JYSh}oQ8 zyHOU@J(H@i^$KMU7xXO&>Y|@&qdbYfRApo^_M&?m3HT6~aJOFVSoXViMgD%6weRUc z_^Y5#-8_x2d6BWr3X<0;Dpin$x3`jOMD(S(a`bj;3(08fC%w93!Ha2_cG)uJ;9iO1 z`dNL47usZ1)pbM^O4cC&(^vJf>y9?7dHRCWF&pve&E)|F_3G=SQfZF5<<=2D$I z9aYUabIbtcjETd!|g%|bFUm}uQI;R8*Q@QW*7vPl8@lx)}U1)=@TIcAW_SdW=P~-?HENm zRlPBvyzV!xsS!en?5wqjen)Y%9Ggwk$2|G?IVVul& z!4k$;Gw5`{q3%zw##oa>+S+N?;>ZfB(@CNS5u}AI9kmRw6)_D2{QMB>YzYxaJMe@gOq8f`xhT}}+u%Rg5jPve%EIjY z!y})RI2vAxg~f|t(6(I5vTjy8K;^ARm)c)dN1UwFThK5 z6@N?DkMk*PkQ?-Jmk$X?J9O#jt}9z@0y7$CUsZkOU62N55S)n_+PSS^lT;wh6xEgO zNLUzQI-bjS%&Xsbd~MZgyoL#IotsshNC#wB2xPLYAuIJ;4<=ZKl1A5bpK)sLF0wId z`>`&s6m{oI1zcy$GF_Hc>Xg7q#}*e;Xs2YXJZ|4V?C{rYGrthK^5n;W%x5Sy^})Q( zH@}FxIa+!T36iQ0=A$K04}=8i#J{W)r{?m6^0+Zm&0}LAOjk3#uyzS0sU{`)&O87Czg-?(V zmIw5wDoirc|9yOX$LzEIpOA^M_CMhiUMXY~pGyDS8vn1e<*=BFoMg0raSeB$VQ0=^ zg=9p-$d~q+yBlz*f(lo?eyvvZKOHW#mURqW)+P*`Qy-2gf@rYGc^sa*a*@DGQG+^35W)%BeY=7vH;cJlofmPrK>-&m|^b>=w8<>SMQiKX#(J+wM_%Y`F$!r}GhpGOA@H~w4* znl*z3ldjlf*iS*G|E9X9JwaIvG6B2Y^noBW6=vsWy!0F_9fDO!KrP5xVw zX_}B<06+~qW=4BwjYrZP>aB*F(I(i)&VM`C{tr4M;g&O}x9oxR2^wQ?r~ zS8#kV-P$5;@hcsf%kH0L3HdiS!~J2AqB&YQrvgzQqf8P>@DQ=m2?~0pNc8e^J3rz9 z*)4&@mgWR1eTT$?nT2a#W0@wJ@=vWPXC^y;9|ZSeXawVUE&oqs$PP{p-bc*$Nf%`j zn9rEt(nNH8^Or_vnP8@`sCnj9@QW5el-BCh2|?k2TXnvTx9#=h06xh`QNP#$rQWI@ z&nMW{6moEVrdG~ClXacUGe=Gey@Z}ci_gaz_zEyYXN*6H&e@yC*{NRDcWWdujMveo}sB)Ym-t5Q+E0hDNWAr>sePJ#vKTBkM*-77J+_`v$xQm0;HqF^hC2w$L z!{7m?=0!3(fGJv4szqa_vRbG7J&IFid%K@uV@rKb-;M~z|6%JZqpFI!b`KyS-Q8W% z-O?RON=k!tcPL1Aw@68sbV`>Xpma!sbT`}u@B4lC{_@M;dnV@LFE?lZiY)u5ydv-BF$OIM-sq<$f%Xz`J@a(aB6 zVz4A#3sVUp?PZpsWdH=iCYlcg!5h>0YxB+UeZzbd>bek5>Am(LMP6ROp6VOTxyG+Q zw&~x{V1_l)Hs5<4hN_yzqM_Lisl&5^*LtbWtG4p@k``V0{SGW-%Zjurl+B{lQh?e(q5y#s3u?F(@5 z1%z2(1P;PnZn|@(K-MiAX_~(uOT<^C-(R$EkelYlS}V-xU95*wDpS#(;xpyU2naxth$c_wRj}o#Y}2vD4(U z-l(-l3PGF#|5CG;FvM3_$7*Q}c!Kvqk|7h2fC8FOzK-9nuOl(~=PU;ZY80o-<9Q7W zlLd6LVF`eH#)}bxk>fF7;;rEKx?p}(Pb*`b#akO}lQNqwQ~f7|S=a$S2^AOu(g+bK zs{Xc3in%Bu#Kt17hA}CNUd*ARq;6zl23t1t3TH#%UvKtx!H_90=Q?=50-_b9b3$Q? zU+1VKd*>4wwtS#sCv22d&(3SV{FH{v(;w{YZHYycYPngSixdA4t)z;<#&#Tk3kZY* z(@z+N`Bk56Qkq;^?h(Jr6eiA=tEOqXpJF`aq?D*OtTuMK@+T7?!=eY}mL*=VgBHy= z*<^h7RXxj0E@c=KV z4_@OUUXA0ETBR%;_lUf3*?#ykQ!i~*k@)H#>;elWmW6a<3;rxCa5?d(Z{sVH)w8^5 z!quUy9U4I#KxvdYi%Y~Y?IAOp+U_!WI|?8#FHqCnb*kD41Fg|hWY~rGC=+mC`RUbzjEz-ErIVa zGwQx&s4PyzZJgDL|Mq*Z;#-O^D(#CXnO;E>fvwXHbO>YuCgS7MHJK5g^s#!&&#H z_v5l<-@8nz-@OsgE!&(3k1x)W`78}yZw0W%cP>~c<(o`uPHo&X*;{wq72PUID~e9- zWc#B@YTuP>Xl&`5(zEgL;nTa|;WS4S)#nKL1%>`2)WTp+WwCVdSK#~uWk+7&wBDyinuP$&qR;6C*hYoSqpq`PX)6*&UV_fA9k`DL_4SoP6OSS zGM3UppZ@xEyV?8EoS%8`qX$RZ7TXy1mmeQ4b38ZG68qeBm#${1(DrRTsS55bVua3m zRQ!(mM)%(mxwjtOO^bS8M&o7YEM4#BvqT%V*&N2-C-_*cXj6O4&~i6P90-b^pY(t^TV#*H6B7&JNUMM8@zZpxl)Dw41) z7D^Ht@)qelXl!-=or)a!dhPP5#0#(uu#&g#U&*k(`gCK-Fa5*`1Mu&HZr#Rb-9jGR z(TS!7!EW#Bcec(h_3vysm)Hx~J^Kj+4OdALvRIF!8D6Lk5D&xu-rhb30Z=FmdP~XdJXlSC8>-xc=?@k&q>lGVJ@>o(`Y5j!(aI+OGMF z78El#*4nj`@seGxM0r^553U{<{sBET+j2wxHWCnFaDP|#s3sHLL`*mXpz!m2kQ6pc zR~kcmtWM)>k z(ziG~xESpavKU0lH`JP1%nVnxFpyY+qg$|L#k^4Vd*K_7;>b|1&%M>0LYR1(Nq=w@ zG$%)Hb@Ahv)e00C9p!5z^`ZQ}r+ml+n1Z;hOss46JxEp1AlD@)j|^B-wxlT;%92b6 z7qWi;M=5qA7s+xtbeR1+&yg@9{IdCsk@o|0QkxyEZ2dP*X0&)}M%F||{dlQ@&c+#U zc~Q=xumNjhqFvKTMOI)83Gye> zh%0=nhZW*|6)h2V@IH4(QnFcDxO?^T77HxJgq4wpo{#|KKrV=pRWJ8H0^A>=sS89# zYJ=A1gEB=Bo!nye=5r^rTkknJQM`{I)vE8?dOpZ}pEDlZHCHA|zz;TA9r?z$v(=sJ zoMFI~CEabz#Vjp;b`()vc^~F!)>LMUi3}l!d9saDW{x`xK3=5xJE|A4)uv&Z$-{1( z-se>p?J6-J5`W1}_KtIUd5b1c8%yfN_DP9n$U^Y*-q7umVdQ?3%}$vXr6j%f-5YL5 z)zTQi{j=Th0up=rT=3kDAZj!f%=3O@8qOD{J0E==_e)YQ@9KMEGCQNAY>Jj#iucA! z>{X?3*nL*>sNC9bPG(YX?uX_2bWJT;9xU1(2qZFy3m6)=CqK6T$4Pn3^s94ZB*TEq zm=o2=Rl=8;;XP%vPFvOTDe7wU8jT<$YgmiNt5eFGL+h!^i29$LUs=?rvYZZ?kAR$p z5B~)1nXyewIjiT4HT5O)aNe*{_T^zuzQD^LO0;-?jjL&7Qo`XV5@2ln6DQuKZU~lS zFMZ&J0KC)|xuHG-hB63ZHh_Qw3JQkQ6UX*8otfy=(`H8^*%_Grh!PWou&T>QngMywkYNv)o+?ADQwf*p#0Q z0>TXla`4l9r(sX2QXVsSf25J7O=FiCTDGnhu_dSwa}(W z3F1m-C!ZQJY|WGH-`AMBh(RFyXiw(J|3t0kN%8p+0!f4JhyxJ{fjB*5eh4HW^V8F0 zfRNe(Zu9grz&}HY>~M^EEXhTfv@ycygCEiRy;wsQ$~+~sKX3foxLSUA|JsO+3jj;Q zB*1PFTMhyS-rhyvpoKGqwvNYG;W2}TuNM=n+}QXP`a5lByJ9R=b0q#g-Z|h}LQ+I7 zeFGa8Y;ZO4)74sPhVRr2o9R<-ddG5#t-dv1tJrHNmO?QHQOlK18NPpdrx7wpvIHyH ztiD6W`awDX5kdlj0oISjuqG^t0y0@JAOuF(=aH=wKP6=F!~g6WV9(Sj*sTIGtb#@V z%Pf^lNi_o<+v;~)2dqKc2SOX=#kx1YAdtVS@4?zm<+D4y?7KQ%6cr6^1Sk4<++E(X zEc-x2hBP`a%Exh+Veuf05(ow9Eah)I95`UYHrT>vqFAob^_%H4Ha!Xv*3?sx67 z_DMP=b7v_HrJCwfQ^9Fhds}^KVmEB;rTb~a^zx#^vq4L0y$0igvcG8Pkbt-&U3_w? zh0}r-Fcu?oar7K*>>g^ln|szr6H`9bcPsk>fBddLGl_Ui<_H}pkU5MZ=^u#R-SCIh z{!Q>dZtC|tYO}w)@1FD)d5rK!3hp~48O3`z#WTE`d;G3#3)jI52_XLbG|!uu_}h;w z?hnbKd!AtPFc}*9c=~xp#jMxl_P`O=#Xw9x0Yun;ap^SI{kYj3R}r$d9GQ#R!x#KX1C zTA9vwSJ7G{K!cJx=ltbSv9i*6E22Z$ucV}cf@B;9(#iaMd2i{r3SZ}GaMs^&&4Qc5 zGO_S*8#nk;f3HhJ`FNkzfm}*7o^BBCAUi`KBQ%((kCV&3!q>--_gaRs*ETxAmo?`Z zq}NltCt0wt!FGK!Kq-5vS$^B8A$oNSzt~H&_G7%G@$i8L8Nlq#?yePw6RJ|TnHg1b zZ>u+I;wK~X<-EU(kzS!3o90c|cc`gu>$z`iNW;hH?#Z_gb39(oe5}(w(19COE#2aF;osM zb=_SpW&8doxAE9Vv;WPdPJt3o)}4CRpJKSsQPium?PY%`=zm>M`EzmExGq-=99=>C z?BW$2W(tABRv-@WBH1sN+waaR686{DOs1F5=2HU@l0k^OKRm&;KVjUOz<wN)Dp4>~eE- ze@x_9dp;1r++~r=-?fhy+SJ56)Qpe;(^QaZ0;De1Rs7LWLPWhEKLJBa@YpVKVB>%L z@D3C?n5fIwY2Xx!E=?k3cenuc=Q=L#{6-)I<`YAdx2}Qx;(cFKgKulQm*?|3)R@puN`;JbQ2!1*FT3NNZx1?9yEZNhk9emEUwU2g?4GP5l( zrD@RQzCLbg)Az4E+hPzFF=}GJ715jnKDYIb$9;*noPj(15R66FF^gRyt@qoWJD*`- z4(nU9@4o!f_sL~FJa?7uT3I?6r9nV3f*bcxWMZ*zKVi&+k^6HpK^BSn!wB3qM;BI% zi2kIi>sZPGj{tfAl2P64XE=Dum0u1neW%lE1iGq zco&Ra$aReR53@TiBpR62Jad3qo6Bkt73v{QDA8yD;|BjbclA3Atb8)PHr|5Ue7{km{^XU4x~16;fdI^k7HK#;j|7H<9q1B|D*Q ze>5`(WClsS2CGPzjDPk0H3@KSwvK{fC@hYK zC?9R5L!cnBNY8fkZa-s)_+t#>kux-?dq;=c;Gq0r;T~-jIWT|yb;r)~CybXN%Ra8R z^)Ws|KQCbe&bq&n2Sln)s_MUsbEIR2MjtnMy!#csTws-p>J%V1CVV|(5+n56?F;wM zOW%_s(V63}Q{dt%r@m0+_fL`ASq5%W;u8!>KW@1=J@c=^(n_Y%vJ!<`n@*X-*4sHE zJ3E60hX460u+(WZHQd;@-*#J4o4Szm7kMuBZMqUo`Y(fcm1)aSe)OfVXmgT{pncQO z=lGghHhv}~edBR1uk9=wclreeJJ0xQbU8^PP=vKEt)qqe`>~5)u13aj6~IuP@Xc6M zaN3q6O#4`&hwIQ>OJO$$I; z&3sXih)78h8XxgMKqW0e7nQR}L@rAm5srxwW=e&Zk3{KrK$44^C@4F=hcx| zKD(jEd9tE{GDx610{c6^;692g%E91MNqv>aW2e-}#e^4}D=j@~f@&kjn3gr}!5_rj z(AB19xyYWNPM-@=WYp{!gMC`9O}{tyX#^ioGQWY=PX0>Bfa$!&?r7ji{$5jCiXwr= z%`<61m6Cx2>n+(XlDX!f-TM;7UeAfB6b4wHzR;fQm{Heg!Z_KT zyNMt5mdpl2O0`*GzVluiQ#uc4X&P@#=d`IJe!u-p$0i-F$b+>wQ#K+rBia!}pWitR zM^4px5yFeQ=Q*}bPhR8-GTRIlS}wEVjqqiUNsRD`s?(!{`P}@;%_ZrT{AMEC$KY?Q zuPRuTvLukCOz>RdqT31DUrCH#MedXMJ`k{;+{9ryFLagE_#h2~lT1$^H*`~_%pzrz z9DBc@zX30$xS$n+`zgzfB?MwEpBP=(^mt2tPNzZs82Az8;7>Fon)zrl?x5h zTFDv~bZx8Wz7jZpfnt^%dV8JEr1{&R`m0-D)~&<*OsfSZ)p_@pG?*ge^@i}D9lq*n%+sh)jJOiQ22A2fJBM~EDy=vrBTtwEIQRr z44i5BV<}jtZb&57O2{aM6xnA)1Vl(FQ#P$`!;v}n%VYh0dBr9k(x#lXwkZr+yaW1B zOM)ZU*QE~$Nx^-2t1#y^a)nfFdKt+t5xxdV9|*nE5_Tp%nzQ%U?_M=e*jZiHO=kPQ zfD8gr9JnJk;$!=@yK8O0JI}FZle7XBa0>VU8mB4+)++XttSqgW1hwi<7#Miik>TSA zJDIz8t$3e}UB2mdj4&n0OHfd}pgB7|0gK`|sjpcRmoDKm_8=FO>{>Y#KEf&edGWK4 z%vc?=-&=TCNO(*i{KJ@1+E41aT`fZz zZmMyeWxDZ;*WHd+06jINH-9P_+cWc88XlSe1u$(8ep~zDU5n;+7pU@v;>NI|1 z#^A~2 zjA@rX!anxH<)8jqB5P$Ei!zAq2q*J3P)IJ!yM_W<^=)pWjz?wFjF$x`Uq zSBi0QFGoNp4Wvne(x3JFRAj6!cmyN}5WN!Cg>R#a?-deS3kC%&@GdSNQ#>&whDe@s z^sP@I+oGjHOnKle%4li0E{x!9FdvJ2o2=+WaN#$qTpxSyY&5s|>I$F{wy z6uq7#0{{$wHU6!JG2wHNlK`Lw&DZ!nkCkiSrAhTLpxq12iS(JOvW`S>!58#<$@4%5 za{xg_=;rKV%N_seqrkfWa?6|5EH)?D=5xyj3C3o5LUo&^M%y;dVPIR#$h>#-uBEcY zdbkAV*%1KC$kBD)V8h_R4+?9i0gLvdByd9(CQ)B(E|VUw7r{r)wr3W4F4FFejhedE zdivTX{jWDvbht*0Q;3ZYURY$Ujvg1FrxCtF(nz+hSjgrz1PG z^<3N*!+m?Abblm>Mo5B?|Bo2K=XB@srkCgtp9H~_>$% zK42quChaFTcWX*B$Dw@zqOWbc+Nn|3SiiYdSpsj2-T4oUx!;D$lL*kOMlR79{Wd+F zwz9T|jCQz64j&MVPe4F`kIyC$u1@uXod1=y%zNs5s92GW3$rUabdK=1%Ah#JU+Wir zIDWXleLN<6tp4_GU|SpVSE!`K`o5it^*RcbTEl?y^>bz*&*0|fMnOe|gN6cwP3yC( z)-%wtbHQeVdCoxR^|tfi#gmr@tVcT|jtYszYHn_R_wHTSx9xbh+si}$jU+8Nc=#w( z)0b`MQ@T%`o0LoM5tg;_ne2#$#ht=O`NfryJ zb_2MXvwXEgBvNj-eZ1}C)6?!hxu?OTG3!RMOR0f-E-Wb-uU^O1(7=(pJ_-@FdHUe8 zGMkN|q$?CR2FwRep_$)Iw2o8E|D zx5MxL=6p9Qni6lLORN3cH-wB@PWiw^x7{0VP+X*;J{Qe5(;ap$2oRk^aO-12rz4v` zGphCRAw~vI;sVI9jFuLj<~JA!^9yr;y^PW1cJ_of5t6p^J>B=}yHv+WnxGML_C# zQSPplm~=PTH$3U{VSrK=hTJ#tn5B<9FKg&_+pp>k82UXnPCF;-i|etkTWxLF zVfhr6DROn) z5{XJJ@rnzD&-E!~nkK>)LOpkQq;ypZoJqrbHFRpy6(d}KM&Kd`3&Mr$^OSYA*;xZ+#=RAuH;^elkqN1a=TH9C{RACRk8sRe4aKpHE%jMg32GUgDKW0$0UHSv|v4UTzQr$VRcx17liqu;zIZ16F}XzpQ!YCk#m8AuI@^ zARTGfx8MJMiza%hhwrLBd61LsdoiyfjDr9dh1vtD{9f;OdfablWy>uPn~AGnssBmh z(+|fyn83&#Ui(99+PtJUwb9*rSvlv2o;wALxaM%Whvuk61YNmmsAw|%@g!iAY zt*TkledckikUfP@b9SorXx@_NwL^PQqQafZl}I>8laER~5_J8FO}0yl)=N zsCUwA&Fs{~_Ou=(=UTY$Bla7|EcS?Suo11+auhy$-ly0NC>xBuc{`Pk(>H@R_up53)bw7SkF}7}@1mrxcYkD|w z{6{^bAJ4hkux;Uu`u5C8AU81daF4!j!{Q^A`=ZsD-$f(eMl*FrSkvjuZvXdWEU(9HWB~geU(?-Og^3_WBGt-kaSs?ihVoI5hOFJ6Ps*7|yiplh&5b=;>ee&$(ro?0sbe;`EB1AYz zUYiqBm5szs!%oA9qfF=-D68^8qWYRj%asK*Wu><7-lpjp48-U8v(Q((7dP^|LIgS+ zrm?*_*Dq}(Qtu`X%Oye#X4-Bmb}Ks^*QE1I2C+L9+iu0Yy-mz$+KDC=5iq7*)7Yhw zqoU1nA9fe7+9!ROy$)tO&?P0!f+tW)H$D*IP#Q;uZda84X{mUFB_MpM{M3_t_+rVQ zduWHI(*A4B$iGJU{x`RE`m*>GAQsqSJjAD6lqG33G))t!J}ah_{o=S*6v*8r+>*>6 zs-avV@_0ErVjk)Kw#?r02p12JKMTNnX90NVn1_`=6VO}Jb{(G9-ZD9&*ROqxExc|c zqbBF^^!4(vd~b5?L~wD8=Mca6ATe+V{w~J?E$W&QE;icckQZ?dvxC;gkwJVJoYW{U zdJo)e_3OSji<@f`a|r@G1KU;Iv+a=>s#vn;HZ$RK=^k_8c7}>Ej#J90sfqJyT)+Kx z(xP-I;;{W*Lczs1HDmPJ=xQN>TH5qYqECVxkIMpaq%>8!w4_@C!)@d!zt@+Q6*qb?tOn&1@!ItWi6LTFaEF%+NdD&KNS_cPjk>=jb6@^m!Ea{z z2zY_$_COgCXAAkqoYu2&FxH#VDxbDjPQvLF#L`cyX)4DxSR_RuSCQ4%CupylnKQsAIuDS2-Y^ zdTgS5{W0hZfNKoN))6%4d$AXmtcN6AT%$;xcbLs8=|_) zzj-r;XZ{YA%fx~*T_i2Gbq&W=Bd+Fzz%c$-v0&%2-PWMQN2-@z%SAb7&V90RJ9E-z zCgnodm_UQaDZ*ePsa2^8+P`aR$Ng0APAI!6Fv5^nEV-Ed+O$ep<36Ytn*PKM`~I|+ zJ7mAAtCPf5q85;mzFS)p*s^zb_5GIn!>o>w1W2tvOqDS>2~=B?kxx1(OHg3o=iskj zMjie5D_N|fW2~7}UYcxgsrgU%O^u9g^?KC=&UtwEP-rMdd1YMFmn_@4i%;pJuoy1^ zlOdV!_zy9(6aTU~?c+@@CkWHw@WRvVz`wHev_S(6X-p9(KPM4C!%9>yb!t;5vz5xf z&Mp|q%BitttJu-M&ofzR{8RQj5jUUJYXG^x({nH>zm8>^H0SW_hz)O5Ol2&DaP*bz zns_Dw5mA&RU)-W8@mj2x&5i+U_T;-6L=#HVdb{?*JPZjETKHZSq=B;U=L)oo_!pK#$uoJU1TasQ+q?saP! zPCoGEQ|RA!Fp3ipgNvs*;mbdPcJoq2R}(K<$MU75#5{D_MrA5Jm;IT9g?37!36w8t zvZ%X~5$I>5FWWX6Nh9->@xO7DGp5LA#($&=wgAG4G=&fmpt7yW$PzT_?352LKF< z#Ey>&pQjemD=rC>`)gibfAG(v9HvQ>lvGMe5{B9bm9h0A@#8D-A2h&_TwjFI`DW%z zRf;X%YbKs=Y-qsZ<=9GNh9!@Vit#B*c6WD!r|f8GX#D*1N6V}2pJFg{6S{qth%h=L zhFb2Q=DK!~*~ZGEt@U4?qJ%i8;MqJHeRlDqxPeAT%k}ol3k#8^Ceq7(|B4;BZqP25 zKDdy_I%1~{AP#6~Q5f%nPeD-cN?e{=hOxj?!MR>H470_&5y%8L0&daB-{ zAZ5@7%V$lj+cQf8T?E-}z^Wq~_H_oz1Voz_`o}SOB`;CdNrab1PYx{2J&LFXvMt);qe?KK$J1bkV zjv8XwRIWp6C|vjZkX}q>>Lor`MxBqfzpOQ=t=O+K4ypMuMVa*-%@1 zh6uU-;!ICNgS+jqWpQzkxU;ZC*V;PX8RM-pL?aoiIUBjLu4oI9IbSCLKt=YP%9=)e ztIm-ignr`{ZRC*9f}fUrHHW4a)tvrO4PD#Xn{oErr@<>EV|LEyQ-$xMimnhz4D~haLMnzIP2b6sL zxXyRm^Y`z=k^$vORPwMl4Qc_vNQj+$rZWoc!h!j3~ zc|Y-z;E<&#fyK-@7UzCrmzOVUy`5wSx~RpxJUxV+I~@Cv+grhqkTBV#ja};xuS(=4 z^TsVXcy!`Lw6(NMYoq$tS_A|r!z8zk9GvCCe1Fu*J5PafrlC8K{lKQ(qo0ItO5o?` zeu<6`zV^0Jyr+|pz5#RN6i*#C%C9e1GZh&*nXscHe2YB!l$%*79SLI!Dz4v0BHE0Y zQ|cH?p}I{HZv4X;5>vHoRy7@}ty+J8N`(P#ib}~|?$0MF8Q;TG&YWTv7CJSRQh$jY zoELU1D})}2&2Q^&m*t_FA_S`#N2ArwO{|2j##e|jp4RD6Bat_~@1z|zuu-LnE10ka z$6OrZce6L-b@(Z#<3q9i3p{J5Xny`1Lp62vLpQAPb%d4yI~6}R*R)?Q8Kouh{ynrg z8aT5y!g+b8?GC8HxWoO^WfYXs)a>S*8I}5NbN<`0ef)To6;{a%8}eh3QP=(b332un z71^2oM^Vb^xRtM%nQLZ4>BmebZYX^__SKZ zQ7ey|ltuXWv4)Bie+`FaXR2=Kyp(My3%*^fia7G~*Fm&Qx7r;W4Ys~-GgiBi(U{`{ z9^VJ7-kLI?M-pGcLg%em>P_;iYzxiud)lpNQ)l0eQCc+f%J}US{_G9DQb-E}_>_`k zkOM<5^5xfrKrwqZKGGOE_=Q1K&P+f4VDJb7$*;ob`1oXKbePTPH?vHJXK@&j?7`L9 z~ zh0u{j{2uQ?=~T5&tT~*lp`%%TZS2Np>Y_1&C70FNSz> zjY8Q>X{tM|@~{gL{WTXe7+u|9%-mDJC=Um^xp7ry8xm;qw@7i!u6Vfr1*?H+#GkTv zKN(~Ct`iMe61q9K_i;Xp`b};gxZ&Qqg|C#X$R6c1fDzG*^{ zqVyN?;JdsehjNB;GE6DAb2pJ2nx@QAfyXK+;Vzm!G8VDA(8 zo?HKwJ}1;|Uv&R8NAQEM6YYEJCW-+wi8xs&(~(diH$e;%5`5WBh}reEmWD=gjSyy7 ztIujrYs%d6WCF1RZ%Ser3&VTKet3AavkL=x4__0L|D@L4-Gs2A%DFjn1%>eai}i=U z!XnE*qoM}ye-zfvI+YJ=h}a%WFLBJ_AN9`L^8%enDh`!(MdGcD=OcZLRgY^;nogUH z(^eMIuM~j-Ax`e3L9#`MgW#-Bcaj~X(HIy&={`?r<{BJHZA1|Z*Xi_ks9^|mvI*g{ z3Dw&8MclSFZ*^ojB*{q7CCA_ykrPMi%7<-*j(`aq$s#Vvv`O*`k_O8K?WzK4=y_U4gc-16Kjvr-L@-4M&gqJvs zZ^RrMIZI1HS2u1_M@KL6RhRS8_5G_5G+s^3iX5j0I5EVGuym%40}8({5m;)>6kx$l za&>x`{n@UGm4IJf9-lEp>&J}}9cFkSNBobL(pYl3JTZfb8vDios4!xc`tpu_`^WOR zG`)zkp}l>Zc0Jq!p60f#zFji8pt}g(B@0WfNl9^~sFZ|3dq?s|ybbE1q_u$`nX^;=)e1wAB zIx)u>1h@fbqsSqof>Of7(H}jslG~Z6E+?I-RaqochIQf$Lna~K*Yk9=wEHqtt5*aS z&TmW^M=ikCkVHP3yNP$tM(rB!$$k5fC|#;^j6K84m7GODNQTtk*Hz8Ce3*V$)|;=_ z)EpnGcRfW}Wb43H=6w9G{VfAt7%1sauxCmYGPCaUB+nk?MB{Qosr#;IMC*c?<2eMz zcrzM)|JEK&Mk#l0@&5Rl@*gS5KB+LPRHPM_e=@Ootz7b2a|Kd$|JE>XhP|zIiF;GL zmS0EZ;eC5MfgxYU4+oEwiI`fJ0ujRn1 zZsvIWz9T1e>HONx&W3C71jpd;U{sk!^6|>!aW<<*GEr!@lVhKf?a$_)Qs3TQ`p=vV zw3U~vc1^vydVuY2`YOnF*Hh7;sgZoYqd^oEz0swuUcwj;w!#=%O}e~>c`YKOoKPE& zQc$xvq70gWotNgU!0~hZ+oKRSga3bA;)G*fn+b z8qbG5-_^!o*v+WwY8mid$A0+=$o*WdYe%P7=otFWIvT#gA5M-fG{Baux1NNnj|VC_ zzBjy;g)7ok=#c|u^x3VgQ8+lfygZvqo!n}n$dt1)JFvyz`MxQB5%;)?$l#w`kclTfT{#O0(!Fctz+8&ePI0I0du4{c! z{*J?-krKDFfZ7kxjm@Ysk!iP?!@Joe6sITZh8{;y`#q`i9PL9Ic07TO z()y^ypg)bW=8CZ^DR{JBYn@JY8XJZKl=jsg7VZBdI!u2|X%X=HcuceGRqD)68iqu% zhnr9`61#0JKo-;UiniA{-Ia&AVkcL&%R0MsZ1v_$i>$8+%K0yNhF^{-*;Ag9 z{xOYDS^M4$s^|BY2Y|lgJHVUxys=T87Ja6SA@DmLpHQzX1CjCvEc;NNnFib z)U;m9Q3^HKu|DwWecYR?vU=^BjADPuOhP_;-Q<$kehMW!eC;pOez&`X$!r zxAfQtQ)d5{nz0$Co;82R3RKN>^domo%N)S|ur_^kQ0Z?(VXzNgz4-gJn4_FN%~cT` z`Cms`W})-qnFlTLB7Tinwf{R1roXisw|D=J$s-j1Lkhh?~eF`)}hh>^%MMwT@ z!cL4=<*-Xg*fmbyZfS)c)~|96gIZcJp>OHJ9NYBF0*@Lz#-T31(yb}$bRFgAOHT5?uSl8dgfc_{U6*Z z0n>m7Kc>klOOB8H;!{*oQiK!tNVgc=P{X?l>#}Zj$OzcwDkiNRCjT0;a{`_cPJ{sG z@3QPGn`6}Ew6*2C`cLV9yHcG$-b1WvD{?8&k)^A|Ngz%{PfD(VSI;Ca&~-gI+8k$@^PccqHT{=& zvO0JLV%2-aQ>e}V+c$~rwQC{#1Br43p$Hl*{GGwg=0DSED;oxOxCCO&D*U;wx?12r zD1_TJ+9Zuv$Cl7XZJ2>Eay~Q2PfTFgHk1waXQUu3n8=!TF-6PC|Iq^EzW0^*G_Gb! z21*vpQZRSDF=v_V#rT^)^p5VKVlkp!u7Ci6JY>(_e!0nh8L+xW^k~Cxno9pZ%-hwZ ztN*v(LQZ8w3L(#e&dclfY4017&xfjFPQxx(&@lSno~;I-2r_vTPo+++UoT>rGE{&b zEde;V6e89P=+kuw+{yJl=m2fgL!5w?9_VJ*Pj3%>*W)bQ_BjM%_0BfP9u~ZCtVe(`4N~zxe^2T4Z>19S$;B!_ zX}t&oBaT1}1vea{qwzslid3e3JP;j2%D6fKMglyrZCmV3FIHHyaSHz_v;xr+*Y}0_-Kqk6s+9G6P>TtswI`49NtGV5JIE6+)(i@~L#3Hm2rDeX*+M~YpXD9 zulo*kA9vd(=9-H{*s(P3+Ky}1^X=i+-qa(aE>yN;obq|f{~7UO>h63mAuCwF0y z7XRw3N_@Dsf8XjI3QzSBDX;q-?sL8zzJh~uaKLAV$;im?_3Hrrz|Y~dL8A20XD1gT zB7_((v-t3OQbz{zRdchrljWn#-gpM~q>fjczj{&`=xjzbEQ8)Tf63sk?^6niHoop0 z-7A>nN57gKt}HJux%-{W1OUuOt4y!Ypy>hjGm#Nvt+D6sB;;UA_JHkh+h^pWzj_IU zo;X#(7={{042!3aCRmv?^i2)T_PQzs=8tubAD$#)Vv0h2VKmOxXDXLmu;;Bz8XeIs ze7Em*e+D6oP-eb=FG-tJ%<=N|OMPmW@Leu0md_GkiD2uhg&y~X5uZ{~z1_p@ z{hWiW!)r~fQ@$B9gB^}aJp=M$L>l-jaaBH+(a2Dktfxi`TOW6p^364%vZ&>BG*~j& z>h;-til4+$?}G&bR$P>Q^X{g-v9S09s!!otvd ze`PD1Qvi|8fRENd=nG2l3p{EC7pXRaj|G^Ajf^3h^Tg4O5mM=aZAcvLg*&Q|5AhNp^Z2JGI%^D6KDX_eEuz zA7k9iz*8YXFH!W7%D0bxx$B@={*@y~7+{jI?(ZbY3{OH+keGp=576@3EzGQgN1T!7 z!3^?Cq4wKq87{7$6Os(noD7!hc30zHm@_vYTkgqT5@=E0R$OAAIa}Eq*TbM{WqUK zpQE4v6dBPT?yuasu+uqLc2*#Vhhcmv^}z7lTV3JM7PmB_vM+B|-5*LHdEsr>cM{%c>_FddYB z3n{)pD732AoZY-oSD-jEbqwZkFkvB+e>Z!x9bVgGOq{19p`N!u>;tn_44LU z*P~v6fyaYe%fjy#9`0IXB4Sj`YG}|NE}U66ID@Wl=nC5(hdv+$mF%!(bjdo|i9cT8 z4!&pD`$5$?j&X0);a6pt6PcWPV`yUXO2m$vT{6_-J%{oz%#e5<6u5_&rTY18bcAAq zVntdmZZj9`pfdcJ#f#v1dxk%DuV(#jqhD3!^?2H~7%MpD{WNBoHK@UWyn|VS75{0) zMv^m6H&as8M`*`#kV$E(F1vw_uHGhzm&0j!;?$y}Vop)I>Z126>%d8sP)t_p3cmm< z2!HGHps{<)4M_4Rn2Og+0wP}FG|v2>nMc}0c(w5KeS*B-^ZyaC;ubNoCBqF=Ue<9W zKwoP>>EP55I-zP7l$k->F=_uFroI9ytFYPnrI7~d2I=lD0SW2mrAs=c8>G9WyQQQ- zq@}yNySt_SgWvuBd)H!7Z=LhRo|!#+=D~#Z`|YQhCIQ?>3DChP))J?@qEM|AHg8a4 z1__;mb~ri4Mh$DS&T(hQ=6 zPdpx1-heLTfn>sI+G#j@oMP(ldd+wNsVF>{T{IyfcR-r1rtR;aYE2$&cE0eNgYeN& zRVuMBVr5AT{zC{LKloZ{R^j)WA{)I=^|Gcc3Eg!FK!LA4m=>7^@qaGy zp;+isE2Y@+YR#9sB*yRWbWrqL?pAwH$z_~OseYTi66hk=yXw}j>}z5hDrsh>%OE*H ztA*rRY}Ct`;^nkNLE(a+L;^QhdZ$u^4RazZ`rzNLldgMBuzK(8hXD;O)D9?Fng?6k zsUdM}M=R3+9$Zvj8eH^Y!)+3f0`}qYFe+-W&pG&-DaI7WvD&9}>Z$TN3H5d`hf-iyZgAA0{rmqxX_Q2^C}S_`h+A?`y7`u&{b)haC>)9ic(zMQyF80kq{uHcVzm2)pY`48;@(e zUhfF1O6$mDkrO$?yI3}K=8OIfYEHGWULVoJN1(y^Hc!y9j`+)U(g=Tl4Gr=H zXnSm{qVO7Xa>0U1QK~smLABZ2EfwGai*Ta*>sOWFF(wmA$My@BZR=PS#r1^j1(7n&B4iP2m zb1hhjBvZwrqMFKc4tempR1!9ux)xDUen2INzW0L%V<{+vdxuQFHzwD%>|<4q1SX!$ z#0GwLcD2p{rh=<|$@}i7t`8u^6Pc}m6Mm4+7t^$>Ddob80*_F&PZ>Hf>}(=6 zz)-4%WD`yHngtbgnIL5g=e2r?T3uZDMes7nl6Ft>p!Zz-klNezQA{FD?U90H=jGv| zdwD2aCTU%(Ym$zi>xH!Vp!9|_JgjJtesXo9emcvEfNb5x1)t~>=Vg=5-SMi86Xp>f zWh4FR@bGctZEICN<9%wnD<09%q|OYL`-%0M;mLmJ&qF_W3oMVmPiJliTX+Fk5 zN=9XT4-l!zpkH6CEmS;>A`ccx*v2z2#xE@*x1p0A2bD)MJ$%$k;}_!1vj8DqKxO^VLTpG zFDa7`r>M5mAqg`OfZ|2Q^8-UPM!~eUOyn7uVhM*ibfrescks z3=3?MH|+I&nAMhmQ;rgP9kF{k5Pkw8)^fAf-0?M`hwb*`B|sEwVpE$sKTG<0&9t*i z{E$CKEMX4ow78zRf^C z{&@lvTpE-=J%5>PefGvJi6DM-kC6S08dvSYzUFN0s^UI?Kwj&mW&5Anm_~+2W#!bC zMS~X1F)hpA1k?Z&k`+uA)+_+1(O%t@>LFEjHuFJJJ~~p7cW9`B8}kr3=|*w8RN`Tp8V*&@cA0eb;OSxAn^BS`~P*nU4FQw?jRp zf^zTPG_s*+XWN;s+quO0;hyzK1;>`Qcbn{-Yh}ii%dW; z4@0IF2rLs|I>reQCh}%x<_Hh~z3qYd^u9=`Xq%Qf4|YBTo~SyUOri2aW%(kju^5?E za_=*peoH`}_+!erIvH`z3w(lhEjN?T{J~VLe5I%WFuqi~_EkwqpHO9p-DRR=E2je_ zR1O*yQ0IPUXn%`ncZ_!)Tb{N&nO9ww|L5!h3zP&Tf>B38!MGq9`zPCDymw{qQp? z+}#6laS0zjNJuI%{;8f3RK=gmDOl>mr&pj(KKd^Q&c64kj-~)fDlQ-ae09xO=p5!I-k(C8c0u4P&%>Ne( zXtk)T(!7}F1e+}qrw4K0jd`6FJ`0W@lPtNvac=*!49~LWp+LS5ebalIg7Gt^B^BI& z$482js=g(o{oCO3FP2%*DW+$6<(ZqWzJl4@E}-`}2J2XI1TIc~&YVyCCacb*A}}NN z!_tGn#0e9p@W6v2PS&Ibzy&(|e`B)+mH6l&Dy4>pPvma7R(rSdu)!GztXO5Mir2VD z;Za<6J~ZWte{PY$!+roT-0%>fU#5?@s(j8Pw?uCMQ)mprC}v|ZLI2NA*n??ldpvC! zdIK%KP*OBDI(c!{j1CFZJN{I12z`4gD0EdKGZkEf{E_Z7@8vu{<<>_?X5=?7MtMTd z=VV=#;_F%_2~dbqTHApAt{ipE_isp7{)ao?x-AMZAV{*L;0+e}p1^M2Z|1jOO<}_zfx1`aezx;6p-@VeO@z@S8W4l0oXNWi~Eb0t+%|7qa4e*uuQ< z>tQ01P3PD$FbSI2#fUuY5n@vwmNyIFyxT5;N=j=3&;hYy0d_A5?JejfQG!%_o!O<9s%o&+$v?tvZ>8VOhc?< z!v8yE@zw!t+%P1$JuG$*jCaezPz8dvn>P!H1yl>^%(RNVj)J&_ghpCsL_CM{_#6(glVs>0G0jjSZ4= zM@Kj^Nr+~G0RC*7C98=FgnsXr2(b-!6I`w{{Z1D(b3W4b#j{&j2Q9-oWA?{l>Xm5z_urM#>G_% zzRcfc|IG#7Pb~;O3<+?c!)U(3W^@)Z9`7JAuniZSPZxw1iqkd!ari0xwqK-nYL?0+ zLKIuA)qmfYjo8-SI=R3A+$j`U+2{8x_~=s(CWPBxu4VCze&OePq60_WIC9t{$QipoB$d{bq}HeLbH@fOzGz;ER5nsS~_faec`{W&8CP zpq>|%Dz7@b#r;R??5n?xphRB{k6cI??jhv;0wD+j4FVkkm%o|Bz6~91w2?IQNq8)a z?v)XmE1}y>0{48Voc4$pIR!z|we9RfjSjed>tG5hb+`&}8N#nz6^#GB0760Uw;R z?T<=Bwh&ExNpkXH!|^^Sc@$8sG%mssb`YoguRgu`1Ie`abt!{~e1vjVhQfF`~Quf3)Sym)U7e)nXG+Qb<&Q5?k34_vvAqk%qf2*2;z|Kpf= zm_M?J()I5C{5hhl3ph(sL3AmIb@rZ;-Y2pGS+?dnlVU8eTM?K6`!eEuJi?e0j%+`I9LULWd`$FApGlf2q0ix5ZUQvN$D^WctCzre;5$V zA_rRREg+1OYaT@EbHC*RHloSrd9kzjR*)Yn8kb0FCKW+4Yw_-wAOuKhNX_uhx?XDP z1O1R<)p~e&7$d8%Y9H`GW;s#@@qSlLS|XAD`bBU4W33dLAp z7tP_fEfge{5P}@SnhL>cC!2E)D=c!+X0#XGSxYJI4E^bkN8@(gxP1rtoBtY1d4!5w zT!RN|t-bfjEVeH`OeO!rg~?<=N}8ory44ZWhQGPQX9%+HIF)D6dfnn;dK!)pE_z>4 zF#}b?m~QPf9DmtWWOLFe1~Z{?b_L)BBoep!{Hc4g5Js2{Vas-(}(x$(gY?H=Jo&;biO}R#=0bO+Q(b-N5O{=R?sVLa_{#x51({ zF^RO-QGh;@?(=bZx_cwn(hQsCP56EKM+wf0Cw`la(`1VqgD5w3^i*+lb8#WZASM>R zS`}G~{t1Kt+IziSQ*bCGX>#4kj`1dR-sP1s;nBWUwsmhxs3<{MMaY!A9f(j|3^&csiG z|Jo*GO-Xqa)SW&E+h(7}xgCk_?3T+Td#Bs(b-?pwYP9FzOAlr5&EWE6Nc~lpP{+yr zI#>B*mX=vV(FdzQMs9o@y}~`slL0#aLr{qvl^s?bUv2W|3&0(bmgq$KGx!%lZOJAX zkkeV4p4JC2BrYY)!2qlVF_!v!b4ZZL<}ig4aTPp%78MZSa|!Yzg191jj=n;HjLS?+Bn=|5lbf!vgonIV`f4OddlIz$j#6R4zI)_u=7En0xymj+k>pT4waf?nr z_4xBwY0ejG4}6ep_-4(*knq@D_RGeYzQ;wG@8y@@mos8ea*?i`-Y<3tWlXAk$(zkr#sp>e}P z1H3)xBF>JIw7oJ)XW}UzCaxYK*7F-r-W+1c`5xgW+{B-nAENK>AVkJ`?t(~h-batV zT;4$aMsq2nP)7UIh8e`5F?}u{@ z6>1}c=5+!@3WfR}EYZV!q#}j+jf@YqO2OAlIOPf;*SF1)A#F$=pbqqx)Y<(@2_0J4 zqe?ghmM!}cffXLF8dXdSY%5GKEJ!!Q_X7_%PCv!e>8(}Q9I-EwIfv;%UfBH=9O{oW zQ`XeCT{j}y*d2*(&+EI_o5|^tKH<**v6sVQzce4LMH~02F_D+idV(RIwJJ?BAq0%2 zXmUQEI&49hqca0|3nAqGBg3+6Kkxx>e>DAweuz{?wKRzVj}mkgHnagRY4-l$I~ut! zJ%@f%JnL>%MWMourNkB}nKVvb(AB_~Dw&gZ&}QyTNP{i3T&TInYX%<=E`?c`zu(x5 zldD&=sntC!U<4W))eH$&k;M8P4Kk0yra`<+yGvk4xywto9{-W;Iun76NtLT?Mpzcf zXO@Agr!TMt2;PyCe)SgQ7+J3hVKOgiX$Cec2NUSJb8Dx9K0Cc~QxglF?8YF)v#SA& z){o^#YAT3KZ8O$-otxLb&y=O3PDE5R-+w(Z%>S-M7?ZQy1`pb_; zm;h#&fc)inMq{^;r@TSI>%8H%x5&5{uE@-6?dZ(2()Z^g(%w_4Tzaz8iz=TW2cIbC zu}oT4CgZRB%+4f&2upRnn%s#vRX#vIZ(XZAkR}SZ%C9j&^_OEvHYB3@}s(16aSHVGzKHZB$ft_ zv;bk*af%U<#6bCbdgX7^#v)2~i>boVQ26^64Z9`c^9Lr%Zs*T&J8R94!rvWDj7b%D z<0y4&z2QL6UNS?=vyE2VcC9W~vk~5Re{Gg3*BZPZV&dB`1!Wl6Kf^ace}~PWM*F(B z83NH^jj@J@Q3L%O1@Fcy+(5~1Dw0&R9~{oWY?f%!apbvv)#~fR;0{9Vk!uLRvOEl5l7QjI)zI`4B7=kA8NY+t28&y$k_11g{ z)6@sT;Znc8(F|-;@anB0Xx;HV7+=xZ9-P`@z-Di0{>PfBoJa^OnwTH&a6!`z(S(M> z{mEowd)kDrxubKysUhciegF;oL@&*^J01?FujL%LkfNyrSD+p^@w4FdxL8jyd0_0| z-O(J%q^MerOZ|N->g%l}kg8DqO(zG%6A+B=vcv_R>9mCm31Vy?G%Jg_)y7f|WCJ8GlwC($ zl@3oaD_xnYsw<-NCP*QTi_VHql8}BQrmHQin!BAK#mn^wL8E~H93SzDa|3NxSJwe4 zfZCGFIXDO`&v7Fc;^zlsBtaMh6g;v3-|=!P4Amd!X3YZOVCWt?tSr-?)VD^}13)Uhw4s<=%>Bc7ky8@ zW^=ek=K%IBT)vyr>0cB6m~2)6oirl_kDG?gOfMJTiL!GW_zW6o+J3y3lUk< zRCcsR{3;1}X;&Dj$6*uf#LcbwLIzF@1z*)H@MDwLh;h-;K*^1dWRRpeBI|?{?|z8l zfJ@&)k%8u?m5d-@OSHJk5(lSogMJEtr;3a(d|yY70D|~CY%I(;^sS5`cUKW?TeHmK zWH%8G_PP>07|kFb2UMH#g06Y%w{LO?`jSL9zHFk`#Tjrwn`~O;UIgb>nCg@ zY#5W0#XiV#bt_<|S){ldlK*3#p#dJ_D6r4=@my^QNcpSseTEukA1Gf?`3*-hR(0{i z=QP`vpq3`mk%fNP7!YwFb(FK{oq$1gb#>#thB7y-Z}cmLYEdv+&8ZRqZ1P=-1=kq< za15s_&62bBUnzCFc? zXNW4(PMp(f{h}M=-31e5-?7WfIQgJI01LAN zq@zB&(!tJq&I=YpW*-__5<(J5>L_JV3o&Kp);Ms^WET`{^?mip9&_zN2J&=6JDgZq zonLHu9cCL}ZAd^~8&j@7`xgXklk-&X%ElFiDjVC{R`+QTDuo7y5zOKU0l@q*8v76_6s;05H$ut)RgiG5M8MDuC}sY+y_>e9?i@FIfvYUcCR+B z;xxJN34h$8&-@4(VrALBmc`^C3Rn!Yq3q!g&~wnFe$tRrm+|5COoS-HSHtWpD=SAr z$ZmJrKPpRrBO{q!Rhw5`*+Jq4FhU=xsi>jYjnkXe*Mn&QnMFO#{@u$6^{INn=Y@eE z)w$KJtpETZAkWM|{P-_+iNuLi|Fkx1Ep2|ef(hshA@=CE)b$^A2)g>EE2!PbPZ+(E zZ8>yHpX?jok~T4MKfXlo%mr)?tLW|~PfA;*xSI-(DbUx4dFzJLQy>Ba5=p?LIBFhK zI>1lhkAA+lEZV>Ss)f9f^}vNW1LtJq+EWlJ{GUWmU^%@Rd2P(|b@Eu-bJqW4-@xvz zt*yZ3-y?S4M$d;T(Fx5tsOpG@f+tls6$4HKBU9@$<+6Ln1>=3>+l5d=s7f1(2K9q4 zBNZ!(m`bLwv3%Sqv~a>kH&u=i_*~UQt;Vanwf51*Q?jAPtSP^ef%6iTVv{E2+tGf+ zAW?$Z$KDxWeAk{&T>+9$l(KeQIJezPlpxHYHrd-CVi|j#Yw97Zjmp6ObBn#|(poW` z_woC=RKBBMBi07}CTe6xJ>L*%P3hNCGoHpiS~BI&%yS=Z#w#!<6rFtpBD@bG|Vt zv_YLC_DeDV5~^rjEWs)cNYwURK?j7v?=2Pb>uSX;FYTqjZB*UMRlaDob8!WPA@xq8 z*_T2j511CYHapADkY%8?npN59WJ?>#L+Juq4HT+(z_w5tY9??uMy{;lCph}Z7h+sH z?k(Lsd`k9+mwhiw!Y4hg*(+}Do%%(2J-K~>nALy zGQT{Ri)!iVTjBbBJ;PMCQ6e2lp*S@`QOn0|b<36@%N+RyJZ2%{sFFEBhNj$4QA@8y z5dj$z+;bq!ir3|)MUsuR^etY*tx$uXi_XY`W2n3~OMi+=&i-A*Wo*y5}}<*$H)1azylXGC3f z7C(d2K^Z1Fl3c_FOOazJe1@Ucmy9be;Z};IAR%dZuo@d&d%{WGfG!|GRS+heG`7eH z-`a>3;-QpRc&96p837#oeLBx{$MH2mmYa00HZr`Js;ASZ^)50+F7pgJ)uc!Y1WJo8 zMM?8Zr5L3E0#^w$dkq}tup~qXWv~E8Imz*yn4#>>E|ZX%L3}C$1o<|{G_;9o{|{XG zeL&&C717*WV9H!*I27+I*e;aQ>a3L=np-qhFGDEHmcRoK0Y-L)7+|2P<-rS56pw5i z&}fu#w)I%h_yCQY-BTVO3IZ}ZsXZkr=2ufr17C7o*+h|Yq!f=#Tu);Igj}pu;XtDX z_6Se%R1Uw65O#WP7mSOc4Kjx;$2}#UHbc31D1qb z*~T~=sWf^5bF_$su+JRd5kFF#C2cZ~_NhaiSEROfkcCL$>G1p_XRWrmd1_x%9hUwt zbt_6z%p|DhQn^Hx2D3)1f{~F?qE9&&jVz&SYRZU>>ev((WPyb)5{C=f!e7o`Zmm(w z;d{UWHiQ1N{@v|Xmk|?P)K40=GV_Mxvqs|;Ri%j%sqhsvIvbrVAY3zi&isfjADV&E zWq1*hk8gabb`qbu+LH_qF-h0P{^RwUh%*yPyebu(8KJvCXJfnRe0t^j7o9ArGmfQA z@JBXJ{uqT4j=hQaoYSv6%cdU5BaS2_bkq3zNrz`{>iQ>AzoEgjR%^46X(f91`Awb*e|W_oABnKN}aaj-0YrpyXE;(Q_S z9k}x&R=`w>lG4n_%??}<6tvC) zzfwTqT+F}1I?uDQ}bGd?DSc;8Cwz`@nuiF!!%04Tnr zRIyN|St4AAxdAP72aFaeNQ%&Qg<)-)ac&;xcd$B41D8X#?!&3fp_?jvpeyW6PK^5r z1{NJSewFp&uV0e7Kg7K3@l^BoijNi>LD-yJZm*_Iy}dryMSSU^avftRC7I4e*d%Hb zx}h@#PTaEuL_HYrfhMr}2Qw08vF!8n9o*c~LVXYI%(akF#KZ_&8-}gDz2Wqfk&+kZ zRYDw$CMYNMUS=V(+368^9Dd~j?Q6bgt#?2M80I5Lw?h?kOuWa5aEf=>7y54|5U@1a zSknbZLKu&48jt9$g8SC+rs0ZvQo$K=VHe<=L3Inq1l6OwnARH+92R0MJCs8_EKp8u zbh~fFvZl(ciWPUQXJwKKuY(sRCy@mC=6Tipg0ySvV4~8+^>?4IUO$P)M^W50ign~! zKZl96rmy+tU$*-sMij9YZva<5GRA*IgG{3#5FjaWeL(~1*Y(GCiOS9@ZZK7|u)m72 zpS|>{Yfeoq#0do$0HLozo6h4s`oe&Sj|i)suZXhN4?Z_Ema^gK4>$KHSyJx+Zqe6{ zh*h>Vyh(lD+@SaUct{~ZbDe-B&LfN=@-Jqfj!P)OlzM*fpkbKgr_k(c zC_D;cbCIs)UF~K}T|6#SW#-A@_oEd;kdcMgR^88tPrlK^VHE2pALFv)Hl3@qJqj-8 zHf1oNTc3iXYDT)z3@E-mzZV_PHdzQRa-fO)4N5>7(F39^<$^!X*_5oHvM>P7mcC(t zNdHtTu_hGUbvb{UascF^blcgp2RsrOK?*Ep zgDoXRXWW?ZFdD9D#gWJwS?X>nO%TY;{!7`mW7W6j2@5P>_TJ-dBiZi9L;bu;PK2Pr z(3v9xjHApBX=c|%XlSAR#=B438Ep=NYt@cg^P|xiSayA;-TY$YWx1aW8|@PIP<_6_ za=J8>$p~?jA3^3>5TZl`<1ZR|KgUweZfO{9=>I0<0}LCG zaU{b;zv1M#ZRJr5kb*QmXi|gFsQcXWE~T`SKj5z_5+#audf|hGh+fCQHzA?)Np?Cw z_AIT8ub31WzNI9)hXz{anpM&;VpHS^v&)#=#Ix607t=;8Mocv^zpV1W5q^&)+M$Ts z2pi|3*^Vnd9k?DT{RM>+e}thc$&^^WKcD>@`@U`L??dKLfvnC0p3j6WC`IoC*y+7c zOtbGGf90ud0?t_=!$(g)*BK?wk{$p)`@i(z&C89ejX8IE$(zPA{fda2VvQG3g95?# zRj)?vVH16YReuldEen6zz*;8C?S-81xi~~Z(pufhO6BV5_PNV?{($1w)dAT!*+SX%csH-tRA}8Pa)Feymlgn#Rb< z5qwgRWSLzi*pw)gX4=w(i%%_fIwq>JriN6~Qz(P+{UUS`kRLbK8(8g1mZj&&5(9vz z_J&hG0g?-(5g{&OD3MSKz>a>J*_LtJxtWtkxn6VK)4d{yxw#86GtF>$#7b|(tlMj! zUtUmK)*GKEK-g*DM}du4LL~6?+(S7%Wm@p*0@3PlCAD?c4nt>-A zAQ|6tLP-#@#2n&Kae{(epGm_ zT3-kFsC(d10zy3Sc{0Dkdo;N*ZyWZ28IC&-Md#2$)~rr8>c9NM48laB@WTorlo5j# zvBQvns@#@TsAdn0l&oOXs1!d)l7C;luOTPAEs3bquI zX}Y~lN0hc&&yYgLShXRK{f&tUfD|qN^&CdsemAB6oR@QP;63K^e9;$<`to-xMo{-! z&*7`dHgG=**v}8iUR@A=l0dxK9rhk3>a7DRz&t6B^RNg#bllzgJT~DVE@tyPUHhM% z2+r{f?*Tu(*FA9r^2nt@EJ6f~JZy0Tz_XHpT95FtCqRWk7yYm_)_T31`^R-UyUlsu z$nbmQgTCG}2|f|MkUQ&Whq0MFVtRJo5oE7fkGVLI6YNi39o^hVlJ8-5>rXk~oz6u@gHs$+jXK5xR8IpW(|VtjfkLzhWJoId=c^pn!Au+hu<(}yg6 zp4ZSwc1+Eu(1i?3E?St4o%R&E!}O$rcL4JGOLv0Meu0~EvZ~1er-fEW)DKp{u*6tb zge_zPS_@$7^QGzN-@8340_>PH>i;|F*Z!3uA??IBd;y z_I5P&j?Yhz>ATafJ3oh@o@Du+kLh_Bzb^Hiz3x9YGYbr8yuLhw&ToVtho5?1uZAPW zS}$i$xb>c!?^gNRpDRfog#$ipDG2>tf9hSG%J+R`R%qm$^F0}A*S3!b*0F(%_6UsY z?0TwM*1_p%+eR<}(>+oC#HymXvEznKy(9O@;KbrG63Ar<#`W%m%Sypa!)hYclaG7V zl^WV>l}#Z!v{dL$n(C_7GY~>t1qh9kDQBl>cq8b%Klr^B9gjT2@8x8iNLdt0AW5rnk=+sRHo{e8gUu{hzW@x0LWnvrk0)dj!s3V zAzyz98hkj-UjYBAxcp1R)1NAPuYtY+voS`}m~x5PWj8*mYbuVdtDhj9c32`w9EhOl z9U>@-H=GF51%`NC>jCpr-eSN456X2Xs(cDIVvMTqGEN|AwYlrFO+Ny+J?%~a`qTD8 zRu%!Sn`GaolC0Y2OF*-h@M@$Yd0zQ)uX2UDI*l&Cik9YU-8`d4S{tGV?hpsU=v z`&DFSbVb7IYyX12=PE|xW27L|vMD*7U0~uScjK=u^mOrwIkafY4Rgx^6JCY2+&P!+ zEXmi0qOP^U=}ai0OHorC!(2V3C-CQG68!5^Vk1NjDYDy@)LOgH)#j5vi{09gUE$<{ zfw6%PzP*#d{SJ5ZeEIG-j`hr~;^1B?g&0}P0*D^hi!n}YUwUi>OcKH<>d!t$MOF>h zPSDS7e_a21o;DS`r5_SB*q!~XylY;8Iq`LTeePWR^YKKkqT==TUKm^QGBqaAO{|k6dlTxE^S@@^CqFmCdNoF(Ne9plS>?LB<{L#uX0OXATJ z{)UcdjLdsGpN8%at9pn1@ll|2pn{CiwnY;cNHLK{9&yzb5a7$a^yi^{eBX}?3W!hi zRV6Y>!*nkJv@7(?iJ!gC14uHmVSy7t0nRU5w+1`{5p$&D*|D`ajeh-$h^uU8xzzjI2E;){LJxw86V{3)~(%He%)&Z8gA!S z{~TP!5J_2vX>5@>mD?yQxk#1yjzT*M5lQY~pGjm2#Mos?YS%28 zT|mGk4$FUd-|I1uCqf=DzyJx_Td$d*ww z10ljA)R-a}*HPWeWx*La!@&&?Xl=QIAQ6itlQ>vtCCLAL`J$apnGNLxv2nkg9r?*W z=#J=v6*~F8(P_Y&wcId~-R3ywpEt2>?gCE6`_CBxNMEnMeu4!^qNzV9$OkTn9KV{T z2658~coJz?X;7b$;}ZAo9(^AheLa0-WMqbbIKKc;sQ=1gb_MSCDzgO=rm+l1=-&0r z+t7cfq?MugA4L zCnRkPZ~Z5$qVJO^j-z~4N$$C!m@oZvrIjl|sM4Tj-#js6TJj4`5k2gjiH zSHqlMj5?lTl;LNjrIpoS*?%QC$!?7%{Z?wsfgHy16$KlH%J@EA@A>58$l133L+HYp z3Hw0P7YYoxh(C}3^L5*<*H8XuL~!yZv!)9YjD-tT3@93c60D`)aywvTc!PL6NO5f4ypHmyz=mF=h4t1aWv+0hQrDg2X5!M2P*{&-jCf0+g#} z%1pP8dXf^(Au27#^o%|Dn>}Gj7kPphz;BIX()WW};f?0CvF+F!uX?$)Sjl5)f1hWu zyvPtW5_$k#imxfFc(kd?~KYz&h7!_3y^J@nBx1YviohCkd z=a(-ZnO9<&aG>@}lP}ZBsMzlR`n6SnFL{8^(%@{F$3P6L4aQ&H+zN+&ujF=9kfbjP z(B2ZL#nK-ULklPEvWZcC0W})QJhZ-?WTI?{)V-q`iXcA^?q=J7=@pul-{8@`CEXik zYS3_VLPKHdvoHzg>v1fX0HXkx%c*r3LzcjYL#q_&qQ<_U^1b%l1ADNVj(YZ*hOrYo zkTmcs6%&X`^OJ(Kvfi)DSZkhUsLhFiU>OCA$$cy|Aq)%kmqtg3s2fzRC?{XATA3O< z%gzcTB7v`%;}D5k4_9w%j|qC8GPsdmeB&2kW07>YA{+PX6YT|Iy2-3k-!lr8jJ=0) zi^Jz^xe=Q#rxnKI}nGvrl((KYEU_>nd!wGbsc}h5EJ6kGzFRH06qB3 zR6S^E7=SB+;_E3DM}@J5oNZ@CrT!pspm3$-t#IW{yTm6d0dw<*^DlvAA*$_8rZY|p z8$y<27*L)1`kXEY@HC2=CZn^lp8_+Y4+v>6kPDbi1dAamTlpt3kCO@p)9|tJ@fLoQ zO<|M+$GFsZXts%mbvtzfdm?``SZlz_EF38V5b>=IFob`f>aaD<*ejM2Ws7)zzZqn_ zs48m@%VwtR?6t4kTJLCXU0OVywsrGyQ*qC9&RMEmD3_Xzhwp=rwe;zWKLqX*Da*^n!{0sC+97DQoGGcHIQTlJKWd{-6@s91rV~ zzWnC`DX;nKI*dEfTZPY`GoZpp3JehR?_@Eyxs&~BIj@($%n6?_KBywq9_oz10A;}J zWfspd+00WO@=!WVeX~S!{a?R4M{}7Wn<(HD_3Us82s{OlJOQ11uo*CaYGwE@A;=MP zVRUA|Ca^|Q)B~GEZH&mY=y05y3Ao)gzKYieNd{z2*(pNb<9M14^93-mpSMz!a(zX&IZgt!b2vM$bn`NaCTC6G8U8GfWg?C1yr6m6 z3{6^V$3{oX?fp#hRc5#0>dr1n(yUt?vzD$!DCdjX@(d-nN6N}D146TRdi-cE18a5w zgkR}Ljv%On&7k~6dri>r%uNlQwcy#FD;^814Mbb5Seo>PHHIhpvfvUcwj zKpzzQ)P0&oRWi{9OVy)_#7Wd^D$e6?6xY_S6&>~ zZkIo;2h@BxKg^p6n&Bp0BU;S901nz;5W*^moMQ+oWL0b{%Gr66uDrDt+M=+kz{o z^7?Rqp_l#sxY=?o(8;@adUFdKh7j8zl1!8%2#hSRZ~*uBI@cmj9b$C1>x1FM{)Eui zLY<>LY4A~qbnq$csbftl^j8!iI7cvIV{rcufcG6hnw{6|aPG{`BJV7M)!W;wrQJpn zK-n#%)BBR+%3O2#yScgjaper4nZFn!VZIY^X)8<(0*nwIU=U1VpvvNyn+%i+3KNs)Nu{7-kL?eo_DOtHATiQtkVaU!qe}<-A8|>gML(ig z)Y4AF1dcxQX}d~s1gUp0Q-f~~869=lYW#a`0AFx<&%hL>244aL5PsB34_D5vBcYMSb=nic z{86ba0|CjM&mEuWp=VKhN!LyMBUINWVP5WSPNGyi7@{Dkn;jCKIvFRQnrdpUUu#|W zCY*`({MXwn4_6;=@6Nf73Esu8)5S0T=vgF`scEURrmSYK$;{|B-s#UWpNx(%4#tnC2`O+QAvv2o+HXnQK_q3fMO=6nTVtp{TKf`DCS^GvE*!j{mZxU+j~fm{ z$ERqbqkq&OLyJJ?Tl~FLpnn~L6PR=U?8DINq}WNiOR$&@Yi;pHX_j27jJIobi9cJ{=>lG!x2_oBVWF#>R9Ab5on#yfo0!Y=|_`$;#S-j>9Cc%TKzB<`U*&M}I zwCoICo`=91YoybX;djI;1bR6cC?*d2f!I_1PW;xOv=@5>(<2)+Gnu$jGDc;KYBDRL z_Og~+w8%sp#l7xfmuJabNYo_FW}mwt_h|7je!t(;ZI!T#z#ZE^dzoCAs{9Ts7t1uh zC0rrk(q5l0^VjWlkW$R=Ms72~RogB@)ib87^I%~Lw5$Ibi{q)?P1IWRfvLfD(C%fK zSU!%*o|6z)S>L<#Qg~$#ywsoC@##FxZAvS@SgFNLywzJv4#(Vd#C0Z7GO!oT_Ut?N zjZ}>Piu9eGUX=|FsKa4nfLm9i({9z8>uZGzQHt;SU*%{6vptREe577Wx%cx-=E!2S zgGr>s(1jnZ`ZenM&}@{@D*~<{u&-9Avpi2k*35)%{k2_e%bgiC0x;38H+R(+(BB`B z-$a%ATMQ=UY+|6Lzy`L=W4}L26Bh_IB+-P06}&WVgdjgYQ27Zf$C8?FCurrnD>OFi zGT;m;V=o-YcHx*=uEGjc0VYukA)W-*aE@h^~DKQ+8yl1V^lKerKdv@gZMn>wGU< zs1#E+K!$IpLJ|UM4;{dU+Tq)A#@vpde1P{H(||*6t6ySUtnuj<^%zHh#IM>*KVIF( zHAZxnOs_s0-xOV^hP&$-9B8q@N$$!?OK-yyVE47 zzsC|glSZpLZ+)08GS%38N$}KXG>6gXO=Zz%e^(-^4rafTVHA97&HVdjgH$;xc%j`J zF%|~Fjr>i>(bq``C=9+LQUtAKdqG7iH&n1`v)?}W5=6|1hCk%my2l1w|k2iBP+5LD(oCrsYOJ0_V}Og1aWG+Ek)R%=h1pOG?Hl z(fO8aIb5SHe3y*-m-_O#JKSFJd3+pQ!fM|v#LA%xEf!34^jqJy(a}-u{_6D@^WCeY z{S*`XI&^|?c^JJ6)A ziRtyrDE(R|>71-)cl@`J-LVY)NEM-(VaxjgyLgXu@|QkMblH;ET1kY4*J_-}@I2G~mN| zHCT#xpuQT{9Xb;q3qN6oFEo;1jSZx5F%hnH=k*&__q#jaHeg(Faq2#(ht55)>&FVx zTt83=<`=02jduffzDY=k>!?XS&ExbNT$Ln(k9NN<32v48GFzmD_Cs>$&UP!l=;PhC z441KN_Ugr@ z2nu>p7;@_432fvGN5x6Yv2m2|-K_O#H1aZ{5=0<+>GrdAU}I-`jMFs>yodRjmk{sZ z)0Y}O1>pyOSSX4lQ`oj-Qre?Qu^pEz>iJHz{vigTl9PtS*YZ5YWQYpho6w*o5JZ6| zEYxBMbeo(f=nh`^BB|_dNh8xQeWg=8;~0fXFzHhNBLx&unTOB zB)`LS-{=dJhE2J?xb*=>PRE7A{f$&t;DA~kBSpCz6>#Tr)ccm^*$S)pers}Mor(7t z5Z&~gg_Abd>GJm=Zke?3UMPi=UU(vM0W}B)`TOB`BuZgNOq-H-0#15T^7!z*?7KTIEIiK#j18Z`rY*a*8;2_>FH+;8Jo73aZL6x zR94sBr<{6FJRsMb{CSXk&eFcRy0oNozyL*@;?Vcq?g!MTvw_3FSLnAKmZ~k&No*1W z1Co@;(-F@D{vuSKv+6i=63&_=AYy^@Ch3LdpzVOTe?a0I8obKM#`dewQ*0Og3t67pn3wzL(E_9er(Nh)oW)KmVFXLJCJJRs|kXZt2#<@DWdbCr=ATfeJh0 zEe)Zrp|H|Gp4x{r+m9iH3=PT1pGE1qwZ6TBC6?dImc50{A#m2Q{J)-LR2!*_*krDB zR;$}W)U#r&`oEpE@5&27RzXmK2d&j`9Lii|@9QgkM@elMi5lw)>o{x>RsBKBhI()C zvgu>s`}m+9D|)-LjP>%o@%mH!==HVNztm%q;xV&|FFFh0)Q?_Yh<@rp5HvVc`eiNT zy!nS-^lpINZ%?jczPEX!s2pqALx)CVu9*+8O3qqg6NJ%yL4m;VG=2+YV}Kylg3ZPG zjGf*)T*>6Sl&YX{!lZw+htD(!w-#J>K~Xi+3M6yxJnn8_4Ia9UL^ZskSqa}Se9Zafyos|h9688{u@^A@==w3BE&)c@;qJ}XEG zjzLf%FZ|b$@H`sr4wW~e^xs9ixW@PjI;Zu1u0#VZ37W|T-_H4s)*$`;iqXotj7(^O0YUn09_Q2kW z5pcVp^UNt#%H@?ukXp7n*5lECSJPgmVX^Ue z4a}if$af@j>!2Y_0y`R5F)AtEz%W^;%mJyQYhEA{3^_q?&b|HgPuJ!XCVJJs|St3RTT%UEM47lWDMOPP2_ zBax-ow$NKXQO|oczz&z;S{;$^Q@;Nz>ewHv(;x)l>|={9bA{^Gp}#p*V}B}-OdKcy zjCEzIWXmk|v`T9c3^Fx4oy0fR!~#Q(!v5;%4Jk&dwNB$LUz^8JNCJ^UByFbjq<~(2 zy2E@nJ|QJ#AEC#=Rpv?i1|sP8!ofGd&@{h*Hhq9V5hJ!9-1oKWW1gD7|5zJ&=B%`M zpmK*Mi~7AHMn8$TMRkxXZ-BL>ubqV)!&=$iiBqrN3qAW~E=8ntkjF_Abhb)2U;cVs z18lHeez(m)52)HFJI4eLVUdR^4pMO#;=}JY?_r z?3OeR#7bN5*U_pfH>&nMVs*2NU_eyZytR@4!2Gl%3R8L1GZX}=KxX3Pt2-hpVR)?$ zi$`r6n{U>!$GFJ%os0-xd^(=osj~*=P^f6CanIGBDSKoSEztSG@zp@&e>+j%&T<=-Y#i$|HW&kKCdXNjxc1}BT|g_n8HKo(MU6!74Kl^*un zZs$*AXLhEK_0y8>hxo@?pXfqa&4bYBQO#ee+uH{YZH<7ZPO4^GIpuR`wwE{w)0W{x z!V!MCoT`mmk3zeLYt7{C*#}E%Off7$wMvRx-^=aZq>BpWzhL* zBh&)V%}BClMg4~E%5{E8BKznRPQJrofrFz+hV>L~JUfF1}3>HK7 zI*Ze{I#&Y$B?g{iAIu36o<^{87J!NdkkyBc+*aR<>)5w=Q(IAf_G4!Qmy4r>pad1$ z<#FNc(OTX<1qoO&jy2{KW}e8G(#9eny#CMC6!(XZi1i5ay;(+~#wJ7XE4V-LSi<`< zN%BKXa(;p_z$NWUr#|Dxo@d1iuQsW3)jl2=F5{oz}euE`0xtf5+C$w_9i#$+ocwYg{bhL_?PMD+*KPp%fuY-_Iz8Tljet_ zn`L1OvG%;?qp{~*kD5DUFqcl3+j1XIz+L-On^>~SV}HL0XUZtz(@MJR0(Wz$|K*d1 z-qxQtz+mc_&b|`}A==s!=udebe-{wSwt*pjH36LtZuPRvsFy`M*4taq(GP_Kx`i`6dPr(*Bes|@f z_pRaXw)#2;TEMrTk<5heL3wNwDGfDfUUz3TVMZ#30fGF{d@@Y`MB5BwrEp{Q?jZK2*RKc3;CN~_;3JSax75Qs5M zV)V+mLf2~^upCZ~wG^x%v&!xR?wuyz`{AJjx+02j=3eNQ@sXa`z!bkUP<-0kgC$^I zW1s;=u5og1_L>qmuN&(nMIcPf;Gce<{T-k@W(tcGb}8C+_=r`Xu_WN-hHa9z8&-ax zh@oV=zxVTh`GahT1lDR5Gs)?kK*CTkvMmD8(L!-gH!>e)ViHnCMS}aDNDGsqEGHD% zAIsjlcZHBMyVx~wQi^H*JI@!yFu(D&eo5rk?ZZS^!nHa~BiVxrgGww6D~0c1G71fc zY(i3<(Ec^Sq$;_yGo)3$18iUx7YQ%D?X?IlK$0A)_57)MXTL_%P(aRRDNKeY#F;cS zBw~S;^CcCBkMA+GAYvawIB=Ryg|})PT{0i1g=aN$tCf_jK)_5W*iEjJ0e%p;r3wxR zdvg%XA%(}E&6+rA(Voht0+mnlvPO-rBSsWP%eo(Ai!U1~`$u{B1867m$0qgI3>t`hI`0$q1)&C)-b z!5G%Y6*IAsk!ZB<199(PNMr&>T7As8I!E#(5>g7Cr)GuIf%ltp=%fu$4idN3U7Q>! znHLh*5OJHTqpJ10BtR`UXHp;UWdvipkFYaZoQONIAT6#Ti67OwiZyIVR1YYS5FBjX zBzDAk7MN{rlkD)o6jAx8cEdD55;oQ3AipCk2|@-^0g4`;PYc+fkcFLl?cjor88xR* zHsE*2KwMep7E#BLlQBKz#U!Zqd+(GZ_J@^3w5?qOFI24Dk)(rI=L^MM`3<353Uszl z8_8LUK_Q8Mk?$wyyCqtGsM#@tA@h6Q(rYr!A4U>2LN1g1nPcuojv3b&O!0w|$>{7J z>+f`mf+hCX5-`2b=IV$g)96RlODt7azfpXftpdSDY|)o(e0kco;z=zMe{;MnTXsFr zS-3CX^L+UaYfd1l(3vO3jKP2o_B5Rs%~%eOuDdH4f-LqOjL!_tDacD6P1(Cl)*!*9 zzWfpa3WG=@OXzdv6i%Qk?3Apl)JeW&Twd%bxx4${C(*Ehb?h6S#0M#p;3pQDVHo^I zEbMgZOc26u0>LUvLSZ9+fUR5q7Itd~mvzoiVT11ObFpw3glibkk`Y@EldZO!8DXh! z2P+UICE-=XZ1CrJK2`8Pj3t3=rA*$CC{C#)c<07%i zDQ3H^CQ06Nwo@s>z1nD;b8!pZTb8Wh2}|j;>>7h=bf|Ju&<_Zrs5?D86}tSMno%7E z90)#WuGGr0EW00KN}Tz&%YAS2-PWM1`MMP9={KO6m7a!*;dr#Z4R6icphz+d#e)(d z0%n)(B_=j>g#&pU`ipVGUM*~kSikvfSqw?)`J_NDPSoC-ut zK5UF_OB{YmEO=^lCTi_ZggyGkC>!V3zA4qF_3`vAQ*p>x&+$9nFexZHQutM>dm91U zqO~I2?c2mEn~7+joA%rNz<6QLu~K&2_XWy=*Yb%ibi}wo^2TJbq=2Gw z?z2+(ZFo=I>mS-qgT>1)@aEt&wit*(ZS60?2MV|!n{ojY$cFD~9L-{i_z(DHc~i(w z(UeKJdTVOnVLQK4d9wZ$Ki?YsmzQYCF+P^*gWvWF%Ik)kJ zhDOrT2HS=tl<{9<@P4j=hy51^fJ+jBbF9*E0rqt>;nV5fa#G1*-gDsnCpa#tLHxAe zF|`izc#)8BX>M>v`mzM6g$BId0J3IPBDfQG7CTK(K6yK1C0FE&j9)Q{>sCSD$eq;FE~AA?L^$^BTwCOK0*3pFJ}`-V|Joo@cG~8 zx56U>5A_cG^-m@Bm>@BAlb2o3)%l*s>*5uGaWWL7)|{N}zS3t9M>{8w!TsI|i+&`C z=ul5!+7IyTU2)NEvxu(B`|?|gQSXN-GDaXEy14PTx{0?M5|hDkAaX;w4G!k4tCsA7 z0a-@L*pUB`O=g?4p`SsMnV$Hp_j}b!>hy~cfumU?YG3u`a+QIZGTkHB3l<@#TA zJU;h2ZwZdI3$c_`ix-vYn9c-|bBUbFjewF^DYTa=PuQ3zh2n&jTCYd*`VKVaoN z4s^%+ZLd|Cs2`!Zw`IUX=CqGj(hety|5%CHQ9;5z`n7ezYcD{^8gT#Iyi@xi;`g$A zuw9Pd6!4IGmkqPlMy^wua|{PrJ!o|3Po;H7gg{Mg2LjoLXA+nu(ZZM09{!dHJxx_) z6|cB+OC6p2iw$+3=mXgA)yKdpDtlWT&^QS}Y~q&4s9F?u$Z9Ys0eypO?4ACy5aMsi zo<2gZcXhpz!2yk`4Aks6ovwjGh+%1dbZ7h9^_9%jkAxw<^OGDt{cPcL@E{hTk19?J zo6QHbxIaX;LP|RbOJ%dj+3ioyM>_WUOJA(5gbjY7iN9p-eA$t0J2TY!Rj9JGlZxx+Ur@UT;*0HKDtg`H=+OK<-(~L*XhM(VwPIgoH z%2Oh0hyv2VofxuA7!lZ)R2546@t(+{l~axc$S~aD!mLS?#+q1`uoB{i3!e~lKAVSK zg`%3tp=FH-w@cxf$tG7Qj}Hyp_WlC{p(8XWy@1WncTtU%7&b^Bovm7!p*f!gB{f&Z z@~|s8tQrs~CQgS%JQxaVbGSXC-n%5j+gjpKHg#6(d7HOC zj947$2|L>re0$PA6bA^am~s~O7Vk9~rIY6k8PTQ^3$q+h14~@KiOKz*A127PPs&D( z#f7K6V6CUY`>KI1A4}JvFL?K#+lOcdjoq1~{iCVDuMN;IJ+MQ1BL+Xm+hAryOZ^C+ z<8KVXR0wnCEHrvQ49J{iCI!~JbLgrb&4i}B7JdF^53498J!T7*pVI6&&Xp&sZRhIy zyXm=XJU~}90IF0xPdq$Ky*Kd2mb1pbyX)8W$TPL zz<+V#AVUq_<_hnM)QpjT+6XfrsC|&@B#H!V)VqRzvBKnK&P*T>_(Pt!$s1TmLRt_| zk7LE;^HO%Uz8K`|Nb`1fd;zsieigbGx@<_tS2RkaX4eH=1yd`>j4lz!X_Yw2p9Xbk zQ(h!P2^-=xl4!qf+dh^AQ7BAVhUI^7CLjveD0@Fz5?K-kX`c;bA)tZ%N^NMT^1HXE ziSA#GGO8FzbsbnIq&k~uGJhdR@^D@LEgof$?m}(BE8^W2feH=yS@8GG< zpRI`c{WUpdm5p4t3LJ7sRKcf&LQx4-tmQ-_otB^qldt-u$6y zt1)7Z=ZB&P0;C1TyPznEV}9oLLQwobK`X*np;s4GeGeAutLE&hpW^_V$Jm z!hq=euFg<|YeZmxkSoomjXkpJ54jv@KdZ{WBlsYrl9lPdIKG=v;3B>&!PmaK3!NI8 zR|1mg%J_lo?fAY>RfpIc!;Zt>p(<=yzLzQq znuS+^tOAtyQa;U_b?_SOtpS!v%#0RS40%;3lH|r00bw_HvFRn6E)jg>5gZnc-M`=t zi}SFQG$3t8Wie)C79$k1N+4GO5=T z>}L@&uMEcDf8_6*9g@;m7M5IP7TXa*bbZ0|lMn?Xqc=hdu10_)pie7yJ0 z^32NhdUpP7sab^z#O(Y`J*^8nTHiP8VTYTW_q|6rwQA5nZedzaG8)!&DP8~Qk;`#% znivPI>`IPR?Q4ce?i7RQNF(>wDx2}^EI6YGI6Lh;O@TeL72J5D(8BpeOZpk+*>G9gdr99mUFFB5?O}Z)mtTvjA>CVBVR2Cmm1xRQ#E6m&Ao_M zXsvU!!P2?|f<&Za)kpY0XU;z@>evvl7DrnD&NxxK-#O)dsdq#Bw}1EqOoIu^zdF(r z8E9@ zo9cHg0sVP1gYL^`@}m}C&Xm6c;5Dj*cuzbGR-NSObTUQOEHp?wbPy8-Mz&j?*HU&@ z{?91!JqwsNZO;Jwnt(750bw7daI24rlFZW&7&%QRBOx8((+{BBUdjN?R62%5<=Lx~_Ndvu9B?AAFxZ`39mx#~%sQk! zRh)oUKsLC{qJ5DVx3!#RQ;P8p;}rL-nFz>=8!!4;Gra|xv`K-ej35x10*~YoxuzJo zptfaSMM624GRX)A5H@(#e7{J5eI(?)o#3Kc>)leyW5B4Q!p$GsG+l#5NBO|cd&_w! z(T?Od>l|)JO-U!ARY|?uX=AE6Ts8>6C9c2g`U`3?O&iUx_hZ93*3;fv3GwI2VcxQ8 z&9RV37#ATzTp)6F8LRcOg|g)mTHhQ}a-!A3Ww!{t<3BN$+tgWd;za*i_X0^6dx$?S z62gJ&s-@)K**$C0BPnUteAvWXd&*sR}=CKFWKrFLT2O;zkS`>j>X{VUIA)d1-gs z!v6vx0Ap4^`?B3vNAR1c@!)-(9RLtm#~pN4$5%EKIgxZ|VKktsAVeW{YHi-)73P^H zt-^{VMgX`}t^Mhryy5W@0Ja}dC&>rs=Ff7kq9*keTjEa#!cY6t$;UApN2D=*m8Uru zU~wzrvB?}jU@J27h6u}+FN1X3)sWeMDdm34Wo!m`<^~=f`P`}&Ky5e7-8@oi-k z)1uID)y2;cWPQB)-;vvhMuXZieb6sOA$31JU)-w-R7HKn$ls{bW@2KLm9$U{j8_>Q zS)Qf?{2pb?N`qPUrH+<^i!!i;{4Xz$f#7J){?(z^x>+0Vmnn%fw>EFopET9ZR#m0| zQ+e@bZ2#<+J3eqqE!l3La;}Ohqk6Q@?9jOeeOEDJwNc`bpA8e8Nj1o-!%>z;8XM<$ z*}Bc68Ry!_FBQpQe-H9tFDI=zi)sQ8qhZ7 z;o|J>y*PmzlOMy*&7u%+Kunl85GyKBR@O9-`)+IeD;fFV)zD_VxkC%l5+iOIjAI3w9*|`)jjtsurhQ)@m#_WQ3q!+! zp^O?fbsssG1sA+N>n81k&RVmQitrzN9JK)kMRa`r4o=67ZH#`-?8-kBb5birHT`I5 zhkcL{VmAEd>z;@HF{iR~SjTADp;*z+elBoMp?D~$!v?TF$d0yZFdf&5Uw(}{M)lPs zGySYWKiW;r)Rq!r*e_T9_WBS_wbpo+s60}Vnq*HoH3hyE2zn@-@5=+u zi!zbd;g&`s928rN$_$&t0H_!$x%|mC8YTCO2STcL=N_Q z$Dk+~-z~ds=%}V#S#tin`|;I$-lGzEA6nYx_k_L3rI$6 zUPV*;e!Lwv-x+ZdBg9a@waYY%?J7oLt(u0kps_sh(ca3t>SpZi@ra%0W7J_QaBv4X zpl>Wn|GUZ3TiIYD9@%~SHUk-;qx&j3Xp!M7P+D1ONH|{32zKUw&Z-gdgFx~>s(~FB z1-L^vqnIk28P~Sc4b9cXmF23&*8lce*~%1T#BTjE`A=Gjse}#wYK_?_0yV76XBv19 z2d*EGD2cFdN$9`(d-O(GU;-sB9T<`vwd?p?pH7Dx@rgP9U%ENsxn+#{o5e?1_jVb zeR-P%$Y+dn;Hk`=vzpIQiddi=9t7TuI$cs6kIxTt654U$>Of z8$g5yFD~@nVMbI8DhX(eSX|u8+sk%OE6>x%C#imUK(nZ+C_3IN!B+M%2D#$g_wm49 zf>ClKcQi76(060dsHh;du`_~`un@+H&+nwNf1wxy^x>jwoyVp9iY_1vi@hXAa0G-~ ztTR55r(Q7@jGCns9ypzjQL@*C@%?1|cS`XuQ|&JoC+fALSAR+4eXkEheg2h~UI*OJ z2i*LzXopT`4Q}{_D+-@4R0Qm_i)zl?mA%|3QQRHQDPbw(EWOVje0}SsSJPj4<}Uo@ z{QPb1(>-$qd5QH)aKOz`|MTsVdTd3D_huqci6C6mtFiQn{rLdv^#-aXhmEmtaB%U$ z*YovlSLLOnt4|YS6i;1qm}G)(_623r1qQc^?Jq+oYHrVWLk=SMp%uaxLwu!+?WYFq z54%Kd=7PtKvA{xn{hzjrf?Q3v=WWr)%L#Vhqs{Rw<0)6@DR9*_(aW5ux|{#{PK6L~ zi`K`!ywYpY`wIur>s|*9p5tYY|NbLo?xq`eo-I3TxIiojB^0b~JLjhEHViGDw^yfS#8I=IbJ}*(C^c@@ zR)6_gcQh>eAZDdkrMJb#DnzqXr&oXPk@Xa*p615e63QN-y&0s&V_nW%cTl>&I`L&U z2=uwvJ=^Qw$a9ZZz(L)Sz(rfltXh>`VVWyDGJHG?1O%T7)7LwlIF%kP_X7+wlG|XM z_Ax~^RF(c!4Om@Ve7Hx@q~STkjEONT0N*zs1?e?*+E)7iimc>5xdAeVvB^l3>})>j z@i#Fkm!HLnhZ8@(q*{!IMwjk)dpcwqX$FRvmMQE9X?rBlbQmMgVCTPR@GbtEu%gdxD?T|&mUD6NXSY-Eqd~q|;n@03pZ1NQ*-KoE@1z84tRgewrhYj>}77utKXg2NcuK!X6 zB4lzszOWnu)PA1l3OBny{ru`%Y?5%$3;(ND#-hioe@}D_{b7-YPKzlTRRy~OwkLGayGXN%v$e}B$wu!5Vu|N(orSjw- zkY=7g)H*1i2phwR2zMvE#8=h6YvB`Md$ZNFs;Q(DdhL# z7~6|<6KgU_H&!ibZrO;I`E8$tzfvz{lY2Er<@*D12N1HgwCPAcenXbf48n(j{Rop9 zI05qAU!LWD_zM?2Xwi}GY_wD;(Y?qeY4Q!p+7=VGQt#WSjiv-xEE3x@wuUs=8^bor za6($r(jHqS$1g>NBfGe}`~J4UK6ahF_3LdXl1WSz+MX;>N25Q$&kbEBqC*3?gioM}*Je1V>ghaL)4?*&oZbQkoRuqIfq4Er3P^$aklCnZrkV(qIU; z+;3V49F77OE6mS$GCo8#_Zfy1vS8Srl7a<>&t=tw;?u-m_^p;&h^(K+(;d^*K z@X0p+*vGZ2zBsl$PO)>sZ$t&q@Un|J&2NB6znU>- zy`?^pnrgkj?k7XC5sQ!F9w-ohreBnk4mjUni5?vZM7jX8H$djrC#5NVD_==KMydIL zINb*GwCf`@f}=(vJ?VYJd3BEhRJxr2xJS+y|L7V3@^xC|8pSg zSTW)|5jjy5!MDMrjY-F-6`{fvk#hQrSG>4uv0=Ve(cU&uX(b;}4*jn#z%#8aUe9Y7 zHWdNvh-JV1#$fwAaoJ9U_~9x&&x>RMuKYQ^M#{#S7N^+F&Uuw-HxdZ49tzIsnyI27 zDlCO%lwn6rhcw#)?nh3S`O`0{Fm|6WiJgUHpmIj-mR21JY5i8VFx;Pt%)~mFfk-tq z&o{m3T+LlA*dW<%5?;G|WyAVi0a6h_I!lOYirZQd=`5PMz#$6xS^)8>&K8}^@0$ki z#xVtgKc6`QaH$TvW6Nm>JL-|98+Yiff{a72F-qI$T^*u5cj;Vo!q?(QFUk&pxS)j& zEW<&8pCN|&8~vxiLWzTXyRSS((eHi;tKE6d$oa6e81h{gxCm02sUrA3QL1QbwRKvJ#g;rJApsj(?_ZG8BrO z+GDD9NI4D2CA^&I@EG{<(bk%IiI)~>=Q6l~OauHO_h$RbMqs@P&^6#haJ<2V!2&RK z773|L5EWq$kQ7{FE=)r{jAfn6#iJt$jzr z&Y^e=lL*?PF)_xUD^}xG$n&0#ed+gBx(r-+jfzWi_1CPw0!6UNv;(f2@tn94U_OFc zaIke!)dJ&Uc3b~#}oDHKBAm6`EtLPdEEKIZ;sCEz`KAN zlI07i`pS@CfiqlYhJT`A*kxf-TTaK(oVH_(xM)LK{uI2>z9QHp-Na|4g{UYn)ReDq z22YLlSr2>)xonuDUs3~9)ty%IPk6S|#3B_SGhFu3#VEWCq*ni_{3pEG8$DR*!3Y47 z_{;&OE=@7!U{8oAtco)TW(>BwG71f_>;PNCSJYpv<`m%qDck_BbqOJQqPG>#Z%q*Q zE%6ac0;T~tjNq*!d78aLmSVKh`|JuZWX=Pq-CUY8F3z zD1jZ*LNen&Nz5c8`LNUBI#FoYhRJe8=>7*CbidTB;k^<->t>Mz1IwWC(^Rzy zqRRhj8nKf21;9Vz<4QaC5ZnQt)3wjcA$r#ihn(}7Ob`F{0d^5owD&}-ZRjwM6aer? zISlL*l^Bcam-EG?<=99Mhh?n;tyHGXUI@}UHGj$!9)HH5>RA@jqonfhlH~jnp*!J* zn+6|aUBS}(OfAIUyb)I;#;TczipcG~NCMedz%$B~A4gxe-y}ZsbztB>IJj4}zh`AH zRR2U7&`7_2BV6gS)EKMO>PBV2vXXGvU^u*hOC7Y@71!`F#8LD6|92KN*dwPMi-}QgNL>+Z)6O5y?-A6b})U81& zzuf1E$eiuX76;>-c*TzE{ZXF>#nBUVQ2TtqW|+@$>)IHJqH3OeB(^jh&06O%Ja3p4 zSOV{hED@22rf1k#mn1UqJi8-jy`)oZTTbTJ^J`z+x5p z#;U(zVB!tH`~Ge5A9=hk-Ff2n&LAz@MlwItovLyffmON@1$glb(m5X5fRmghc47`t zvK@)+-Wf}u#Plo`jsK}nWid`>7e~g##+Pi?&kKPrXc5A6bU^^)@9sKK8{1{^VIT}| z<9_~||4|6Zrvpjk`dSyvN`_ZV{x!=~Q7=aj2=GyIF=0w$ej`Wjm`YBPQ55-0F-*!r zH>72)#!)ZH(!OH?9Q(tq4c#mp9p#M5oqkinrWZ)|Ybs5r_89j`r+n_OI+C?*_aWKQ z0N?$3=enTzS4q$WxE5?EqZ{J4#_gnsSH~Oi@3#nl9)O1cr@{_0L;6F$Ds(gYc}kuL zLlqTHZ(nHskZln}-T9FK?s1ger=Tk!{@Vqm6GNjLP%0VF7OuA}7 z#C@`-SzPR@jtBv5<0?`?EdagIPqjnMfUB%zW*de1weeGo%zPlT*Q3~y-Ky2hxw-QN zkovgD$bqBw9NUnYVe#_*)74M` z5d5iMwS6#rgMDe}5N(U@LQ(m1CJXUzbxrFmHH)FDhbG5bg&Y3i`FvGgIq=H&`l^}g zU;jg~^+IMD`XroLg6Zj9i#|qetHCX?p(`GovCwwUQ}niRKGnckb7^LSs36H}JWeuc zEd`o&QlX92IfxZ3z$aRp;BB6~r2r-t^q$vo(QO$6IH|7-X99I)JL=!QLia9*~y(y)}HO8T{cPnDB1-5 zZxyo6kPA9Qgmr;Vqvs5SIl-Q%k}t8Db!t~{(z8D9IHy{E7zj2nF#d^Q zgB0V?lV*<;Mv9E&%_dD$RrAEYQ{(5zD~w(8bAm02rNQ_VWRyBfv-auRuDsj=JdDb} z=}5Z+L7*Si3$AQ}Q>#wPomHQQsu#b;av#N>c|uQL>OFQio%OzC%#SrXMEb@5@f+Ku zi~7+JSy(HpuHvCyQZ~4(w+yxN82ir2M#^o!#L+^Eh=_;|M&w=VL_e%UavU>k(%Z1U z8NSrcw4TuT`msE@fp)-@EByf%C%sCh<>vT|GEv^?(98}CH7&YNB^#vuYz9*O9Z&mlX~+_X@n9!M`AMvdw(qzF51?- zl+@Ufbv}T=v`h6PjnGTS9KU;z=fzhix%~IzW>Cr@?|T`BRXmU%JmE(GB5%-Xqg&DB z#*}KIdHT45_CaormyNem-d*8XwSn*b!r|b#&W;G#@v-0a{F!*;W3hv&XnI4il!TZtR3+bqh}mCT@Dz2t@i`Rb==k8llZ7l;TrWk;w~ z&6l6BvF)IxoI84)OvRH6^SNIw%Mr2Zx4WB;wN34aW@u*-B@W#2(~unQ&(fUe3Mok{ ziMq28L_eJ$BF%XF9SOx9)X{z=-*a8I@^$nA@7{=dte<%ooJD)A`XBraZ#~LAd*Il_ z67qOFBXy}c(Qo45<>8;7d3xTZRXTX@@p0R?iL$hedaUe0M@-wAA$;*nt z%ldTUlc@jEGEt7m{cxnr>iwFEl1x+a+g8S%F(TQ3^ArF3_~)9{<>VR|kZq6Binqy; zn+(&_pS9ESlRA3U_OrqYdDdZr77=}hw1}U&<%qz!c2{q6CnxG$omiM<;ZT(ljS7qP zNa0pL$?0@OcGjJ_(Sl^}f75uwzDB#=OR4Evg%=u5nL%Fd2}5lqPY$ho{2?jg+NrQ$)W^Hn~iIf;p4vobfBmzmC- zX=d~9Z~vDjzVxZyQt)wx@E_j!o(70g*y|^*Xm@$OX}d}Z_IU`{N;kPnQEAln*&3FK zCnt`Q5>K`A)ZWpQm37k8v8HYoYW_yLe-sk}D>?1>mw960J7oYrUTw^aE;j%Uz4OM^Lkh0O%#Xx7TJ$^`g=C9sXbs zUX7fbB>w5cY%3}rCpQj{;FbjjkZof#f%97*kg^~H zC6x-|D9E0t+8^&6{IB*3;?Zwhf2@iITs>WARxE#u4GV_uJRPx*{#c(#3cpd5XMen% z@wmpic{*tKzH45+({`}frhG~g<^LUE$k_}WB71pu2)M!mDwk&44M6Lr+aLKv-|U^B zB0g>;)r@5cT^f0zcIItk|8t8~P$FR7>h`&&Sbh03yuPmMUeegwUT~pM z$fl$_M+4x4lK7a25exYzWLB>3krj)u;qv7W*oVj}Bq|$XJh6ox>?l}5Wob`5wHtTPLNmtYqt7-jYP_Vi0 zSp9#dodC%baaUm)G|CII}Kv6v3x~nLtBvFE70Y#!@ zP@=$+b6j!;$p}b}KS>KJK{7~Yfh9{`U;#xkOU_HqS;;Q(4fwzB{a@9+x9Yt+Mb+-G z(>;B<&pG{_@AUNWb1c`*);1q2diWI9@?7-EBpQy}z1j^pqrD&{LLQ#_1QKwm~ijZ%KL${Nh4T;FVZ<-haRDPvbLpF>5_ ze)dJEQY%>WqkSyHE#(j|__G66Zy1Z6N_=p^f_y2M4>H@ZJ41Ap#Js&ELcKXiWB#<& zL9Lkfd~(EH0Aw5&@5CjxpdTJ(Bk;#VbQpM#2E16t$Snl|x0 zxN3*yM#rXSJr3@ITKFk8cDA7^K1*B%X-NNM2x za~Q;6_}1RLVCbLHeSCC9x{xzBF6m9gzM4EPQ z3D$cA=+h&T`qP^E$I!D)UKvmL$$2n}@-m)vN+LrJpQ*cKgF7UEQ}ADC`*oB~>ih}t4Jth!o0IuTZ(4E6qXK8nI{0zue( zZTeNbiucxBz(SE-S+mQ&W~+0YdY-B8-b8ryPWsv)igSQ?A16KF{C*Gbh3K4$O0yg| z#smuWzoNXFEqU=v7S?>RLdH^^qCQPU`35WxH^%&kJ(6za#cSnE_utlWJqyRe86V?< zhA`OW?@u0oR1wpMkDk|*#Oa}F-5ISalx#n`2zPF9a*!UGsmb%XAcb)27sYoKiZ0S@ zo$?3CTLb6&aju<{|B1daM(pyiEOxI`!W~oFe91;cJt=nB-+a~DHZ9Nl$bQ$H>4?yO ztqpkb^Ocb?1l{X<4EUv~g^M6DPYjUofCOkUX)O+(kx!w~$*-NQ`vXpz&z*%S&FJ`9+b#`v3D_s%CqEM!;=?M4z45|99g{=J=AXOk?z4O8*ewTK#;l&NY! zA@eWxjW60jLZW92#>#!;Gqg`;15VtpHI}N&b9s!T?jB_>wj>83!EQL4qk1Fouke~NkA=FM>`zN?@rp^asC;J${_81S$hvj$Jm~gc1FgBSTzv(M_%+pL&zO!TrS`;5I+kQUUhXfOdY=C z*y*4+e3*cXo_P>R{XjDK8gPU32+XcO{=Ka?NE`%W2h8F6$XvN;A_!DP_4DQ*feArR zZoUk-JjA;BqDP=j1c(vnpzWHg3Ue_uP|91(RN8xp#^!SJ=mF6f-BejF62XUPdX|`n z+-NN2@7$AxCUx>MnW(9#@BC5+KqW3cKFD75<{lI3^qQ{AKdMB9Hk2$a5%i;>)(8h$ ztCZdig~~#KuCTLDNNSdCnJ1tL#lKa%87N^YzLLAnQJ<{hE_xTC@uh0ILg`E3yAky3wyV`7R5`Y6-(G=spc{!$cnKWU{1=JZ-h5tg{n5OaTp&OHE;9N-u`)e zI(AuupJKSMkd+m`?1lc`4~>I~UL5+mL6Va?`)9Cn=0< z6xvN|+N`JH(wOMzr{!+uv3~GL?e-hF>B>IW zvyxznmq7D8e-#$X!(5KBV$r5TX;ZH7#mSQd@e}pQVc+-fGJ9T)*gcH7XP7jGskrw6 z)Y5KZf89XgcZq9_269lQ#WjuAEwF;h`E^XX{S;%$Y^c{xRQ4Vj_~qytHXc zx!FP8*O>k~f1i|D9#Dz<`Y1f+^zeD{{Sjiq3l{A^$`Gk=en`gK%AnXhaC*`6?lTLl5FcXW2~alYB$(a9BKF)})6#)&cSYid2SH*}=D9zXZMsPzYxj@zEpl+N z%DJFJK)3xiY5PER$i-um6(7My91KlL-ZfnbtQMh4EM?!cVpnC%{&0*ezNSh6a1I!9678X-805F4&Kv))2AqEGC-8@(jDkPwd5|6zz&Z5@OKc2ET(%V zL4OPXv!(poB-D@bH4W-z51{6HlG*;E0}iE;=(9!R zoyE8GieOAcqYp|*WOaDgsH~Tes!}W zpx)HjfbOS`kVEE#ZTW=1IVof7xjxhXa#FfAY`&|m29>p|z~>%C;;~}~_6Od0M*Zu( zN099m5z5wxILT*1WAQ z!v6XY3hHgOX735SiOyS~6RaDnaL2Kd55=_QkP@NDmKAV%8T`7rYZ`Px4Ucc;&uvCH z=hf-`kI-{|1}xCDz+X`<>2DF=zGY85Hg_{mTED)z?SDG8;@wQ3pINlJ5eCZOng+Vs zt`(PfdCLa}{T{3Ri%DpL!K%Bnb;@{2U6n>}Rc>UPupBbYnioW?5#PB!|2O>dA9eh1!1iB{ z)XmU>{xcACT?w&%)gut-$(9^gT=Tz|ED1dW3N*BTU8`A=0074=*;+Tf1mp~mivR_t z0pP7h7p^n0%UH}o74E+(I=n=iE<8ephh3W~a^mRk@9$kPEpXWBE^sZ0`^%r82`e7R z+|DL6b+zwQ?Q2>xbKZ%E`$@p5<2N1O%w{nv0w<-S-I)HGHQ+tGfQo=X6NB#BbGUk{ zPak@&hA$;pIsh=z)xq~X=l)wj>3|D8{6CZDPX-D|^UKmpi`k~@2am%Vq-hA-g!nI9Kn*dZg+l6Z zjV5|l66UdI#tD;_NS#c1LutX0Spu6VcnwqbbvuP0rcz;Tj{f))#^I%W5xMhO&t~zQ z>20{SWg3{tngeUHZi_1TJ;+vD@j-U@@8XtUy%#y)sRdw^q+idppM^9?OpJa zX#8@LZ=Gj(p3QS*h%NGVe~cxQ$B+{xH|>w5FU^@)+V4u;a?i7sjNqd%9!OlPBDHb6 z-m@;N{;`2a*Iwsv)oEn(JsY}5g|9UWHq~T~_mS`Kr4x+CY7&W|pG;jV z>WLlC8PUoL!j@SE+|7AL=s#I?0k*(M`r!;gX3y;MwcR)4%^Vq1c9CAeafxe1u;OEb zVS?bLH20?!?)>>iaAMN%PO?v!4})gz_1b-`t@k<_&KK?wjk2<~oqswu2;d}@JK`+s z^VaO{_jU||nk6?$S8Mhv`8duHfV1JEmPvX(ZQGj23NxLica6hjjjk}Ad^P43tFPW6 z@%v$m3!Y>}qda!~?5laKAU zz_Y4kuFOd(#%$}t+T>d)I&kX>4=>ZNP}}$wrlUt{Ioc_WfAmVeHXq@2^@*fOzDNlQ zFKUCq2aO-8JsTY^NZ5=Y)lysAupjSSD%H?W`UADDCR`zG0~o0a8%(&h<2lmXG6ZV7 z;_H5zJ>7`gK-@S{`3v5qP{vo-HX6$1fk&g_zGGg65qwp)hH2PKw^X*3MAG@Eii6r{{Cprv z)Kw@fk0X9LM@}SO|BQV4j$2qMyR2{~!>!0WzYnfa_lXUU`J0l)DUFk`M;?u%T#1eeiXY^KD3jQz2DGq)XjaHnh4cftW8)P zpGfwi-;E%x?x+O@$1RRCgH_9bkGi|73H_gC$D7*pKL@fplmQ$m8+NN<+r8`ON0qg_ zT?~)%jnZFT9W3M+*ZO8v^FR~;^~=Xos6uZqmar-R&_LM1w?x5IiAtg#gQzfLfqeSTSX~0IH$5LSQNkCz3vZl9`nGp(nvgh zwz>RY2wuN$fc-6iuNeB1Ynqc%(;NS!K>PX(l-+l#FK8h+xozk7RU;ho$aXV zwTN}q;*yh=%1|2)<#gkuR|?>jl@OuQFY8Y>s090pt=eb%`VF7c6U{VSC0o~zJB*a3 zUBqaVLD##x&3I?F5Pd&>hM}+;q?Mmfg$ehk)gOA>c-3IEPi9{@z7{yy_MuN8A4vCE zS}a;wc}>q!#`YB39v?D1U2(afdy>C4EiWmol+Ktn8YT%7TC%|yp)yq!q7_y?{qy2u1K*TAe!AOcwKt2-UK z<(fOgH2NF@fJ~opGPfqSVJ}2Yrlcr90JTUHT$@b40}q6V6sfInKz{M%$KvPJkH?n` zsQoP2inG;APzQ^DGNj^9s8R(GkPlalar&L8dW=$XZ0pbC;!p03WUa) zN^2BJQcp|o42$vEN2EwX)()FJtM`Hkf$(&b;C{^9A~+gt{x$O5!Ey7FI^reJ8JBx= zyg-CZQna|yFN(2Z*0i)YGBzGjkh1Z7RPL243B;&-Kj;oZWKb7|EJTaZQGSeP?EA?U zEb0+>QvM$o#g#)}lc;rZzG(HW>;E%FbbygT5NsZ|r+%8FM272SR522V|#PnBC*@(Npi?Biu8 zGn1kcr)O8vbd>#FKg9xvK6K+?y8hT8-^l1{nO4B$;6cm6W9UG&*F{Gezi9n}S4vVB zw~b1eck$eM1Z%TY`hc0Z^xGqxaiqE-Ko+oV1YCsXB&AQ72iI&SziDDy%WDLdKZ?lR zK`@khfBA}6`D-=P2*$MiRx+kvvxfxmAOow>8L_4`HV_}dk#quv#|T6;r$%ST26R)U zXtFWIFvcaVwAYJB48I!wOga~kvSqCnaGScOD&Tb2H9zMP!#!cWo$7#TLw-fOooe;f zslL>SLZ2#kHA|gZM@Z--oBN|3v%=w>74JBXo-2=b#H$@Ii3GVW&a1x1$Ah;>_IVz2 z#hMLC^ovNu>R!CfPm5v{>3|_fF0PtZ-aq$r3Nsa*zY5dqJhDcxJy!5GmGVh36Q{A& zpyncJdLGQg*esRsJwZ2F`bX2L+94@M zI)gl3-C(yY`N%M$+khy)5e`;(_kuNG=WIk;5tBl*DF|P0OPdc+ZX^!+G^$)3^?<%s zjc4bLYesb;P3!rmthEh zX4b5BZ{_S_n9Fv*WLZBgxty&$juEw_FmUJ`^+IaK?dDR*x(faXk0!$1&uE(UJWCmW zx6%IPa!|KcC@ZCM%QHooy{0Ro>!?7JY@DH4Njz2HRnmkrTs78L>VZ~a!!wCN&`G|Q za9y7lN$i{jgRHpzb4lSLD;-84=&l$iAGT*w4nqiKwUbr7&~%W^lE+jK+dVou2#UOB zZGJ~j_I|tdt=Bf$pIN}w&NOPcfcSt1HI9lnuhz?yaJO}+HFI}s8r1J`sp-{=l}993 z-_!Kg^QQGD0q=(*U6{3{g5ZU6J0sVJiu?hCeNf^lnRG0)pIt zrCc}Pf#3QDuFFR~vnR9fFBc8hRE(SqQU>WQD0rjQny;i9#7c-`$KIb7j{6o(MZ>=~ zapzO3fD2Z;Rr(u%ip+7n-3s*&4DTN6nc`&QRVUn&BxWxI?@$#J^K z0>2s*2X{q^pd?p2=KA{#SXQm_${MwpUlPTbP7S8!_}8j4jjiH>!#iy>9SuBTNojiN z&HR2C)M`UlUrdRUdXY_gV919$8Vvg*I`&_Z`^+6-^fJEMmT3T*bOMvG&9j4q-Wo1O?Dh3n2C&#Wt6ri)GNiCbD@Dn#9U!{ED zKP%}M)r!sU8rAg3)geLW)+7O}%A@)3pMZ&ck*-FS;5rh?m}B&)!yl%3b$C8B5^{b% zT791ve>fuV41G#4rw19X{Ivm_$82vGtQD8?&lA27#EuMIq)tWW`jA0$u?d?w1ul&M zfa#^8L$2NESB|{mvYK}En@=(K)iBjoOtflqZgb+|;flHUnnr0Q7)q?sDdQtFs&^tl zG5_{B*{y?pB$u@h$0yFFxG#NJ@$Ol}CxJ3gy@G|I?TR~SN7gzUPu|*W_8^j2-VAYj z9meh`Ldro9ni0RY=y8yArwXjzLP5PyPDLG4;)9fLgHK&vD zd$yEap+ASerkypfzOU_0m|;G6Z1cNpw+S8J2M>8<6x#K}OGWZ`C)NpHU=#SYxl%gj z{IJ)#94G;vd6d}|RcZ8NniU0?oAwfy*Dc7;#F%4Aw^%U(?Bw0Hw`#30Gp&>c%j|A` z2+p7_9Yle*cHZM+}GU+$@b=M3-{PHJ(gQ3PlG6pn%GJi zdom-F1i1>1-6>Euyd=X@Y=RQYw)<5~u?>sGNlAdptCrExM~ssv`2;9R(b1m3n5eZe z5xphr@@evJULMm8oF%cQyGn3?qsH<1x9}tD~1deMsME6ct0w2}*b1#P?f3%0#7~(p&B8$FcVG_q`6G z5D6HV7`J&v17K=-H=C%!@BG!@?@Nxq94U5wWGmW{S0hNoMWc23;XJ# zhK~EM(&KE^3p(XPC`v~RS;lQ(^pP_3^{jo#_d0@#IlZoPPtzQ6jjfU2M}Ft7%6}_N zZT0xbSKqWwy#;yvF(q8k43*U!$G(BEbQZC#bMfvi;>}(7vJ|rwv!pfyjH+nTyRDb^ z)n;P8-BkkjT9A*Bu7p3Q{=L^l=Vs&X7CMk3>7b?@ib#IWuh0sMFC;EhcZ9mSDhU%6 z@>;-|%n!WnmCK~!^hXW#O69W{<4-$qY_nVpNmFrNuDeh_4gn_mmJ3O|z_btHANXr! zv?>Rrf;S`oun}M;hAonLr}L8Jf(;V;K6$yx0BCyrYyMS@7I;vA>aW9hE1Je#XswE&|OUAMxeWVc42_l_YmJ2z>Ov;Uy# z^H2^fK;KkR5a0bCIb7Urniq(w4qfgxBL^%&p=kG6|4W6L_;DGejQ7a9)V-}2?2tJx z>^5TD^0~zn)&T!@o4!ffDhwPM3h#7sef|Q$t&6RXEnraCuvlBSoBb9asJ!Gh7s#Eo zXC4eqPEP*eFDhsPP@79wq5X@;fcjcD9>zdy0Lby{1?|2^`n)+-x2ms)cr$6?$O;+R z^e_Zj&l&+2f~QZuWy*_j=c{ECs5sp~hN@do5Ldl6PMD2TPvyBG%UYDfo`8ft&gEgv zNLO+=QXGyLEirRLp~=~|F89R`7C+IX#fK^Y`6z>^Cg*9Y?kB4|Vx8%E25SE9vlm+@ zU*#obnbwz#g>0WmM3A)IK0oSEv@O+qV?oXvt&L@qjp_rT)P6)*yD7F#xii8#RV@mR z-k$0fIoc{GzVG-aFWW7a5RkS7c|DFHP62*)0+Xu1&^0;tb|a@c6LMEOi*k0~8`19a z&}{J6KcYJPw$v>lUfQ=+DbP4bjs!^9h(8!aQ2U;^tDEk(hWax?>nSBP01dYY;*1BU`CnNy zN|ArytEw<@n4k^&Em|q8uXwH=5^n9UNvzO1)QgQ)ZoZr`mg*BKqu34UcN`+HUy&?a zR#7-Y{A@_THyK#`H+X@DjN3~uPAKsEPiHCn&Lep9&+O~6F=z+ESJNXaBZ9 zz*$`N5qTh#IM&jI9*CY5_?X9XSApLERaM4rEb`Grb!x82JDuF3Tl;|ApbR`f-^e?1_N4qA#Cg<=mU zD)C;WB{$J16|)u)mNg6Q0@I;qbdI=kh%EDH>S7lEC{gRPX5gmf_a9=JBz7e%8zWbJ zUv21OUv$$gZTPJM1I*#AAO(MI!K@q%7l2_86>^k(q4R&^c9oIn;H(*A`HuJLFM|Wo zuZLC#*&%pRm`P5osf;Nb<#i{g-T}n? zY*pRg-zSt)nLd`Q=q^HG1Hq|Z?-B_eD~jEF5}_!S_dV2G)jZ%=5WG`!yS9_RYq&}? z&R#Bsl_YLUy+ED^yO(Tv>O*?p)~cRW0yBQ%Iq6x+&gFG4{(dJ8jA85E`qT&ig9wPL zEcA;hu#4A5?F2t_`HVw@N8WcyheA~zz$uNBS!O+LOHpK#s~l_IRYknilBrUKNTgBl z*T)vz27Fr}3b4!u=`0qGG}rutY`$b{KhvMnbFkhSe)TcUk$6?Uusqi<93R&JUt8~r zzTIBaC7yb!v1w8p6TX}Dg5tV453if^1`4F+`h1$H-0bkbDN^(n08^K^Pk!9xoKRcb zgGy2wN62WX=H{}eONXfF#~^+nAiTso>zz5i4}fhGforXN6U>5-`Hll*r;?aW_JQ0H zU{3n0SCdbz_I2a#?KLTrlozZJMkN_;C zQxgadIH>rs#$N6|#j*F#)yJ=8W`Dm{38HVyO*$VVpjf;fBtmk4e!fer@zy{DISzzS zWaXG&!ZuRp#qJ9^fqd6IP_$D68mJyr^5DAqw)f>SZa=qWd<<2Fzwa9A-XO1Y3`6j@=cU!=r^jI?izB=Dy{klqjTXrR1qkq|9}C0>X>Ekj$c2F5iIL6!e)MoA~bauPjr z4S)kb%ZI^unv~c4-_CppAq}3r&ULhmwJM5V7yf@+RAF{gpJA5l) Date: Wed, 2 Feb 2022 16:55:19 +0100 Subject: [PATCH 080/132] Fix test, for real? :-) --- tests/examples/data_solutions/dp-foundation/test_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/examples/data_solutions/dp-foundation/test_plan.py b/tests/examples/data_solutions/dp-foundation/test_plan.py index 6d50117a..10225f6f 100644 --- a/tests/examples/data_solutions/dp-foundation/test_plan.py +++ b/tests/examples/data_solutions/dp-foundation/test_plan.py @@ -24,4 +24,4 @@ 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) == 40 - assert len(resources) == 281 + assert len(resources) == 286 From b38b9b2d57039557ed3c28166f7d456d4e524e86 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 18:34:48 +0100 Subject: [PATCH 081/132] fix readme --- examples/data-solutions/dp-foundation/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 1e68878e..f985de24 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -81,6 +81,8 @@ Resources in the script use the following acronyms: - `orc` for `orchestration` - `trf` for `transformation` - `dtl` for `Data Lake` + - `cmn` for `common` + - `plg` for `playground` - 2 letters acronym for GCP products, example: `bq` for `BigQuery`, `df` for `Cloud Dataflow`, ... Resources follow the naming convention described below. From ac1ca68dbd7ef251eb2ac5725958dcfd0b1517c2 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 09:37:50 +0100 Subject: [PATCH 082/132] support policy_boolean --- .../data-solutions/dp-foundation/03-orchestration.tf | 9 +++++---- examples/data-solutions/dp-foundation/variables.tf | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/data-solutions/dp-foundation/03-orchestration.tf b/examples/data-solutions/dp-foundation/03-orchestration.tf index 45aa4d61..dd4e30e7 100644 --- a/examples/data-solutions/dp-foundation/03-orchestration.tf +++ b/examples/data-solutions/dp-foundation/03-orchestration.tf @@ -79,10 +79,11 @@ module "orc-prj" { project_create = var.project_create != null prefix = var.project_create == null ? null : var.prefix # additive IAM bindings avoid disrupting bindings in existing project - iam = var.project_create != null ? local.iam_orc : {} - iam_additive = var.project_create == null ? local.iam_orc : {} - group_iam = local.group_iam_orc - oslogin = false + iam = var.project_create != null ? local.iam_orc : {} + iam_additive = var.project_create == null ? local.iam_orc : {} + group_iam = local.group_iam_orc + oslogin = false + policy_boolean = var.composer_config.policy_boolean services = concat(var.project_services, [ "artifactregistry.googleapis.com", "bigquery.googleapis.com", diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 809c7d42..eeba9eb7 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -19,6 +19,7 @@ variable "composer_config" { ip_range_cloudsql = string ip_range_gke_master = string ip_range_web_server = string + policy_boolean = map(bool) region = string secondary_ip_range = object({ pods = string @@ -29,6 +30,7 @@ variable "composer_config" { ip_range_cloudsql = "10.20.10.0/24" ip_range_gke_master = "10.20.11.0/28" ip_range_web_server = "10.20.11.16/28" + policy_boolean = null region = "europe-west1" secondary_ip_range = { pods = "10.10.8.0/22" From 2f86fd882c1273166abfa6e4f93717d4fd102fca Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 10:39:08 +0100 Subject: [PATCH 083/132] split Cloud NAT flag --- examples/data-solutions/dp-foundation/02-load.tf | 2 +- examples/data-solutions/dp-foundation/03-orchestration.tf | 2 +- examples/data-solutions/dp-foundation/04-transformation.tf | 2 +- examples/data-solutions/dp-foundation/variables.tf | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/data-solutions/dp-foundation/02-load.tf b/examples/data-solutions/dp-foundation/02-load.tf index 1510aa65..497a309a 100644 --- a/examples/data-solutions/dp-foundation/02-load.tf +++ b/examples/data-solutions/dp-foundation/02-load.tf @@ -105,7 +105,7 @@ module "lod-vpc-firewall" { } module "lod-nat" { - count = var.network_config.network != null ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.lod-prj.project_id region = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/03-orchestration.tf b/examples/data-solutions/dp-foundation/03-orchestration.tf index dd4e30e7..53d28cd4 100644 --- a/examples/data-solutions/dp-foundation/03-orchestration.tf +++ b/examples/data-solutions/dp-foundation/03-orchestration.tf @@ -132,7 +132,7 @@ module "orc-vpc-firewall" { } module "orc-nat" { - count = var.network_config.network != null ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.orc-prj.project_id region = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/04-transformation.tf b/examples/data-solutions/dp-foundation/04-transformation.tf index d2d059f8..f1bafefe 100644 --- a/examples/data-solutions/dp-foundation/04-transformation.tf +++ b/examples/data-solutions/dp-foundation/04-transformation.tf @@ -99,7 +99,7 @@ module "trf-vpc-firewall" { } module "trf-nat" { - count = var.network_config.network != null ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.trf-prj.project_id region = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index eeba9eb7..beaef5a2 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -58,7 +58,8 @@ variable "groups" { variable "network_config" { description = "Shared VPC to use. If not null networks will be created in projects." type = object({ - network = string + enable_cloud_nat = bool + network = string vpc_subnet_range = object({ load = string transformation = string @@ -66,7 +67,8 @@ variable "network_config" { }) }) default = { - network = null + enable_cloud_nat = false + network = null vpc_subnet_range = { load = "10.10.0.0/24" transformation = "10.10.0.0/24" From 1d2c041e28ae030ab29fc6c54edcc74f93304a62 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 11:18:27 +0100 Subject: [PATCH 084/132] Fix README. --- .../data-solutions/dp-foundation/README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index f985de24..487a0994 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -193,16 +193,16 @@ Description of commands: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [organization](variables.tf#L76) | Organization details. | object({…}) | ✓ | | -| [prefix](variables.tf#L83) | Unique prefix used for resource names. Not used for projects if 'project_create' is null. | string | ✓ | | -| [composer_config](variables.tf#L17) | | object({…}) | | {…} | -| [data_force_destroy](variables.tf#L40) | Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage. | bool | | false | -| [groups](variables.tf#L46) | Groups. | map(string) | | {…} | -| [location_config](variables.tf#L136) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | -| [network_config](variables.tf#L56) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | -| [project_create](variables.tf#L88) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | -| [project_id](variables.tf#L97) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | -| [project_services](variables.tf#L125) | List of core services enabled on all projects. | list(string) | | […] | +| [organization](variables.tf#L80) | Organization details. | object({…}) | ✓ | | +| [prefix](variables.tf#L87) | Unique prefix used for resource names. Not used for projects if 'project_create' is null. | string | ✓ | | +| [composer_config](variables.tf#L17) | | object({…}) | | {…} | +| [data_force_destroy](variables.tf#L42) | Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage. | bool | | false | +| [groups](variables.tf#L48) | Groups. | map(string) | | {…} | +| [location_config](variables.tf#L140) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | +| [network_config](variables.tf#L58) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | +| [project_create](variables.tf#L92) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [project_id](variables.tf#L101) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | +| [project_services](variables.tf#L129) | List of core services enabled on all projects. | list(string) | | […] | ## Outputs From b3238969dfef485c4b1d1112242a06d1c3cca3c5 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 14:25:46 +0100 Subject: [PATCH 085/132] Fix Shared VPC, first try :-) --- .../data-solutions/dp-foundation/02-load.tf | 7 +++-- .../dp-foundation/03-composer.tf | 12 ++++---- .../dp-foundation/03-orchestration.tf | 7 +++-- .../dp-foundation/04-transformation.tf | 9 +++--- examples/data-solutions/dp-foundation/main.tf | 29 +++++++++++++++++++ .../data-solutions/dp-foundation/outputs.tf | 19 ++++++++---- .../data-solutions/dp-foundation/variables.tf | 10 ++++++- 7 files changed, 71 insertions(+), 22 deletions(-) diff --git a/examples/data-solutions/dp-foundation/02-load.tf b/examples/data-solutions/dp-foundation/02-load.tf index 497a309a..b49544a6 100644 --- a/examples/data-solutions/dp-foundation/02-load.tf +++ b/examples/data-solutions/dp-foundation/02-load.tf @@ -79,6 +79,7 @@ module "lod-prj" { dataflow = [try(local.service_encryption_keys.dataflow, null)] storage = [try(local.service_encryption_keys.storage, null)] } + shared_vpc_service_config = local._shared_vpc_service_config } module "lod-vpc" { @@ -100,8 +101,8 @@ module "lod-vpc-firewall" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc-firewall" project_id = module.lod-prj.project_id - network = module.lod-vpc[0].name - admin_ranges = values(module.lod-vpc[0].subnet_ips) + network = local._networks.load.network_name + admin_ranges = [local._networks.load.subnet_range] } module "lod-nat" { @@ -110,5 +111,5 @@ module "lod-nat" { project_id = module.lod-prj.project_id region = var.location_config.region name = "${local.prefix_lod}-default" - router_network = module.lod-vpc[0].name + router_network = local._networks.load.network_name } diff --git a/examples/data-solutions/dp-foundation/03-composer.tf b/examples/data-solutions/dp-foundation/03-composer.tf index 80c5e1ce..a2743266 100644 --- a/examples/data-solutions/dp-foundation/03-composer.tf +++ b/examples/data-solutions/dp-foundation/03-composer.tf @@ -39,8 +39,8 @@ resource "google_composer_environment" "orc-cmp-0" { node_config { zone = "${var.composer_config.region}-b" service_account = module.orc-sa-cmp-0.email - network = module.orc-vpc[0].self_link - subnetwork = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_orc}-subnet"] + network = local._networks.orchestration.network + subnetwork = local._networks.orchestration.subnet tags = ["composer-worker", "http-server", "https-server"] ip_allocation_policy { use_ip_aliases = "true" @@ -70,15 +70,15 @@ resource "google_composer_environment" "orc-cmp-0" { LND_PS = module.lnd-ps-0.id LOD_PRJ = module.lod-prj.project_id LOD_GCS_STAGING = module.lod-cs-df-0.url - LOD_NET_VPC = module.lod-vpc[0].self_link - LOD_NET_SUBNET = module.lod-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_lod}-subnet"] + LOD_NET_VPC = local._networks.load.network + LOD_NET_SUBNET = local._networks.load.subnet LOD_SA_DF = module.lod-sa-df-0.email ORC_PRJ = module.orc-prj.project_id ORC_GCS = module.orc-cs-0.url TRF_PRJ = module.trf-prj.project_id TRF_GCS_STAGING = module.trf-cs-df-0.url - TRF_NET_VPC = module.trf-vpc[0].self_link - TRF_NET_SUBNET = module.trf-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_trf}-subnet"] + TRF_NET_VPC = local._networks.transformation.network + TRF_NET_SUBNET = local._networks.transformation.subnet TRF_SA_DF = module.trf-sa-df-0.email TRF_SA_BQ = module.trf-sa-bq-0.email } diff --git a/examples/data-solutions/dp-foundation/03-orchestration.tf b/examples/data-solutions/dp-foundation/03-orchestration.tf index 53d28cd4..28c46c15 100644 --- a/examples/data-solutions/dp-foundation/03-orchestration.tf +++ b/examples/data-solutions/dp-foundation/03-orchestration.tf @@ -102,6 +102,7 @@ module "orc-prj" { composer = [try(local.service_encryption_keys.composer, null)] storage = [try(local.service_encryption_keys.storage, null)] } + shared_vpc_service_config = local._shared_vpc_service_config } module "orc-vpc" { @@ -127,8 +128,8 @@ module "orc-vpc-firewall" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc-firewall" project_id = module.orc-prj.project_id - network = module.orc-vpc[0].name - admin_ranges = values(module.orc-vpc[0].subnet_ips) + network = local._networks.orchestration.network_name + admin_ranges = [local._networks.orchestration.subnet_range] } module "orc-nat" { @@ -137,5 +138,5 @@ module "orc-nat" { project_id = module.orc-prj.project_id region = var.location_config.region name = "${local.prefix_orc}-default" - router_network = module.orc-vpc[0].name + router_network = local._networks.orchestration.network_name } diff --git a/examples/data-solutions/dp-foundation/04-transformation.tf b/examples/data-solutions/dp-foundation/04-transformation.tf index f1bafefe..f007c2eb 100644 --- a/examples/data-solutions/dp-foundation/04-transformation.tf +++ b/examples/data-solutions/dp-foundation/04-transformation.tf @@ -73,6 +73,7 @@ module "trf-prj" { dataflow = [try(local.service_encryption_keys.dataflow, null)] storage = [try(local.service_encryption_keys.storage, null)] } + shared_vpc_service_config = local._shared_vpc_service_config } module "trf-vpc" { @@ -94,8 +95,8 @@ module "trf-vpc-firewall" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc-firewall" project_id = module.trf-prj.project_id - network = module.trf-vpc[0].name - admin_ranges = values(module.orc-vpc[0].subnet_ips) + network = local._networks.transformation.network_name + admin_ranges = [local._networks.transformation.subnet_range] } module "trf-nat" { @@ -103,6 +104,6 @@ module "trf-nat" { source = "../../../modules/net-cloudnat" project_id = module.trf-prj.project_id region = var.location_config.region - name = "${local.prefix_trf}-default" - router_network = module.trf-vpc[0].name + name = local._networks.transformation.network_name + router_network = local._networks.transformation.network_name } diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf index 5f61b214..31f5bd2d 100644 --- a/examples/data-solutions/dp-foundation/main.tf +++ b/examples/data-solutions/dp-foundation/main.tf @@ -15,6 +15,35 @@ # tfdoc:file:description Core locals. locals { + _networks = { + load = { + #TODO Fix Network name logic + network_name = element(split("/", var.network_config.network != null ? var.network_config.network : module.lod-vpc[0].self_link), length(split("/", var.network_config.network != null ? var.network_config.network : module.lod-vpc[0].self_link)) - 1) + network = var.network_config.network != null ? var.network_config.network : module.lod-vpc[0].self_link + subnet = var.network_config.network != null ? var.network_config.vpc_subnet_self_link.load : module.lod-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_lod}-subnet"] + subnet_range = var.network_config.vpc_subnet_range.load + } + orchestration = { + #TODO Fix Network name logic + network_name = element(split("/", var.network_config.network != null ? var.network_config.network : module.orc-vpc[0].self_link), length(split("/", var.network_config.network != null ? var.network_config.network : module.orc-vpc[0].self_link)) - 1) + network = var.network_config.network != null ? var.network_config.network : module.orc-vpc[0].self_link + subnet = var.network_config.network != null ? var.network_config.vpc_subnet_self_link.orchestration : module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_orc}-subnet"] + subnet_range = var.network_config.vpc_subnet_range.orchestration + } + transformation = { + #TODO Fix Network name logic + network_name = element(split("/", var.network_config.network != null ? var.network_config.network : module.trf-vpc[0].self_link), length(split("/", var.network_config.network != null ? var.network_config.network : module.trf-vpc[0].self_link)) - 1) + network = var.network_config.network != null ? var.network_config.network : module.trf-vpc[0].self_link + subnet = var.network_config.network != null ? var.network_config.vpc_subnet_self_link.transformation : module.trf-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_trf}-subnet"] + subnet_range = var.network_config.vpc_subnet_range.transformation + } + } + + _shared_vpc_service_config = var.network_config.network != null ? { + attach = true + host_project = var.network_config.host_project + } : null + groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } groups_iam = { for k, v in local.groups : k => "group:${v}" } service_encryption_keys = var.service_encryption_keys diff --git a/examples/data-solutions/dp-foundation/outputs.tf b/examples/data-solutions/dp-foundation/outputs.tf index ec9a15dd..1b8db1a5 100644 --- a/examples/data-solutions/dp-foundation/outputs.tf +++ b/examples/data-solutions/dp-foundation/outputs.tf @@ -72,12 +72,21 @@ output "projects" { } } -output "VPC" { - description = "VPC networks." +output "vpc_network" { + description = "VPC network." value = { - lod-vpc = try(module.lod-vpc[0].name, var.network_config.load) - orc-vpc = try(module.orc-vpc[0].name, var.network_config.orchestrator) - trf-vpc = try(module.trf-vpc[0].name, var.network_config.transformation) + lod-vpc = local._networks.load.network + orc-vpc = local._networks.orchestration.network + trf-vpc = local._networks.transformation.network + } +} + +output "vpc_subnet" { + description = "VPC subnetworks." + value = { + lod-vpc = local._networks.load.subnet + orc-vpc = local._networks.orchestration.subnet + trf-vpc = local._networks.transformation.subnet } } diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index beaef5a2..04ce6f68 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -56,24 +56,32 @@ variable "groups" { } variable "network_config" { - description = "Shared VPC to use. If not null networks will be created in projects." + description = "Network configurations to use. Specify a shared VPC to use, if null networks will be created in projects." type = object({ enable_cloud_nat = bool + host_project = string network = string vpc_subnet_range = object({ load = string transformation = string orchestration = string }) + vpc_subnet_self_link = object({ + load = string + transformation = string + orchestration = string + }) }) default = { enable_cloud_nat = false + host_project = null network = null vpc_subnet_range = { load = "10.10.0.0/24" transformation = "10.10.0.0/24" orchestration = "10.10.0.0/24" } + vpc_subnet_self_link = null } } From 49aa21cd9a37e61fec9341aeb25a291c6db2b926 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 17:37:42 +0100 Subject: [PATCH 086/132] Fix tests and resource name --- .../data-solutions/dp-foundation/02-load.tf | 2 +- .../dp-foundation/03-orchestration.tf | 2 +- .../dp-foundation/04-transformation.tf | 4 ++-- .../data-solutions/dp-foundation/README.md | 19 ++++++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/examples/data-solutions/dp-foundation/02-load.tf b/examples/data-solutions/dp-foundation/02-load.tf index b49544a6..79d683cc 100644 --- a/examples/data-solutions/dp-foundation/02-load.tf +++ b/examples/data-solutions/dp-foundation/02-load.tf @@ -106,7 +106,7 @@ module "lod-vpc-firewall" { } module "lod-nat" { - count = var.network_config.enable_cloud_nat ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 1 : 0 source = "../../../modules/net-cloudnat" project_id = module.lod-prj.project_id region = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/03-orchestration.tf b/examples/data-solutions/dp-foundation/03-orchestration.tf index 28c46c15..ac9592a3 100644 --- a/examples/data-solutions/dp-foundation/03-orchestration.tf +++ b/examples/data-solutions/dp-foundation/03-orchestration.tf @@ -133,7 +133,7 @@ module "orc-vpc-firewall" { } module "orc-nat" { - count = var.network_config.enable_cloud_nat ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 1 : 0 source = "../../../modules/net-cloudnat" project_id = module.orc-prj.project_id region = var.location_config.region diff --git a/examples/data-solutions/dp-foundation/04-transformation.tf b/examples/data-solutions/dp-foundation/04-transformation.tf index f007c2eb..a4223f6f 100644 --- a/examples/data-solutions/dp-foundation/04-transformation.tf +++ b/examples/data-solutions/dp-foundation/04-transformation.tf @@ -100,10 +100,10 @@ module "trf-vpc-firewall" { } module "trf-nat" { - count = var.network_config.enable_cloud_nat ? 0 : 1 + count = var.network_config.enable_cloud_nat ? 1 : 0 source = "../../../modules/net-cloudnat" project_id = module.trf-prj.project_id region = var.location_config.region - name = local._networks.transformation.network_name + name = "${local.prefix_trf}-default" router_network = local._networks.transformation.network_name } diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 487a0994..54a59b26 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -193,27 +193,28 @@ Description of commands: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [organization](variables.tf#L80) | Organization details. | object({…}) | ✓ | | -| [prefix](variables.tf#L87) | Unique prefix used for resource names. Not used for projects if 'project_create' is null. | string | ✓ | | +| [organization](variables.tf#L88) | Organization details. | object({…}) | ✓ | | +| [prefix](variables.tf#L95) | Unique prefix used for resource names. Not used for projects if 'project_create' is null. | string | ✓ | | | [composer_config](variables.tf#L17) | | object({…}) | | {…} | | [data_force_destroy](variables.tf#L42) | Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage. | bool | | false | | [groups](variables.tf#L48) | Groups. | map(string) | | {…} | -| [location_config](variables.tf#L140) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | -| [network_config](variables.tf#L58) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | -| [project_create](variables.tf#L92) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | -| [project_id](variables.tf#L101) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | -| [project_services](variables.tf#L129) | List of core services enabled on all projects. | list(string) | | […] | +| [location_config](variables.tf#L148) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | +| [network_config](variables.tf#L58) | Network configurations to use. Specify a shared VPC to use, if null networks will be created in projects. | object({…}) | | {…} | +| [project_create](variables.tf#L100) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [project_id](variables.tf#L109) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | +| [project_services](variables.tf#L137) | List of core services enabled on all projects. | list(string) | | […] | ## Outputs | name | description | sensitive | |---|---|:---:| -| [VPC](outputs.tf#L75) | VPC networks. | | | [bigquery-datasets](outputs.tf#L17) | BigQuery datasets. | | -| [demo_commands](outputs.tf#L84) | Demo commands. | | +| [demo_commands](outputs.tf#L93) | Demo commands. | | | [gcs-buckets](outputs.tf#L28) | GCS buckets. | | | [kms_keys](outputs.tf#L42) | Cloud MKS keys. | | | [projects](outputs.tf#L47) | GCP Projects informations. | | +| [vpc_network](outputs.tf#L75) | VPC network. | | +| [vpc_subnet](outputs.tf#L84) | VPC subnetworks. | | ## TODOs From fb851a5afa588404e28990cad787575795f4a98f Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 17:45:33 +0100 Subject: [PATCH 087/132] fix tests --- tests/examples/data_solutions/dp-foundation/test_plan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/examples/data_solutions/dp-foundation/test_plan.py b/tests/examples/data_solutions/dp-foundation/test_plan.py index 10225f6f..fda3e7d4 100644 --- a/tests/examples/data_solutions/dp-foundation/test_plan.py +++ b/tests/examples/data_solutions/dp-foundation/test_plan.py @@ -23,5 +23,5 @@ 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) == 40 - assert len(resources) == 286 + assert len(modules) == 37 + assert len(resources) == 265 From 93fdcbf3358b062c8dbb03508507b49e9a7e9bf5 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 3 Feb 2022 17:52:02 +0100 Subject: [PATCH 088/132] fix tests --- tests/examples/data_solutions/dp-foundation/test_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/examples/data_solutions/dp-foundation/test_plan.py b/tests/examples/data_solutions/dp-foundation/test_plan.py index fda3e7d4..d2b50c74 100644 --- a/tests/examples/data_solutions/dp-foundation/test_plan.py +++ b/tests/examples/data_solutions/dp-foundation/test_plan.py @@ -24,4 +24,4 @@ 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) == 37 - assert len(resources) == 265 + assert len(resources) == 280 From 2cdea57954326cf878e1e1c0053fc01c1d133c0a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 4 Feb 2022 17:35:30 +0100 Subject: [PATCH 089/132] README refactor --- .../data-solutions/dp-foundation/README.md | 213 ++++++++++-------- 1 file changed, 117 insertions(+), 96 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 54a59b26..f1b8da2f 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -1,8 +1,8 @@ # Data Platform -This module implements an opinionated Data Platform (DP) Architecture that creates and setup projects (and related resources) to be used to create your DP. +This module implements an opinionated Data Platform (DP) Architecture that creates and setup projects and related resources that compose an end-to-end data environment. -The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Cloud Storage Buckets, BigQuery datasets, etc.) and then allow easy customizations to complete the implementation of the intended design. +The code is intentionally simple, as it's intended to provide a generic initial setup and then allow easy customizations to complete the implementation of the intended design. The following diagram is a high-level reference of the resources created and managed here: @@ -11,18 +11,22 @@ The following diagram is a high-level reference of the resources created and man A demo pipeline is also part of this example: it can be built and run on top of the foundational infrastructure to verify or test the setup quickly. ## Design overview and choices + Despite its simplicity, this stage implements the basics of a design that we've seen working well for various customers. -The approach adapts to different high-level requirements: -- boundaries for each step -- clear and defined actors -- least privilege principle -- rely on service account impersonification +The approach adapts to different high-level requirements: -The code in this example doesn't address Organization level configuration (Organization policy, VPC-SC, centralized logs). We expect to address those aspects on stages external to this script. +- boundaries for each step +- clearly defined actors +- least privilege principle +- rely on service account impersonation + +The code in this example doesn't address Organization-level configurations (Organization policy, VPC-SC, centralized logs). We expect those to be managed by automation stages external to this script like those in [FAST](../../../fast). ### Project structure + The DP is designed to rely on several projects, one project per data stage. The stages identified are: + - landing - load - data lake @@ -30,86 +34,88 @@ The DP is designed to rely on several projects, one project per data stage. The - transformation - exposure -This separation into projects allows adhering the least-privilege principle relying on project-level roles. +This separation into projects allows adhering to the least-privilege principle by using project-level roles. The script will create the following projects: -- **Landing** This project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery, or Cloud PubSub. Resource configured with 3-months lifecycle policy. -- **Load** This project is intended to load data from `landing` to the `data lake`. The load is made with minimal to zero transformation logic (mainly `cast`). This stage can anonymization/tokenization Personally Identifiable Information (PII). Alternatively, it can be done in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is recommended. -- **Data Lake** projects where data are stored. itìs composed of 3 layers that progressively process and define data: - - **L0 - Raw data** Structured Data, stored in the adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the images for Cloud Vision API stored in BigQuery). + +- **Landing** Used to store temporary data. Data is pushed to Cloud Storage, BigQuery, or Cloud PubSub. Resources are configured with a customizable lifecycle policy. +- **Load** Used to load data from landing to data lake. The load is made with minimal to zero transformation logic (mainly `cast`). Anonymization or tokenization of Personally Identifiable Information (PII) can be implemented here or in the transformation stage, depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is recommended. +- **Data Lake** Several projects distributed across 3 separate layers, to host progressively processed and refined data: + - **L0 - Raw data** Structured Data, stored in relevant formats: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the images for Cloud Vision API stored in BigQuery). - **L1 - Cleansed, aggregated and standardized data** - **L2 - Curated layer** - - **Playground** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers -- **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. -- **Transformation** This project is used to move data between layers of the Data Lake. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you recommend using Cloud Dataflow together with [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates). This stage can optionally be used to anonymiza/tokenize PII. -- **Exposure** This project is intended to host resources to share your processed data with external systems your data. For the porpuse of this example we leace this project empty. Depending on the access pattern, data can be presented on Cloud SQL, BigQuery, or Bigtable. For BigQuery data, we strongly suggest relying on [Authorized views](https://cloud.google.com/bigquery/docs/authorized-views). + - **Playground** Temporary tables that Data Analyst may use to perform R&D on data available in other Data Lake layers. +- **Orchestration** Used to host Cloud Composer, which orchestrates all tasks that move data across layers. +- **Transformation** Used to move data between Data Lake layers. We strongly suggest relying on BigQuery Engine to perform the transformations. If BigQuery doesn't have the features needed to perform your transformations, you can use Cloud Dataflow with [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates). This stage can also optionally anonymize or tokenize PII. +- **Exposure** Used to host resources that share processed data with external systems. Depending on the access pattern, data can be presented via Cloud SQL, BigQuery, or Bigtable. For BigQuery data, we strongly suggest relying on [Authorized views](https://cloud.google.com/bigquery/docs/authorized-views). ### Roles -We assign roles on resources at project level setting the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. + +We assign roles on resources at the project level, granting the appropriate role via groups for humans and individual principals for service accounts, according to best practices. ### Service accounts -Service Account creation follows the following principles: -- Each service account perform a single task having access to the minimum number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and the Data Lake L0 project) -- Each Service Account has the least privilege on each project. -#### Service Account Keys -The use of SAK within a data pipeline incurs several security risks, as these credentials, that could be leaked without oversight or control. This example relies on Service Account Impersonation to avoid the creation of private keys. +Service account creation follows the least privilege principle, performing a single task which requires access to a defined set of resources. For example, the Cloud Dataflow service account only has access to the landing project and the data lake L0 project. + +Using of service account keys within a data pipeline exposes to several security risks deriving from a credentials leak. This example shows how to leverage impersonation to avoid the need of creating keys. ### Groups -We use thress groups based on the required access: -- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to troubleshoot possible issues with the pipeline. The team also can impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. -- *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `playground` project. Default value: `gcp-data-analyst@DOMAIN.COM` -- *Data Security*: the group handling security configurations related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` + +We use three groups to control access to resources: + +- *Data Engineers* They handle and run the Data Hub, with read access to all resources in order to troubleshoot possible issues with pipelines. This team can also impersonate any service account. +- *Data Analyst*. They perform analysis on datasets, with read access to the data lake L2 project, and BigQuery READ/WRITE access to the playground project. - *Data Security*:. They handle security configurations related to the Data Hub. + ### Virtual Private Cloud (VPC) design -The DP accepts as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) to run resources. You can configure subnets for DP resources specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to host your resources if your pipelines may need to reach on-premise resources. -If `network_config` variable is not provided, the script will create a VPC on each project that requires a VPC: *load*, *transformation*, and *orchestration* projects with the default configuration. -### IP ranges, subnetting -To deploy your DP you need the following ranges: -- Load project VPC for Cloud Dataflow workers. Range: '/24'. -- Transformation VPC for Cloud Dataflow workers. Range: '/24'. -- Orchestration VPC for Cloud Composer: - - Cloud SQL. Range: '/24' - - GKE Master. Range: '/28' - - Web Server: Range: '/28' - - Secondary IP ranges. Pods range: '/22', Services range: '/24' +As is often the case in real-world configurations, this example accepts as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) via the `network_config` variable. + +If the `network_config` variable is not provided, one VPC will be created in each project that supports network resources (load, transformation and orchestration). + +### IP ranges and subnetting + +To deploy this example with self-managed VPCs you need the following ranges: + +- one /24 for the load project VPC subnet used for Cloud Dataflow workers +- one /24 for the transformation VPC subnet used for Cloud Dataflow workers +- one /24 range for the orchestration VPC subnet used for Composer workers +- one /22 and one /24 ranges for the secondary ranges associated with the orchestration VPC subnet + +If you are using Shared VPC, you need one subnet with one /22 and one /24 secondary range defined for Composer pods and services. + +In both VPC scenarios, you also need these ranges for Composer: + +- one /24 for Cloud SQL +- one /28 for the GKE control plane +- one /28 for the web server + +### Resource naming conventions -### Resource naming convention Resources in the script use the following acronyms: - - `lnd` for `landing` - - `lod` for `load` - - `orc` for `orchestration` - - `trf` for `transformation` - - `dtl` for `Data Lake` - - `cmn` for `common` - - `plg` for `playground` - - 2 letters acronym for GCP products, example: `bq` for `BigQuery`, `df` for `Cloud Dataflow`, ... + +- `lnd` for `landing` +- `lod` for `load` +- `orc` for `orchestration` +- `trf` for `transformation` +- `dtl` for `Data Lake` +- `cmn` for `common` +- `plg` for `playground` +- 2 letters acronym for GCP products, example: `bq` for `BigQuery`, `df` for `Cloud Dataflow`, ... Resources follow the naming convention described below. -Projects: -``` -PREFIX-LAYER -``` - -Services: -``` -PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER -``` - -Service Accounts: -``` -PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER -``` +- `prefix-layer` for projects +- `prefix-layer[2]-gcp-product[2]-counter` for services and service accounts ### Encryption -We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belong to a project external to the DP. + +We suggest a centralized approach to key management, where Security is the only team that can access encryption material, and keyrings and keys are managed in a project external to the DP. ![Centralized Cloud Key Management high-level diagram](./images/kms_diagram.png "Centralized Cloud Key Management high-level diagram") -To configure the use of Cloud Key Management on resources you have to specify the key URL on the 'service_encryption_keys'. Keys location should match the resource location. Example: +To configure the use of Cloud KMS on resources, you have to specify the key id on the `service_encryption_keys` variable. Key locations should match resource locations. Example: -``` +```hcl service_encryption_keys = { bq = "KEY_URL_MULTIREGIONAL" composer = "KEY_URL_REGIONAL" @@ -118,70 +124,82 @@ service_encryption_keys = { pubsub = "KEY_URL_MULTIREGIONAL" ``` -We consider this step optional, it depends on customer policy and security best practices. +This step is optional and depends on customer policies and security best practices. ## Data Anonymization -We suggest using Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. Implementing the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to implement the data loss prevention strategy. We expect you will use [Cloud Data Loss Prevention templates](https://cloud.google.com/dlp/docs/concepts-templates) in one of the following ways: -- During the ingestion phase, from Dataflow -- During the transformation phase, from [BigQuery](https://cloud.google.com/bigquery/docs/scan-with-dlp) or [Cloud Dataflow](https://cloud.google.com/architecture/running-automated-dataflow-pipeline-de-identify-pii-dataset) -We implemented a centralized model for Cloud Data Loss Prevention resources. Templates will be stored in the security project: +We suggest using Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. + +While implementing a Data Loss Prevention strategy is out of scope for this example, we enable the service in two different projects so that [Cloud Data Loss Prevention templates](https://cloud.google.com/dlp/docs/concepts-templates) can be configured in one of two ways: + +- during the ingestion phase, from Dataflow +- during the transformation phase, from [BigQuery](https://cloud.google.com/bigquery/docs/scan-with-dlp) or [Cloud Dataflow](https://cloud.google.com/architecture/running-automated-dataflow-pipeline-de-identify-pii-dataset) + +Cloud Data Loss Prevention resources and templates should be stored in the security project: ![Centralized Cloud Data Loss Prevention high-level diagram](./images/dlp_diagram.png "Centralized Cloud Data Loss Prevention high-level diagram") ## How to run this script + To deploy this example on your GCP organization, you will need - a folder or organization where new projects will be created - a billing account that will be associated with the new projects The DP is meant to be executed by a Service Account (or a regular user) having this minimal set of permission: -* **Org level**: - * `"compute.organizations.enableXpnResource"` - * `"compute.organizations.disableXpnResource"` - * `"compute.subnetworks.setIamPolicy"` -* **Folder level**: - * `"roles/logging.admin"` - * `"roles/owner"` - * `"roles/resourcemanager.folderAdmin"` - * `"roles/resourcemanager.projectCreator"` -* **Cloud Key Management Keys** (if Cloud Key Management keys are configured): - * `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicy` -* **on the host project** for the Shared VPC/s - * `"roles/browser"` - * `"roles/compute.viewer"` - * `"roles/dns.admin"` + +- **Org level**: + - `"compute.organizations.enableXpnResource"` + - `"compute.organizations.disableXpnResource"` + - `"compute.subnetworks.setIamPolicy"` +- **Folder level**: + - `"roles/logging.admin"` + - `"roles/owner"` + - `"roles/resourcemanager.folderAdmin"` + - `"roles/resourcemanager.projectCreator"` +- **Cloud Key Management Keys** (if Cloud Key Management keys are configured): + - `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicy` +- **On the host project** for the Shared VPC/s + - `"roles/browser"` + - `"roles/compute.viewer"` + - `"roles/dns.admin"` ## Variable configuration + There are three sets of variables you will need to fill in: -``` -prefix = "PRFX" +```hcl +prefix = "myco" project_create = { parent = "folders/123456789012" billing_account_id = "111111-222222-333333" } organization = { - domain = "DOMAIN.com" + domain = "domain.com" } ``` -For a more fine grained configuration, check variables on [`variables.tf`](./variables.tf) and update accordingly to the desired configuration. +For more fine details check variables on [`variables.tf`](./variables.tf) and update according to the desired configuration. ## Customizations + ### Create Cloud Key Management keys as part of the DP + To create Cloud Key Management keys in the DP you can uncomment the Cloud Key Management resources configured in the [`06-common.tf`](./06-common.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. ### Assign roles at BQ Dataset level -To handle multiple groups of `data-analysts` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project-level. + +To handle multiple groups of `data-analysts` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project-level. To do this, you need to remove IAM binging at project-level for the `data-analysts` group and give roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. ## Demo pipeline + The application layer is out of scope of this script, but as a demo, it is provided with a Cloud Composer DAG to mode data from the `landing` area to the `DataLake L2` dataset. Just follow the commands you find in the `demo_commands` Terraform output, go in the Cloud Composer UI and run the `data_pipeline_dag`. Description of commands: + - 01: copy sample data to a `landing` Cloud Storage bucket impersonating the `load` service account. - 02: copy sample data structure definition in the `orchestration` Cloud Storage bucket impersonating the `orchestration` service account. - 03: copy the Cloud Composer DAG to the Cloud Composer Storage bucket impersonating the `orchestration` service account. @@ -218,13 +236,16 @@ Description of commands: ## TODOs + Features to add in future releases: - * Add support for Column level access on BigQuery - * Add example templates for Data Catalog - * Add example on how to use Cloud Data Loss Prevention - * Add solution to handle Tables, Views, and Authorized Views lifecycle - * Add solution to handle Metadata lifecycle + +- Add support for Column level access on BigQuery +- Add example templates for Data Catalog +- Add example on how to use Cloud Data Loss Prevention +- Add solution to handle Tables, Views, and Authorized Views lifecycle +- Add solution to handle Metadata lifecycle ## To Test/Fix - * Composer require "Require OS Login" not enforced - * External Shared-VPC \ No newline at end of file + +- Composer require "Require OS Login" not enforced +- External Shared-VPC From b65d153ec14003f5eab25d00cb59d6f23a69fa95 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Sat, 5 Feb 2022 08:51:11 +0100 Subject: [PATCH 090/132] Replace existing data platform --- examples/README.md | 2 +- examples/data-solutions/README.md | 2 +- .../01-environment/README.md | 72 ----- .../01-environment/diagram.png | Bin 281880 -> 0 bytes .../01-environment/main.tf | 162 ---------- .../01-environment/outputs.tf | 36 --- .../01-environment/variables.tf | 82 ----- .../01-environment/versions.tf | 29 -- .../01-landing.tf | 0 .../01-storage-services.tf | 0 .../02-load.tf | 0 .../02-resources/README.md | 83 ------ .../02-resources/diagram.png | Bin 481113 -> 0 bytes .../02-resources/main.tf | 211 ------------- .../02-resources/outputs.tf | 60 ---- .../02-resources/providers.tf | 23 -- .../02-resources/variables.tf | 189 ------------ .../02-resources/versions.tf | 29 -- .../02-storage-services.tf | 0 .../03-composer.tf | 0 .../03-orchestration.tf | 0 .../03-pipeline/README.md | 8 - .../03-pipeline/gcs_to_bigquery.md | 140 --------- .../03-pipeline/pubsub_to_bigquery.md | 75 ----- .../03-pipeline/resource/raw_data.json | 26 -- .../03-storage-services.tf | 0 .../04-storage-services.tf | 0 .../04-transformation.tf | 0 .../05-datalake.tf | 0 .../05-storage-services.tf | 0 .../06-common.tf | 0 .../07-exposure.tf | 0 .../data-platform-foundations/README.md | 280 +++++++++++++++--- .../backend.tf.sample | 0 .../demo/README.md | 0 .../demo/data/customer_purchase.json | 0 .../demo/data/customers.csv | 0 .../demo/data/customers.json | 0 .../demo/data/customers_schema.json | 0 .../demo/data/customers_udf.js | 0 .../demo/data/purchases.csv | 0 .../demo/data/purchases.json | 0 .../demo/data/purchases_schema.json | 0 .../demo/data/purchases_udf.js | 0 .../demo/datapipeline.py | 0 .../images/dlp_diagram.png | Bin .../images/kms_diagram.png | Bin .../images/overview_diagram.png | Bin .../main.tf | 0 .../outputs.tf | 0 .../terraform.tfvars.sample | 0 .../variables.tf | 0 .../data-solutions/dp-foundation/README.md | 251 ---------------- 53 files changed, 237 insertions(+), 1523 deletions(-) delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/README.md delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/diagram.png delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/main.tf delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/outputs.tf delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/variables.tf delete mode 100644 examples/data-solutions/data-platform-foundations/01-environment/versions.tf rename examples/data-solutions/{dp-foundation => data-platform-foundations}/01-landing.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/01-storage-services.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/02-load.tf (100%) delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/README.md delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/diagram.png delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/main.tf delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/outputs.tf delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/providers.tf delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/variables.tf delete mode 100644 examples/data-solutions/data-platform-foundations/02-resources/versions.tf rename examples/data-solutions/{dp-foundation => data-platform-foundations}/02-storage-services.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/03-composer.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/03-orchestration.tf (100%) delete mode 100644 examples/data-solutions/data-platform-foundations/03-pipeline/README.md delete mode 100644 examples/data-solutions/data-platform-foundations/03-pipeline/gcs_to_bigquery.md delete mode 100644 examples/data-solutions/data-platform-foundations/03-pipeline/pubsub_to_bigquery.md delete mode 100644 examples/data-solutions/data-platform-foundations/03-pipeline/resource/raw_data.json rename examples/data-solutions/{dp-foundation => data-platform-foundations}/03-storage-services.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/04-storage-services.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/04-transformation.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/05-datalake.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/05-storage-services.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/06-common.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/07-exposure.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/backend.tf.sample (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/README.md (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/customer_purchase.json (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/customers.csv (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/customers.json (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/customers_schema.json (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/customers_udf.js (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/purchases.csv (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/purchases.json (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/purchases_schema.json (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/data/purchases_udf.js (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/demo/datapipeline.py (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/images/dlp_diagram.png (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/images/kms_diagram.png (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/images/overview_diagram.png (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/main.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/outputs.tf (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/terraform.tfvars.sample (100%) rename examples/data-solutions/{dp-foundation => data-platform-foundations}/variables.tf (100%) delete mode 100644 examples/data-solutions/dp-foundation/README.md diff --git a/examples/README.md b/examples/README.md index 70df2320..2a12dae1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,7 +5,7 @@ This section contains **[foundational examples](./foundations/)** that bootstrap Currently available examples: - **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](./cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management) -- **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms/), [Cloud Storage to Bigquery with Cloud Dataflow](./data-solutions/gcs-to-bq-with-dataflow/) +- **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms/), [Cloud Storage to Bigquery with Cloud Dataflow](./data-solutions/gcs-to-bq-with-dataflow/), [Data Platform Foundations](./data-solutions/data-platform-foundations/) - **factories** - [The why and the how of resource factories](./factories/README.md) - **foundations** - [single level hierarchy](./foundations/environments/) (environments), [multiple level hierarchy](./foundations/business-units/) (business units + environments) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) diff --git a/examples/data-solutions/README.md b/examples/data-solutions/README.md index d3007f85..ffae1f85 100644 --- a/examples/data-solutions/README.md +++ b/examples/data-solutions/README.md @@ -18,7 +18,7 @@ All resources use CMEK hosted in Cloud KMS running in a centralized project. The ### Data Platform Foundations - + This [example](./data-platform-foundations/) implements a robust and flexible Data Foundation on GCP that provides opinionated defaults, allowing customers to build and scale out additional data pipelines quickly and reliably.
diff --git a/examples/data-solutions/data-platform-foundations/01-environment/README.md b/examples/data-solutions/data-platform-foundations/01-environment/README.md deleted file mode 100644 index 5d097118..00000000 --- a/examples/data-solutions/data-platform-foundations/01-environment/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Data Platform Foundations - Environment (Step 1) - -This is the first step needed to deploy Data Platform Foundations, which creates projects and service accounts. Please refer to the [top-level Data Platform README](../README.md) for prerequisites. - -The projects that will be created are: - -- Common services -- Landing -- Orchestration & Transformation -- DWH -- Datamart - -A main service account named `projects-editor-sa` will be created under the common services project, and it will be granted editor permissions on all the projects in scope. - -This is a high level diagram of the created resources: - -![Environment - Phase 1](./diagram.png "High-level Environment diagram") - -## Running the example - -To create the infrastructure: - -- specify your variables in a `terraform.tvars` - -```tfm -billing_account = "1234-1234-1234" -parent = "folders/12345678" -admins = ["user:xxxxx@yyyyy.com"] -``` - -- make sure you have the right authentication setup (application default credentials, or a service account key) with the right permissions -- **The output of this stage contains the values for the resources stage** -- the `admins` variable contain a list of principals allowed to impersonate the service accounts. These principals will be given the `iam.serviceAccountTokenCreator` role -- run `terraform init` and `terraform apply` - -Once done testing, you can clean up resources by running `terraform destroy`. - -### CMEK configuration -You can configure GCP resources to use existing CMEK keys configuring the 'service_encryption_key_ids' variable. You need to specify a 'global' and a 'multiregional' key. - -### VPC-SC configuration -You can assign projects to an existing VPC-SC standard perimeter configuring the 'service_perimeter_standard' variable. You can retrieve the list of existing perimeters from the GCP console or using the following command: - -''' -gcloud access-context-manager perimeters list --format="json" | grep name -''' - -The script use 'google_access_context_manager_service_perimeter_resource' terraform resource. If this resource is used alongside the 'vpc-sc' module, remember to uncomment the lifecycle block in the 'vpc-sc' module so they don't fight over which resources should be in the perimeter. - - -## Variables - -| name | description | type | required | default | -|---|---|:---:|:---:|:---:| -| [billing_account_id](variables.tf#L21) | Billing account id. | string | ✓ | | -| [root_node](variables.tf#L50) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | ✓ | | -| [admins](variables.tf#L15) | List of users allowed to impersonate the service account. | list(string) | | null | -| [prefix](variables.tf#L26) | Prefix used to generate project id and name. | string | | null | -| [project_names](variables.tf#L32) | Override this variable if you need non-standard names. | object({…}) | | {…} | -| [service_account_names](variables.tf#L55) | Override this variable if you need non-standard names. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L65) | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({…}) | | {…} | -| [service_perimeter_standard](variables.tf#L78) | VPC Service control standard perimeter name in the form of 'accessPolicies/ACCESS_POLICY_NAME/servicePerimeters/PERIMETER_NAME'. All projects will be added to the perimeter in enforced mode. | string | | null | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| [project_ids](outputs.tf#L17) | Project ids for created projects. | | -| [service_account](outputs.tf#L28) | Main service account. | | -| [service_encryption_key_ids](outputs.tf#L33) | Cloud KMS encryption keys in {LOCATION => [KEY_URL]} format. | | - - diff --git a/examples/data-solutions/data-platform-foundations/01-environment/diagram.png b/examples/data-solutions/data-platform-foundations/01-environment/diagram.png deleted file mode 100644 index eb9508d8e46bccc5f38583d2b4c0de0882f99fd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281880 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 kQXC5aQ+{BO*p!VAz9vV`NPEW8&8g8oD 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 j!^1KyC`QRfTyYeEb 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 - -## Variables - -| name | description | type | required | default | -|---|---|:---:|:---:|:---:| -| [project_ids](variables.tf#L108) | Project IDs. | object({…}) | ✓ | | -| [admins](variables.tf#L16) | List of users allowed to impersonate the service account. | list(string) | | null | -| [datamart_bq_datasets](variables.tf#L22) | Datamart Bigquery datasets. | map(object({…})) | | {…} | -| [dwh_bq_datasets](variables.tf#L40) | DWH Bigquery datasets. | map(object({…})) | | {…} | -| [landing_buckets](variables.tf#L54) | List of landing buckets to create. | map(object({…})) | | {…} | -| [landing_pubsub](variables.tf#L72) | List of landing pubsub topics and subscriptions to create. | map(map(object({…}))) | | {…} | -| [landing_service_account](variables.tf#L102) | landing service accounts list. | string | | "sa-landing" | -| [service_account_names](variables.tf#L119) | Project service accounts list. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L137) | Cloud KMS encryption key in {LOCATION => [KEY_URL]} format. Keys belong to existing project. | object({…}) | | {…} | -| [transformation_buckets](variables.tf#L149) | List of transformation buckets to create. | map(object({…})) | | {…} | -| [transformation_subnets](variables.tf#L167) | List of subnets to create in the transformation Project. | list(object({…})) | | […] | -| [transformation_vpc_name](variables.tf#L185) | Name of the VPC created in the transformation Project. | string | | "transformation-vpc" | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| [datamart-datasets](outputs.tf#L17) | List of bigquery datasets created for the datamart project. | | -| [dwh-datasets](outputs.tf#L24) | List of bigquery datasets created for the dwh project. | | -| [landing-buckets](outputs.tf#L29) | List of buckets created for the landing project. | | -| [landing-pubsub](outputs.tf#L34) | List of pubsub topics and subscriptions created for the landing project. | | -| [transformation-buckets](outputs.tf#L44) | List of buckets created for the transformation project. | | -| [transformation-vpc](outputs.tf#L49) | Transformation VPC details. | | - - diff --git a/examples/data-solutions/data-platform-foundations/02-resources/diagram.png b/examples/data-solutions/data-platform-foundations/02-resources/diagram.png deleted file mode 100644 index 7a3393212d03a5331d898729b29fc87ec7d8d24a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 481113 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

G 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 P0{+fv8=iTgiH!Ik&?yTP literal 109088 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≶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 D?Q6k7 diff --git a/fast/stages/03-project-factory/README.md b/fast/stages/03-project-factory/README.md index 44b96b9a..2be41b95 100644 --- a/fast/stages/03-project-factory/README.md +++ b/fast/stages/03-project-factory/README.md @@ -3,4 +3,4 @@ The Project Factory (PF) builds on top of your foundations to create and set up projects (and related resources) to be used for your workloads. It is organized in folders representing environments (e.g. "dev", "prod"), each implemented by a stand-alone terraform [resource factory](https://medium.com/google-cloud/resource-factories-a-descriptive-approach-to-terraform-581b3ebb59c). -This directory contains a single project factory ([`prod/`](./prod/)) as an example - to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). +This directory contains a single project factory ([`prod/`](./prod/)) as an example - to implement multiple environments (e.g. "prod" and "dev") you'll need to copy the `prod` folder into one folder per environment, then customize each one following the instructions found in [`prod/README.md`](./prod/README.md). \ No newline at end of file From c4cf0759cd9661312000444a8c3921a64506341f Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Mon, 31 Jan 2022 17:11:06 +0100 Subject: [PATCH 058/132] Fix and test KMS, Fix and test existing prj (it works also with single prj), Update README --- .../dp-foundation/01-lnd-datastorage.tf | 8 +- .../dp-foundation/01-lnd-main.tf.tf | 8 +- .../dp-foundation/02-lod-datastorage.tf | 4 +- .../dp-foundation/02-lod-main.tf | 6 +- .../dp-foundation/02-lod-vpc.tf | 8 +- .../dp-foundation/03-orc-composer.tf | 8 +- .../dp-foundation/03-orc-datastorage.tf | 4 +- .../dp-foundation/03-orc-main.tf | 4 +- .../dp-foundation/03-orc-vpc.tf | 8 +- .../dp-foundation/04-trf-datastorage.tf | 4 +- .../dp-foundation/04-trf-main.tf | 4 +- .../dp-foundation/04-trf-vpc.tf | 8 +- .../dp-foundation/05-dtl-datastorage.tf | 32 ++--- .../dp-foundation/05-dtl-main.tf | 38 ++--- .../dp-foundation/06-sec-main.tf | 102 +++++-------- .../data-solutions/dp-foundation/README.md | 134 ++++++++++++------ examples/data-solutions/dp-foundation/main.tf | 15 +- .../data-solutions/dp-foundation/output.tf | 5 + .../data-solutions/dp-foundation/variables.tf | 44 +++--- 19 files changed, 247 insertions(+), 197 deletions(-) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index 8d4a4ec7..e006304d 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -39,10 +39,10 @@ module "lnd-cs-0" { project_id = module.lnd-prj.project_id name = "cs-0" prefix = local.prefix_lnd - location = var.region + location = var.location_config.region storage_class = "REGIONAL" # retention_policy = local.lnd_bucket_retention_policy - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -88,6 +88,6 @@ module "lnd-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.lnd-prj.project_id id = "${replace(local.prefix_lnd, "-", "_")}_bq_0" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 16155cfa..8c1d81c1 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -20,7 +20,7 @@ locals { "roles/storage.admin", "roles/storage.objectViewer", ], - "${local.groups.data-scientists}" = [ + "${local.groups.data-analysts}" = [ "roles/bigquery.dataViewer", "roles/bigquery.jobUser", "roles/bigquery.user", @@ -89,8 +89,8 @@ module "lnd-prj" { "storage-component.googleapis.com", ]) service_encryption_key_ids = { - bq = [try(var.service_encryption_keys.bq, null)] - pubsub = [try(var.service_encryption_keys.pubsub, null)] - storage = [try(var.service_encryption_keys.storage, null)] + bq = [try(local.service_encryption_keys.bq, null)] + pubsub = [try(local.service_encryption_keys.pubsub, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index 5f1f0b38..15d67f26 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -37,6 +37,6 @@ module "lod-cs-df-0" { name = "cs-0" prefix = local.prefix_lod storage_class = "REGIONAL" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null } diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index c413430a..ecb42dbf 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -75,8 +75,8 @@ module "lod-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - pubsub = [try(var.service_encryption_keys.pubsub, null)] - dataflow = [try(var.service_encryption_keys.dataflow, null)] - storage = [try(var.service_encryption_keys.storage, null)] + pubsub = [try(local.service_encryption_keys.pubsub, null)] + dataflow = [try(local.service_encryption_keys.dataflow, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf index e46b0a63..bd173341 100644 --- a/examples/data-solutions/dp-foundation/02-lod-vpc.tf +++ b/examples/data-solutions/dp-foundation/02-lod-vpc.tf @@ -20,12 +20,12 @@ module "lod-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" project_id = module.lod-prj.project_id - name = "${local.prefix_lod}-lod-vpc" + name = "${local.prefix_lod}-vpc" subnets = [ { ip_cidr_range = var.network_config.vpc_subnet_range.load - name = "subnet" - region = var.region + name = "${local.prefix_lod}-subnet" + region = var.location_config.region secondary_ip_range = {} } ] @@ -43,7 +43,7 @@ module "lod-nat" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.lod-prj.project_id - region = var.region + region = var.location_config.region name = "${local.prefix_lod}-default" router_network = module.lod-vpc[0].name } diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index ce3338f7..f20c2bbc 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -42,7 +42,7 @@ resource "google_composer_environment" "orc-cmp-0" { zone = "${var.composer_config.region}-b" service_account = module.orc-sa-cmp-0.email network = module.orc-vpc[0].self_link - subnetwork = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/subnet"] + subnetwork = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_orc}-subnet"] tags = ["composer-worker", "http-server", "https-server"] ip_allocation_policy { use_ip_aliases = "true" @@ -74,7 +74,7 @@ resource "google_composer_environment" "orc-cmp-0" { LOD_GCS_STAGING = module.lod-cs-df-0.url LOD_SA_DF = module.lod-sa-df-0.email NET_VPC = module.orc-vpc[0].self_link - NET_SUBNET = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/subnet"] + NET_SUBNET = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_orc}-subnet"] ORC_PRJ = module.orc-prj.project_id ORC_GCS = module.orc-cs-0.url TRF_PRJ = module.trf-prj.project_id @@ -91,9 +91,9 @@ resource "google_composer_environment" "orc-cmp-0" { } dynamic "encryption_config" { - for_each = var.service_encryption_keys != null ? { 1 = 1 } : {} + for_each = try(local.service_encryption_keys.composer != null, false) ? { 1 = 1 } : {} content { - kms_key_name = var.service_encryption_keys != null ? try(var.service_encryption_keys.composer, null) : null + kms_key_name = try(local.service_encryption_keys.composer != null, false) ? try(local.service_encryption_keys.composer, null) : null } } diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 5f271ad3..6b31bcd1 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -21,7 +21,7 @@ module "orc-cs-0" { project_id = module.orc-prj.project_id name = "cs-0" prefix = local.prefix_orc - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null } diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index 34ced946..9bfa301c 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -94,7 +94,7 @@ module "orc-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - composer = [try(var.service_encryption_keys.composer, null)] - storage = [try(var.service_encryption_keys.storage, null)] + composer = [try(local.service_encryption_keys.composer, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf index c962edb9..6f6052c9 100644 --- a/examples/data-solutions/dp-foundation/03-orc-vpc.tf +++ b/examples/data-solutions/dp-foundation/03-orc-vpc.tf @@ -20,12 +20,12 @@ module "orc-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" project_id = module.orc-prj.project_id - name = "${local.prefix_orc}-orc-vpc" + name = "${local.prefix_orc}-vpc" subnets = [ { ip_cidr_range = var.network_config.vpc_subnet_range.orchestration - name = "subnet" - region = var.region + name = "${local.prefix_orc}-subnet" + region = var.location_config.region secondary_ip_range = {} secondary_ip_range = { pods = var.composer_config.secondary_ip_range.pods @@ -47,7 +47,7 @@ module "orc-nat" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.orc-prj.project_id - region = var.region + region = var.location_config.region name = "${local.prefix_orc}-default" router_network = module.orc-vpc[0].name } diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index d079fb39..7028a939 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -37,9 +37,9 @@ module "trf-cs-df-0" { project_id = module.trf-prj.project_id name = "cs-0" prefix = local.prefix_trf - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null } ############################################################################### diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index 9170665f..a7080997 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -71,7 +71,7 @@ module "trf-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - dataflow = [try(var.service_encryption_keys.dataflow, null)] - storage = [try(var.service_encryption_keys.storage, null)] + dataflow = [try(local.service_encryption_keys.dataflow, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf index 874b8f49..773ab36d 100644 --- a/examples/data-solutions/dp-foundation/04-trf-vpc.tf +++ b/examples/data-solutions/dp-foundation/04-trf-vpc.tf @@ -20,12 +20,12 @@ module "trf-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" project_id = module.trf-prj.project_id - name = "${local.prefix_trf}-trf-vpc" + name = "${local.prefix_trf}-vpc" subnets = [ { ip_cidr_range = var.network_config.vpc_subnet_range.transformation - name = "subnet" - region = var.region + name = "${local.prefix_trf}-subnet" + region = var.location_config.region secondary_ip_range = {} } ] @@ -43,7 +43,7 @@ module "trf-nat" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-cloudnat" project_id = module.trf-prj.project_id - region = var.region + region = var.location_config.region name = "${local.prefix_trf}-default" router_network = module.trf-vpc[0].name } diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index 8fc66ffe..bd7a5e04 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -20,32 +20,32 @@ module "dtl-0-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-0-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_0_bq_0" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } module "dtl-1-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-1-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_1_bq_0" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } module "dtl-2-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-2-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_2_bq_0" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } module "dtl-exp-bq-0" { source = "../../../modules/bigquery-dataset" project_id = module.dtl-exp-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_exp_bq_0" - location = var.region - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.bq, null) : null + location = var.location_config.region + encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } ############################################################################### @@ -57,9 +57,9 @@ module "dtl-0-cs-0" { project_id = module.dtl-0-prj.project_id name = "0-cs-0" prefix = local.prefix_dtl - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -68,9 +68,9 @@ module "dtl-1-cs-0" { project_id = module.dtl-1-prj.project_id name = "1-cs-0" prefix = local.prefix_dtl - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -79,9 +79,9 @@ module "dtl-2-cs-0" { project_id = module.dtl-2-prj.project_id name = "2-cs-0" prefix = local.prefix_dtl - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } @@ -90,8 +90,8 @@ module "dtl-exp-cs-0" { project_id = module.dtl-exp-prj.project_id name = "exp-cs-0" prefix = local.prefix_dtl - location = var.region + location = var.location_config.region storage_class = "REGIONAL" - encryption_key = var.service_encryption_keys != null ? try(var.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null force_destroy = var.data_force_destroy } diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf index 882e0e08..9a6799e4 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-main.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -17,13 +17,15 @@ locals { "${local.groups.data-engineers}" = [ "roles/bigquery.dataEditor", "roles/storage.admin", - "roles/storage.objectCreator", - "roles/storage.objectViewer", - "roles/viewer", ], - # "${local.groups.data-scientists}" = [ - # "roles/bigquery.jobUser", - # ] + "${local.groups.data-analysts}" = [ + "roles/bigquery.dataViewer", + "roles/bigquery.jobUser", + "roles/bigquery.user", + "roles/datacatalog.viewer", + "roles/datacatalog.tagTemplateViewer", + "roles/storage.objectViewer", + ] } iam_dtl = { "roles/bigquery.dataEditor" = [ @@ -61,7 +63,7 @@ locals { module "dtl-0-prj" { source = "../../../modules/project" - name = "${var.project_id["datalake"]}-0" + name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-0" parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -83,14 +85,14 @@ module "dtl-0-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - bq = [try(var.service_encryption_keys.bq, null)] - storage = [try(var.service_encryption_keys.storage, null)] + bq = [try(local.service_encryption_keys.bq, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } module "dtl-1-prj" { source = "../../../modules/project" - name = "${var.project_id["datalake"]}-1" + name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-1" parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -112,14 +114,14 @@ module "dtl-1-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - bq = [try(var.service_encryption_keys.bq, null)] - storage = [try(var.service_encryption_keys.storage, null)] + bq = [try(local.service_encryption_keys.bq, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } module "dtl-2-prj" { source = "../../../modules/project" - name = "${var.project_id["datalake"]}-2" + name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-2" parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -141,14 +143,14 @@ module "dtl-2-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - bq = [try(var.service_encryption_keys.bq, null)] - storage = [try(var.service_encryption_keys.storage, null)] + bq = [try(local.service_encryption_keys.bq, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } module "dtl-exp-prj" { source = "../../../modules/project" - name = "${var.project_id["datalake"]}-exp" + name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-exp" parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -170,7 +172,7 @@ module "dtl-exp-prj" { "storage-component.googleapis.com" ]) service_encryption_key_ids = { - bq = [try(var.service_encryption_keys.bq, null)] - storage = [try(var.service_encryption_keys.storage, null)] + bq = [try(local.service_encryption_keys.bq, null)] + storage = [try(local.service_encryption_keys.storage, null)] } } diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf index d3707aba..ead97faa 100644 --- a/examples/data-solutions/dp-foundation/06-sec-main.tf +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -52,69 +52,43 @@ module "sec-prj" { ]) } -# Uncomment this section and assigne key links accondingly if you want -# to create a project with KMS and KMS keys +# Uncomment this section and assigne key links accondingly in local. variable +# if you want to create KMS keys in the security projet -# module "kms" { -# count = var.service_encryption_keys? 1 : 0 -# source = "../../../modules/kms" -# project_id = module.lnd-prj.project_id -# keyring = { -# name = "${var.prefix}-keyring", -# location = var.region -# } -# keys = { -# key-bq = null -# key-cmp = null -# key-df = null -# key-gcs = null -# key-ps = null -# } -# key_iam = { -# key-bq = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lnd-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.bq}", -# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.bq}", -# ] -# }, -# key-cmp = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.orc-prj.service_accounts.robots.artifactregistry}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.container-engine}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.compute}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.composer}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.pubsub}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", +module "sec-kms-0" { + source = "../../../modules/kms" + project_id = module.sec-prj.project_id + keyring = { + name = "${var.prefix}-kr-global", + location = var.location_config.region + } + keys = { + pubsub = null + } +} -# ] -# }, -# key-df = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lod-prj.service_accounts.robots.dataflow}", -# "serviceAccount:${module.lod-prj.service_accounts.robots.compute}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.dataflow}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.compute}", -# ] -# } -# key-gcs = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.dtl-0-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-1-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-2-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.dtl-exp-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.lnd-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.lod-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.orc-prj.service_accounts.robots.storage}", -# "serviceAccount:${module.trf-prj.service_accounts.robots.storage}", -# ] -# }, -# key-ps = { -# "roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ -# "serviceAccount:${module.lnd-prj.service_accounts.robots.pubsub}" -# ] -# } -# } -# } +module "sec-kms-1" { + source = "../../../modules/kms" + project_id = module.sec-prj.project_id + keyring = { + name = "${var.prefix}-kr-mregional", + location = var.location_config.region + } + keys = { + bq = null + storage = null + } +} + +module "sec-kms-2" { + source = "../../../modules/kms" + project_id = module.sec-prj.project_id + keyring = { + name = "${var.prefix}-kr-regional", + location = var.location_config.region + } + keys = { + composer = null + dataflow = null + } +} diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index fcbbe988..19cbb96a 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -1,17 +1,25 @@ # Data Platform -This module implement an opinionated Data Platform (DP) Architecture that create and set up projects (and related resources) to be used for your data workloads. +This module implement an opinionated Data Platform (DP) Architecture that create and set up projects (and related resources) to be used for to create your data platform. + +The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. + +The following diagram is a high level reference of the resources created and managed here: ![Data Platform Architecture overview](./images/overview_diagram.png "Data Platform Architecture overview") -# Design overview and choices #TODO -This is the Data Platform architecture we are going to deploy. -#TODO Add introduction +#TODO Rename secutiry to Core services +# Design overview and choices +Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. + +The approach adapts to different high level requirements: - boundaries for each step - help identify actors - help assign minimal roles -#TODO Rename secutiry to Core services +Additionally, a few critical benefits are directly provided by this design: +- adding a new set + ## Project structure The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different stages of the data journey and rely on project level roles. @@ -32,9 +40,7 @@ The following projects will be created: ## Roles We assigned roles on resources at Project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. -The only exception is for BigQuery dataset. We let you configure IAM at dataset level to host in the same infrastructure dataset that need access level segregation. - -## Service accounts #TODO +## Service accounts Service Account creation follow the following principals: - Each service account perform a single task aving access to the minimun number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) - Each Service Account has least privilage on each project. @@ -46,26 +52,50 @@ The use of SAK within a data pipeline incurs several security risks, as these ar Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identify a structured process to mitigate risks associated with the use of service account keys. -## Groups #TODO +## Groups As default groups, we identified the following actors: - *Data Engineers*: the group that handle and run the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issue with the pipeline. The team has also the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. - *Data Analyst*: the group that perform analysis on the dataset. The group has Read access to the Data Lake L2 project and Bigquery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` - *Data Security*: the project that handle security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` -## VPC design #TODO -The DP except as input an existing Shared-VPC to run resources. You can configure subsets for DP resource specifying the link to the subnet in the `` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. +## VPC design +The DP except as input an existing Shared-VPC to run resources. You can configure subsets for DP resource specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. -If no VPC configuration, the project will create a VPC on each project that require a VPC: *laod* project, *trasformation* project and *orchestration* project. -## IP ranges, subnetting #TODO +If `network_config` variable is not configured, the script will create a VPC on each project that require a VPC: *laod*, *trasformation* and *orchestration* projects with the default configuration. +## IP ranges, subnetting To run your DP resources you need the following ranges: -- Load project VPC for Dataflow. Range: '/24'. -- Transformation VPC for Dataflow. Range: '/24'. +- Load project VPC for Cloud Dataflow workers. Range: '/24'. +- Transformation VPC for Cloud Dataflow workers. Range: '/24'. - Orchestration VPC for Cloud Composer: - Cloud SQL. Range: '/24' - GKE Master. Range: '/28' - Web Server: Range: '/28' - Secondary ip ranges. Pods range: '/22', Services range: '/24' -## Resource naming convention #TODO +## Resource naming convention +Reousces in the script use the following acronims: + - `lnd` for `landing` + - `lod` for `load` + - `orc` for `orchestration` + - `trf` for `trasformation` + - `dtl` for `Data Lake` + - 2 letters acronym for GCP products, example: `bq` for `Bigquery`, `df` for `Cloud Dataflow`, ... + +Resources follow the naming convention described below. + +Projects: +``` +PREFIX-LAYER +``` + +Services: +``` +PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER +``` + +Service Accounts: +``` +PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER +``` ## Encryption We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. @@ -95,32 +125,54 @@ We implemented a centralized model for Data Loss Prevention material. Templates ![Centralized Cloud DLP high level diagram](./images/dlp_diagram.png "Centralized Cloud DLP high level diagram") # How to run this script #TODO -The Data Prlatform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: -* **Org level** - * TODO -* **Cloud KMS Keys** (if Cloud KMS keys are configured) - * TODO -* **Network** (if DP needs to rely on an existing Shared-VPC) - * TODO +The Data Platform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: +* **Org level**: + * `"compute.organizations.enableXpnResource"` + * `"compute.organizations.disableXpnResource"` + * `"compute.subnetworks.setIamPolicy"` +* **Folder level**: + * `"roles/logging.admin"` + * `"roles/owner"` + * `"roles/resourcemanager.folderAdmin"` + * `"roles/resourcemanager.projectCreator"` +* **Cloud KMS Keys** (if Cloud KMS keys are configured): + * `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicyTODO` +* **on the host project** for the Shared VPC/s + * `"roles/browser"` + * `"roles/compute.viewer"` + * `"roles/dns.admin"` -# Variable configuration #TODO +# Variable configuration +There are three sets of variables you will need to fill in: -# Customizations #TODO -Variables with default -Add internal KMS? -Parallel workstream +``` +prefix = "PRFX" +project_create = { + parent = "folders/123456789012" + billing_account_id = "111111-222222-333333" +} +organization = { + domain = "DOMAIN.com" +} +``` -# RAW notes, TO BE delete - - GCS and BQ regional - - KMS: Regional keyring, one key per product - - Composer require "Require OS Login" not enforced - - Groups: gcp-data-scientists, gcp-data-engineers, gcp-data-security +For a more fined grained configuration, check variables on [`variables.tf`](./variables.tf) and update accordingly to the desired configuration. - #TODO KMS: support key per product - #TODO Write README - #TODO Column level access on BQ - #TODO DataCatalog - #TODO DLP - #TODO DataLake layers: Tables, views and Authorized views - #TODO ShareVPC Role: roles/composer.sharedVpcAgent, roles/container.hostServiceAgentUser - #TODO Composer require "Require OS Login" not enforced \ No newline at end of file +# Customizations +## Create Cloud KMS as part of the Data Platform +To create Cloud KMS keys within the Data Platform you can uncomment the Cloud KMS resources configuraed in the [`06-sec-main.tf`](./06-sec-main.tf) file and update KMS keys pointers on `local.service_encryption_keys.*` to the local resource created. + +## Assign roles at BQ Dataset level +To handle multiple groups of `data-analyst` accessing the same Data Lake layer Projects but only to the dataset belonging to a specific group, you may want to assign roles at Bigquery dataset level instead of at project level. +To do this, you need to remove IAM binging at Project level for the `data-analyst` group and assign roles at Bigquery dataset level using the `iam` variable on `bigquery-dataset` modules. + +# TODOs + * Add support for Column level access on Bigquery + * Add example templates for DataCatalog + * Add example on how to use Cloud DLP + * Add solution to handle Tables, Views and Authorized Views lifecycle + * Add solution to handle Metadata lifecycle + +# Test/Fix + * Composer require "Require OS Login" not enforced + * External Shared-VPC \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf index 063f0a77..ea674d18 100644 --- a/examples/data-solutions/dp-foundation/main.tf +++ b/examples/data-solutions/dp-foundation/main.tf @@ -13,6 +13,17 @@ # limitations under the License. locals { - groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } - groups_iam = { for k, v in local.groups : k => "group:${v}" } + groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } + groups_iam = { for k, v in local.groups : k => "group:${v}" } + service_encryption_keys = var.service_encryption_keys + + # Uncomment this section and assigne comment the previous line + + # service_encryption_keys = { + # bq = module.sec-kms-1.key_ids.bq + # composer = module.sec-kms-2.key_ids.composer + # dataflow = module.sec-kms-2.key_ids.dataflow + # storage = module.sec-kms-1.key_ids.storage + # pubsub = module.sec-kms-0.key_ids.pubsub + # } } diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf index 52e993c8..e7722267 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/output.tf @@ -37,6 +37,11 @@ output "gcs-buckets" { } } +output "kms_keys" { + description = "Cloud MKS keys." + value = local.service_encryption_keys +} + output "projects" { description = "GCP Projects." value = { diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 9d18b86d..62db6cd6 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -12,18 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -variable "service_encryption_keys" { # service encription key - description = "Cloud KMS to use to encrypt different services. Key location should match service region." - type = object({ - bq = string - composer = string - dataflow = string - storage = string - pubsub = string - }) - default = null -} - variable "composer_config" { type = object({ ip_range_cloudsql = string @@ -57,9 +45,9 @@ variable "groups" { description = "Groups." type = map(string) default = { - data-engineers = "gcp-data-engineers" - data-scientists = "gcp-data-scientists" - data-security = "gcp-data-security" + data-analysts = "gcp-data-analysts" + data-engineers = "gcp-data-engineers" + data-security = "gcp-data-security" } } @@ -134,8 +122,26 @@ variable "project_services" { ] } -variable "region" { - description = "The region where resources will be deployed." - type = string - default = "europe-west1" +variable "location_config" { + description = "Locations where resources will be deployed. Map to configure region and multiregion specs." + type = object({ + region = string + multi_region = string + }) + default = { + region = "europe-west1" + multi_region = "eu" + } +} + +variable "service_encryption_keys" { # service encription key + description = "Cloud KMS to use to encrypt different services. Key location should match service region." + type = object({ + bq = string + composer = string + dataflow = string + storage = string + pubsub = string + }) + default = null } From 57770041878b4cdd2d50e93557b05f33cc8408b6 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Mon, 31 Jan 2022 18:58:55 +0100 Subject: [PATCH 059/132] Fix READM and Demo --- .../dp-foundation/01-lnd-main.tf.tf | 6 -- .../dp-foundation/03-orc-composer.tf | 4 +- .../dp-foundation/03-orc-main.tf | 32 +++++---- .../dp-foundation/04-trf-main.tf | 1 - .../dp-foundation/06-sec-main.tf | 70 +++++++++---------- .../data-solutions/dp-foundation/README.md | 15 +++- .../dp-foundation/demo/gcs2bq.py | 48 ++++++++++--- .../data-solutions/dp-foundation/output.tf | 15 ++-- .../dp-foundation/terraform.tfvars.sample | 8 +++ .../data-solutions/dp-foundation/variables.tf | 5 +- 10 files changed, 126 insertions(+), 78 deletions(-) create mode 100644 examples/data-solutions/dp-foundation/terraform.tfvars.sample diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 8c1d81c1..799f5115 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -19,12 +19,6 @@ locals { "roles/pubsub.editor", "roles/storage.admin", "roles/storage.objectViewer", - ], - "${local.groups.data-analysts}" = [ - "roles/bigquery.dataViewer", - "roles/bigquery.jobUser", - "roles/bigquery.user", - "roles/pubsub.viewer", ] } iam_lnd = { diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index f20c2bbc..35ba3df8 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -72,9 +72,9 @@ resource "google_composer_environment" "orc-cmp-0" { LND_PS = module.lnd-ps-0.id LOD_PRJ = module.lod-prj.project_id LOD_GCS_STAGING = module.lod-cs-df-0.url + LOD_NET_VPC = module.lod-vpc[0].self_link + LOD_NET_SUBNET = module.lod-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_lod}-subnet"] LOD_SA_DF = module.lod-sa-df-0.email - NET_VPC = module.orc-vpc[0].self_link - NET_SUBNET = module.orc-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_orc}-subnet"] ORC_PRJ = module.orc-prj.project_id ORC_GCS = module.orc-cs-0.url TRF_PRJ = module.trf-prj.project_id diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index 9bfa301c..a35ac64a 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -13,6 +13,22 @@ # limitations under the License. locals { + group_iam_orc = { + "${local.groups.data-engineers}" = [ + "roles/bigquery.dataEditor", + "roles/bigquery.jobUser", + "roles/cloudbuild.builds.editor", + "roles/composer.admin", + "roles/composer.environmentAndStorageObjectAdmin", + "roles/iap.httpsResourceAccessor", + "roles/iam.serviceAccountUser", + "roles/compute.networkUser", + "roles/storage.objectAdmin", + "roles/storage.admin", + "roles/compute.networkUser" + ] + } + iam_orc = { "roles/bigquery.dataEditor" = [ module.lod-sa-df-0.iam_email, @@ -49,21 +65,7 @@ locals { module.trf-sa-df-0.iam_email ] } - group_iam_orc = { - "${local.groups.data-engineers}" = [ - "roles/bigquery.dataEditor", - "roles/bigquery.jobUser", - "roles/cloudbuild.builds.editor", - "roles/composer.admin", - "roles/composer.environmentAndStorageObjectAdmin", - "roles/iap.httpsResourceAccessor", - "roles/iam.serviceAccountUser", - "roles/compute.networkUser", - "roles/storage.objectAdmin", - "roles/storage.admin", - "roles/compute.networkUser" - ] - } + prefix_orc = "${var.prefix}-orc" } diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index a7080997..7e361c6f 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -17,7 +17,6 @@ locals { "${local.groups.data-engineers}" = [ "roles/bigquery.jobUser", "roles/dataflow.admin", - "roles/viewer", ] } iam_trf = { diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf index ead97faa..a5eeed06 100644 --- a/examples/data-solutions/dp-foundation/06-sec-main.tf +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -55,40 +55,40 @@ module "sec-prj" { # Uncomment this section and assigne key links accondingly in local. variable # if you want to create KMS keys in the security projet -module "sec-kms-0" { - source = "../../../modules/kms" - project_id = module.sec-prj.project_id - keyring = { - name = "${var.prefix}-kr-global", - location = var.location_config.region - } - keys = { - pubsub = null - } -} +# module "sec-kms-0" { +# source = "../../../modules/kms" +# project_id = module.sec-prj.project_id +# keyring = { +# name = "${var.prefix}-kr-global", +# location = var.location_config.region +# } +# keys = { +# pubsub = null +# } +# } -module "sec-kms-1" { - source = "../../../modules/kms" - project_id = module.sec-prj.project_id - keyring = { - name = "${var.prefix}-kr-mregional", - location = var.location_config.region - } - keys = { - bq = null - storage = null - } -} +# module "sec-kms-1" { +# source = "../../../modules/kms" +# project_id = module.sec-prj.project_id +# keyring = { +# name = "${var.prefix}-kr-mregional", +# location = var.location_config.region +# } +# keys = { +# bq = null +# storage = null +# } +# } -module "sec-kms-2" { - source = "../../../modules/kms" - project_id = module.sec-prj.project_id - keyring = { - name = "${var.prefix}-kr-regional", - location = var.location_config.region - } - keys = { - composer = null - dataflow = null - } -} +# module "sec-kms-2" { +# source = "../../../modules/kms" +# project_id = module.sec-prj.project_id +# keyring = { +# name = "${var.prefix}-kr-regional", +# location = var.location_config.region +# } +# keys = { +# composer = null +# dataflow = null +# } +# } diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 19cbb96a..54c64ce7 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -8,7 +8,6 @@ The following diagram is a high level reference of the resources created and man ![Data Platform Architecture overview](./images/overview_diagram.png "Data Platform Architecture overview") -#TODO Rename secutiry to Core services # Design overview and choices Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. @@ -166,13 +165,25 @@ To create Cloud KMS keys within the Data Platform you can uncomment the Cloud KM To handle multiple groups of `data-analyst` accessing the same Data Lake layer Projects but only to the dataset belonging to a specific group, you may want to assign roles at Bigquery dataset level instead of at project level. To do this, you need to remove IAM binging at Project level for the `data-analyst` group and assign roles at Bigquery dataset level using the `iam` variable on `bigquery-dataset` modules. +# Demo pipeline +The application layer is out of scope of this script, but as a demo, it is provided a Cloud Composer DAG to mode data from the `landing` area to `DataLake L2` dataset. + +Just follow the commands you find in the `demo_commands` Terraform output, go in the Cloud Composer UI and run the `data_pipeline_dag`. + +Description of commands: +- 01: copy sample data to `landing` storage bucket impersonating the `load` service account. +- 02: copy sample data structure definition in the `orchestration` storage bucket impersonating the `orchestration` service account. +- 03: copy the Cloud Composer DAG to Cloud Composer storage bucket impersonating the `orchestration` service account. +- 04: Open the Cloud Composer Airflow UI and run the imported DAG. +- 05: Run the Bigquery query to see results. # TODOs +Features to add in futere releases: * Add support for Column level access on Bigquery * Add example templates for DataCatalog * Add example on how to use Cloud DLP * Add solution to handle Tables, Views and Authorized Views lifecycle * Add solution to handle Metadata lifecycle -# Test/Fix +# To Test/Fix * Composer require "Require OS Login" not enforced * External Shared-VPC \ No newline at end of file diff --git a/examples/data-solutions/dp-foundation/demo/gcs2bq.py b/examples/data-solutions/dp-foundation/demo/gcs2bq.py index b959f3e0..d5851fe9 100644 --- a/examples/data-solutions/dp-foundation/demo/gcs2bq.py +++ b/examples/data-solutions/dp-foundation/demo/gcs2bq.py @@ -38,11 +38,13 @@ DTL_L0_BQ_DATASET = os.environ.get("DTL_L0_BQ_DATASET") DTL_L0_PRJ = os.environ.get("DTL_L0_PRJ") DTL_L1_BQ_DATASET = os.environ.get("DTL_L1_BQ_DATASET") DTL_L1_PRJ = os.environ.get("DTL_L1_PRJ") +DTL_L2_BQ_DATASET = os.environ.get("DTL_L2_BQ_DATASET") +DTL_L2_PRJ = os.environ.get("DTL_L2_PRJ") LOD_PRJ = os.environ.get("LOD_PRJ") DF_ZONE = os.environ.get("GCP_REGION") + "-b" DF_REGION = BQ_REGION = os.environ.get("GCP_REGION") -NET_VPC = os.environ.get("NET_VPC") -NET_SUBNET = "https://www.googleapis.com/compute/v1/projects/lc-04-lod/regions/europe-west1/subnetworks/subnet" #"https://www.googleapis.com/compute/v1/" + os.environ.get("NET_SUBNET") +LOD_NET_VPC = os.environ.get("LOD_NET_VPC") +LOD_NET_SUBNET = os.environ.get("LOD_NET_SUBNET") LOD_SA_DF = os.environ.get("LOD_SA_DF") TRF_SA_DF = os.environ.get("TRF_SA_DF") TRF_SA_BQ = os.environ.get("TRF_SA_BQ") @@ -73,7 +75,7 @@ default_args = { 'stagingLocation': LOD_GCS_STAGING, 'tempLocation': LOD_GCS_STAGING + "/tmp", 'serviceAccountEmail': LOD_SA_DF, - 'subnetwork': NET_SUBNET, + 'subnetwork': LOD_NET_SUBNET, 'ipConfiguration': "WORKER_IP_PRIVATE" }, } @@ -102,7 +104,7 @@ with models.DAG( parameters={ "javascriptTextTransformFunctionName": "transform", "JSONPath": ORC_GCS + "/customers_schema.json", - "javascriptTextTransformGcsPath": LND_GCS + "/customers_udf.js", + "javascriptTextTransformGcsPath": ORC_GCS + "/customers_udf.js", "inputFilePattern": LND_GCS + "/customers.csv", "outputTable": DTL_L0_PRJ + ":"+DTL_L0_BQ_DATASET+".customers", "bigQueryLoadingTemporaryDirectory": LOD_GCS_STAGING + "/tmp/bq/", @@ -139,9 +141,9 @@ with models.DAG( p.item as item, p.price as price, p.timestamp as timestamp - FROM `lc-04-dtl-0.lc_04_dtl_0_bq_0.customers` c - JOIN `lc-04-dtl-0.lc_04_dtl_0_bq_0.purchases` p ON c.id = p.customer_id - """, + FROM `{dtl_0_prj}.{dtl_0_dataset}.customers` c + JOIN `{dtl_0_prj}.{dtl_0_dataset}.purchases` p ON c.id = p.customer_id + """.format(dtl_0_prj=DTL_L0_PRJ, dtl_0_dataset=DTL_L0_BQ_DATASET, ), 'destinationTable':{ 'projectId': DTL_L1_PRJ, 'datasetId': DTL_L1_BQ_DATASET, @@ -152,5 +154,35 @@ with models.DAG( }, impersonation_chain=[TRF_SA_BQ] ) + + l2_customer_purchase = BigQueryInsertJobOperator( + task_id='bq_l2_customer_purchase', + gcp_conn_id='bigquery_default', + project_id=TRF_PRJ, + location=BQ_REGION, + configuration={ + 'jobType':'QUERY', + 'writeDisposition':'WRITE_TRUNCATE', + 'query':{ + 'query':"""SELECT + customer_id, + purchase_id, + name, + surname, + item, + price, + timestamp + FROM `{dtl_1_prj}.{dtl_1_dataset}.customer_purchase` + """.format(dtl_1_prj=DTL_L1_PRJ, dtl_1_dataset=DTL_L1_BQ_DATASET, ), + 'destinationTable':{ + 'projectId': DTL_L2_PRJ, + 'datasetId': DTL_L2_BQ_DATASET, + 'tableId': 'customer_purchase' + }, + "useLegacySql": False + } + }, + impersonation_chain=[TRF_SA_BQ] + ) - start >> [customers_import, purchases_import] >> join_customer_purchase >> end + start >> [customers_import, purchases_import] >> join_customer_purchase >> l2_customer_purchase >> end diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf index e7722267..8d897196 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/output.tf @@ -65,14 +65,15 @@ output "VPC" { } } -output "ZZZ_demo_commands" { +output "demo_commands" { description = "Demo commands" value = { - 01 = "gsutil -i ${module.lod-sa-df-0.email} cp demo/data/*.csv gs://${module.lnd-cs-0.name}" - 02 = "gsutil cp demo/data/*.j* gs://${module.orc-cs-0.name}" - 03 = "gsutil cp demo/gcs2bq.py ${google_composer_environment.orc-cmp-0.config[0].dag_gcs_prefix}/" - 04 = "bq mk --table --description 'Customers table' ${module.dtl-0-prj.project_id}:${module.dtl-0-bq-0.dataset_id}.customers demo/data/customers.json" - 05 = "bq mk --table --description 'Purchases table' ${module.dtl-0-prj.project_id}:${module.dtl-0-bq-0.dataset_id}.purchases demo/data/purchases.json" - 06 = "bq mk --table --description 'Customer_Purchase table' ${module.dtl-1-prj.project_id}:${module.dtl-1-bq-0.dataset_id}.customers demo/data/customer_purchase.json" + 01 = "gsutil -i ${module.lnd-sa-cs-0.email} cp demo/data/*.csv gs://${module.lnd-cs-0.name}" + 02 = "gsutil -i ${module.orc-sa-cmp-0.email} cp demo/data/*.j* gs://${module.orc-cs-0.name}" + 03 = "gsutil -i ${module.orc-sa-cmp-0.email} cp demo/gcs2bq.py ${google_composer_environment.orc-cmp-0.config[0].dag_gcs_prefix}/" + 04 = "Open: ${google_composer_environment.orc-cmp-0.config.0.airflow_uri}" + 05 = < Date: Tue, 1 Feb 2022 06:41:33 +0100 Subject: [PATCH 060/132] add on TF files --- examples/data-solutions/dp-foundation/01-lnd-datastorage.tf | 2 ++ examples/data-solutions/dp-foundation/01-lnd-main.tf.tf | 2 ++ examples/data-solutions/dp-foundation/02-lod-datastorage.tf | 2 ++ examples/data-solutions/dp-foundation/02-lod-main.tf | 2 ++ examples/data-solutions/dp-foundation/02-lod-vpc.tf | 2 ++ examples/data-solutions/dp-foundation/03-orc-composer.tf | 2 ++ examples/data-solutions/dp-foundation/03-orc-datastorage.tf | 2 ++ examples/data-solutions/dp-foundation/03-orc-main.tf | 2 ++ examples/data-solutions/dp-foundation/03-orc-vpc.tf | 2 ++ examples/data-solutions/dp-foundation/04-trf-datastorage.tf | 2 ++ examples/data-solutions/dp-foundation/04-trf-main.tf | 2 ++ examples/data-solutions/dp-foundation/04-trf-vpc.tf | 2 ++ examples/data-solutions/dp-foundation/05-dtl-datastorage.tf | 2 ++ examples/data-solutions/dp-foundation/05-dtl-main.tf | 2 ++ examples/data-solutions/dp-foundation/06-sec-main.tf | 2 ++ examples/data-solutions/dp-foundation/main.tf | 2 ++ examples/data-solutions/dp-foundation/output.tf | 2 ++ examples/data-solutions/dp-foundation/variables.tf | 2 ++ 18 files changed, 36 insertions(+) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index e006304d..0a52ed5a 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Landing storage resources (Bigquery, Cloud PubSub, Cloud Storage) + locals { lnd_bucket_retention_policy = { retention_period = 7776000 # 90 * 24 * 60 * 60 diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 799f5115..01111ef1 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Landing project + locals { group_iam_lnd = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index 15d67f26..2e46f02c 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Load storage resources (Cloud Storage) + ############################################################################### # GCS # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index ecb42dbf..159bfecd 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Load project + locals { group_iam_lod = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf index bd173341..55adef8c 100644 --- a/examples/data-solutions/dp-foundation/02-lod-vpc.tf +++ b/examples/data-solutions/dp-foundation/02-lod-vpc.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Load VPC + ############################################################################### # Network # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index 35ba3df8..ca563b26 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Orchestration Cloud Composer definition + ############################################################################### # Composer # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 6b31bcd1..5428e793 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Orchestration storage resources (Cloud Storage) + ############################################################################### # GCS # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index a35ac64a..468987bc 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Orchestration project + locals { group_iam_orc = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf index 6f6052c9..baeca586 100644 --- a/examples/data-solutions/dp-foundation/03-orc-vpc.tf +++ b/examples/data-solutions/dp-foundation/03-orc-vpc.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Orchestration VPC + ############################################################################### # Network # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index 7028a939..139cd746 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Landing storage resources (Bigquery, Cloud Storage) + ############################################################################### # GCS # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index 7e361c6f..81ebb14c 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Trasformation project + locals { group_iam_trf = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf index 773ab36d..ef7bd8dc 100644 --- a/examples/data-solutions/dp-foundation/04-trf-vpc.tf +++ b/examples/data-solutions/dp-foundation/04-trf-vpc.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Trasformation VPC + ############################################################################### # Network # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index bd7a5e04..55b9dbad 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Datalake storage resources (Bigquery, Cloud Storage) + ############################################################################### # BQ # ############################################################################### diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf index 9a6799e4..4760f1d6 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-main.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Datalake projects + locals { group_iam_dtl = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf index a5eeed06..eb527508 100644 --- a/examples/data-solutions/dp-foundation/06-sec-main.tf +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description Security project + locals { group_iam_sec = { "${local.groups.data-engineers}" = [ diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf index ea674d18..abcfaa4b 100644 --- a/examples/data-solutions/dp-foundation/main.tf +++ b/examples/data-solutions/dp-foundation/main.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description TODO + locals { groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } groups_iam = { for k, v in local.groups : k => "group:${v}" } diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf index 8d897196..ce33bdfe 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/output.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description TODO + output "bigquery-datasets" { description = "BigQuery datasets." value = { diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index ac514992..3f383a86 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# tfdoc:file:description TODO + variable "composer_config" { type = object({ ip_range_cloudsql = string From 5021592ec3d73437e2db63904339702dfe4e1aa2 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 06:57:13 +0100 Subject: [PATCH 061/132] Remove block comments --- .../dp-foundation/01-lnd-datastorage.tf | 13 ++++--------- .../data-solutions/dp-foundation/01-lnd-main.tf.tf | 4 +--- .../dp-foundation/02-lod-datastorage.tf | 4 +--- .../data-solutions/dp-foundation/02-lod-main.tf | 4 +--- examples/data-solutions/dp-foundation/02-lod-vpc.tf | 4 ---- .../data-solutions/dp-foundation/03-orc-composer.tf | 4 ---- .../dp-foundation/03-orc-datastorage.tf | 4 +--- examples/data-solutions/dp-foundation/03-orc-vpc.tf | 4 ---- .../dp-foundation/04-trf-datastorage.tf | 8 ++------ .../data-solutions/dp-foundation/04-trf-main.tf | 4 +--- examples/data-solutions/dp-foundation/04-trf-vpc.tf | 4 ---- .../dp-foundation/05-dtl-datastorage.tf | 8 ++------ .../data-solutions/dp-foundation/05-dtl-main.tf | 4 +--- .../data-solutions/dp-foundation/06-sec-main.tf | 4 +--- 14 files changed, 15 insertions(+), 58 deletions(-) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index 0a52ed5a..a9ae8dfe 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -20,9 +20,8 @@ locals { is_locked = false } } -############################################################################### -# GCS # -############################################################################### + +# Cloud Storage resources module "lnd-sa-cs-0" { source = "../../../modules/iam-service-account" @@ -48,9 +47,7 @@ module "lnd-cs-0" { force_destroy = var.data_force_destroy } -############################################################################### -# PubSub # -############################################################################### +# Cloud PubSub resources module "lnd-sa-ps-0" { source = "../../../modules/iam-service-account" @@ -70,9 +67,7 @@ module "lnd-ps-0" { name = "${local.prefix_lnd}-ps-0" } -############################################################################### -# BigQuery # -############################################################################### +# Bigquery resources module "lnd-sa-bq-0" { source = "../../../modules/iam-service-account" diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 01111ef1..60bab8f1 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -60,9 +60,7 @@ locals { prefix_lnd = "${var.prefix}-lnd" } -############################################################################### -# Project # -############################################################################### +# Project module "lnd-prj" { source = "../../../modules/project" diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index 2e46f02c..c8147cba 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -14,9 +14,7 @@ # tfdoc:file:description Load storage resources (Cloud Storage) -############################################################################### -# GCS # -############################################################################### +# Cloud Storage module "lod-sa-df-0" { source = "../../../modules/iam-service-account" diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index 159bfecd..7b303b9c 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -48,9 +48,7 @@ locals { prefix_lod = "${var.prefix}-lod" } -############################################################################### -# Project # -############################################################################### +# Project module "lod-prj" { source = "../../../modules/project" diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf index 55adef8c..8b00eee8 100644 --- a/examples/data-solutions/dp-foundation/02-lod-vpc.tf +++ b/examples/data-solutions/dp-foundation/02-lod-vpc.tf @@ -14,10 +14,6 @@ # tfdoc:file:description Load VPC -############################################################################### -# Network # -############################################################################### - module "lod-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index ca563b26..1b5996bd 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -14,10 +14,6 @@ # tfdoc:file:description Orchestration Cloud Composer definition -############################################################################### -# Composer # -############################################################################### - module "orc-sa-cmp-0" { source = "../../../modules/iam-service-account" project_id = module.orc-prj.project_id diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 5428e793..9cf47f90 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -14,9 +14,7 @@ # tfdoc:file:description Orchestration storage resources (Cloud Storage) -############################################################################### -# GCS # -############################################################################### +# Cloud Storage module "orc-cs-0" { source = "../../../modules/gcs" diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf index baeca586..09d2db1f 100644 --- a/examples/data-solutions/dp-foundation/03-orc-vpc.tf +++ b/examples/data-solutions/dp-foundation/03-orc-vpc.tf @@ -14,10 +14,6 @@ # tfdoc:file:description Orchestration VPC -############################################################################### -# Network # -############################################################################### - module "orc-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index 139cd746..f6efb101 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -14,9 +14,7 @@ # tfdoc:file:description Landing storage resources (Bigquery, Cloud Storage) -############################################################################### -# GCS # -############################################################################### +# Cloud Storage module "trf-sa-df-0" { source = "../../../modules/iam-service-account" @@ -44,9 +42,7 @@ module "trf-cs-df-0" { encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null } -############################################################################### -# BQ # -############################################################################### +# Bigquery module "trf-sa-bq-0" { source = "../../../modules/iam-service-account" diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index 81ebb14c..8bca4f5b 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -43,9 +43,7 @@ locals { prefix_trf = "${var.prefix}-trf" } -############################################################################### -# Project # -############################################################################### +# Project module "trf-prj" { source = "../../../modules/project" diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf index ef7bd8dc..6250ab45 100644 --- a/examples/data-solutions/dp-foundation/04-trf-vpc.tf +++ b/examples/data-solutions/dp-foundation/04-trf-vpc.tf @@ -14,10 +14,6 @@ # tfdoc:file:description Trasformation VPC -############################################################################### -# Network # -############################################################################### - module "trf-vpc" { count = var.network_config.network != null ? 0 : 1 source = "../../../modules/net-vpc" diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index 55b9dbad..f1ac21fb 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -14,9 +14,7 @@ # tfdoc:file:description Datalake storage resources (Bigquery, Cloud Storage) -############################################################################### -# BQ # -############################################################################### +# Bigquery module "dtl-0-bq-0" { source = "../../../modules/bigquery-dataset" @@ -50,9 +48,7 @@ module "dtl-exp-bq-0" { encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null } -############################################################################### -# GCS # -############################################################################### +# Cloud storage module "dtl-0-cs-0" { source = "../../../modules/gcs" diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf index 4760f1d6..b0a6e5ab 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-main.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -59,9 +59,7 @@ locals { prefix_dtl = "${var.prefix}-dtl" } -############################################################################### -# Project # -############################################################################### +# Project module "dtl-0-prj" { source = "../../../modules/project" diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf index eb527508..dfd11d48 100644 --- a/examples/data-solutions/dp-foundation/06-sec-main.tf +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -34,9 +34,7 @@ locals { prefix_sec = "${var.prefix}-sec" } -############################################################################### -# Project # -############################################################################### +# Project module "sec-prj" { source = "../../../modules/project" From 7bcaa86703c8c0b2500329dbe07a2228c236f42d Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 07:09:25 +0100 Subject: [PATCH 062/132] simplify service_encryption_keys logic --- .../dp-foundation/01-lnd-datastorage.tf | 4 ++-- .../dp-foundation/02-lod-datastorage.tf | 2 +- .../dp-foundation/03-orc-composer.tf | 2 +- .../dp-foundation/03-orc-datastorage.tf | 2 +- .../dp-foundation/04-trf-datastorage.tf | 2 +- .../dp-foundation/05-dtl-datastorage.tf | 16 ++++++++-------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index a9ae8dfe..b9d7716a 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -43,7 +43,7 @@ module "lnd-cs-0" { location = var.location_config.region storage_class = "REGIONAL" # retention_policy = local.lnd_bucket_retention_policy - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) force_destroy = var.data_force_destroy } @@ -86,5 +86,5 @@ module "lnd-bq-0" { project_id = module.lnd-prj.project_id id = "${replace(local.prefix_lnd, "-", "_")}_bq_0" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null + encryption_key = try(local.service_encryption_keys.bq, null) } diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index c8147cba..af293f9a 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -38,5 +38,5 @@ module "lod-cs-df-0" { prefix = local.prefix_lod storage_class = "REGIONAL" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) } diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index 1b5996bd..bd87b3ac 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -91,7 +91,7 @@ resource "google_composer_environment" "orc-cmp-0" { dynamic "encryption_config" { for_each = try(local.service_encryption_keys.composer != null, false) ? { 1 = 1 } : {} content { - kms_key_name = try(local.service_encryption_keys.composer != null, false) ? try(local.service_encryption_keys.composer, null) : null + kms_key_name = try(local.service_encryption_keys.composer, null) } } diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 9cf47f90..63c60e85 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -23,5 +23,5 @@ module "orc-cs-0" { prefix = local.prefix_orc location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) } diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index f6efb101..96a0ae31 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -39,7 +39,7 @@ module "trf-cs-df-0" { prefix = local.prefix_trf location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) } # Bigquery diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index f1ac21fb..8d9308dd 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -21,7 +21,7 @@ module "dtl-0-bq-0" { project_id = module.dtl-0-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_0_bq_0" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null + encryption_key = try(local.service_encryption_keys.bq, null) } module "dtl-1-bq-0" { @@ -29,7 +29,7 @@ module "dtl-1-bq-0" { project_id = module.dtl-1-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_1_bq_0" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null + encryption_key = try(local.service_encryption_keys.bq, null) } module "dtl-2-bq-0" { @@ -37,7 +37,7 @@ module "dtl-2-bq-0" { project_id = module.dtl-2-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_2_bq_0" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null + encryption_key = try(local.service_encryption_keys.bq, null) } module "dtl-exp-bq-0" { @@ -45,7 +45,7 @@ module "dtl-exp-bq-0" { project_id = module.dtl-exp-prj.project_id id = "${replace(local.prefix_dtl, "-", "_")}_exp_bq_0" location = var.location_config.region - encryption_key = try(local.service_encryption_keys.bq != null, false) ? try(local.service_encryption_keys.bq, null) : null + encryption_key = try(local.service_encryption_keys.bq, null) } # Cloud storage @@ -57,7 +57,7 @@ module "dtl-0-cs-0" { prefix = local.prefix_dtl location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) force_destroy = var.data_force_destroy } @@ -68,7 +68,7 @@ module "dtl-1-cs-0" { prefix = local.prefix_dtl location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) force_destroy = var.data_force_destroy } @@ -79,7 +79,7 @@ module "dtl-2-cs-0" { prefix = local.prefix_dtl location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) force_destroy = var.data_force_destroy } @@ -90,6 +90,6 @@ module "dtl-exp-cs-0" { prefix = local.prefix_dtl location = var.location_config.region storage_class = "REGIONAL" - encryption_key = try(local.service_encryption_keys.storage != null, false) ? try(local.service_encryption_keys.storage, null) : null + encryption_key = try(local.service_encryption_keys.storage, null) force_destroy = var.data_force_destroy } From 97b0d57053fa892c14352cf0a29fd7f67b54d621 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 07:22:14 +0100 Subject: [PATCH 063/132] fix README --- examples/data-solutions/dp-foundation/README.md | 2 +- examples/data-solutions/dp-foundation/demo/README.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 54c64ce7..fced8a48 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -25,7 +25,7 @@ The DP is designed to rely on several projects, one prj per data stage. This is The following projects will be created: * **Landing** This Project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. -* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transofmation stage depending on your requirements. +* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transofmation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. * **Data Lake** Those projects is intended to store your data. It reppresents where data will be persisted within 3 Layers. These layers reppresent different stages where data is processed and progressivly refined * **L0 - Raw data** Structured Data, stored in adeguate format: structured data stored in bigquery, unstructured data stored on Cloud Storage with additional metadata stored in Bigquery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in Bigquery). diff --git a/examples/data-solutions/dp-foundation/demo/README.md b/examples/data-solutions/dp-foundation/demo/README.md index 86a96f8f..299ff8e0 100644 --- a/examples/data-solutions/dp-foundation/demo/README.md +++ b/examples/data-solutions/dp-foundation/demo/README.md @@ -1 +1,3 @@ -gsutil \ No newline at end of file +# Data ingestion Demo + +In this folder you can find an example to ingest data on the `data platfoem` instanciated in [here](../). See details in the [README.m](../#demo-pipeline) to run the demo. \ No newline at end of file From c533642b8591de71b42c832717a683737675970b Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 07:25:00 +0100 Subject: [PATCH 064/132] Fix TODOs --- examples/data-solutions/dp-foundation/README.md | 4 ++-- examples/data-solutions/dp-foundation/main.tf | 2 +- examples/data-solutions/dp-foundation/output.tf | 2 +- examples/data-solutions/dp-foundation/variables.tf | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index fced8a48..c982ae59 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -123,7 +123,7 @@ We implemented a centralized model for Data Loss Prevention material. Templates ![Centralized Cloud DLP high level diagram](./images/dlp_diagram.png "Centralized Cloud DLP high level diagram") -# How to run this script #TODO +# How to run this script The Data Platform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: * **Org level**: * `"compute.organizations.enableXpnResource"` @@ -135,7 +135,7 @@ The Data Platform is meant to be executed by a Service Account (or a regular use * `"roles/resourcemanager.folderAdmin"` * `"roles/resourcemanager.projectCreator"` * **Cloud KMS Keys** (if Cloud KMS keys are configured): - * `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicyTODO` + * `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicy` * **on the host project** for the Shared VPC/s * `"roles/browser"` * `"roles/compute.viewer"` diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf index abcfaa4b..02373c7c 100644 --- a/examples/data-solutions/dp-foundation/main.tf +++ b/examples/data-solutions/dp-foundation/main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description TODO +# tfdoc:file:description Core locals locals { groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf index ce33bdfe..5738c3b4 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/output.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description TODO +# tfdoc:file:description Output variables output "bigquery-datasets" { description = "BigQuery datasets." diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 3f383a86..59d4906c 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description TODO +# tfdoc:file:description Terraform Variables variable "composer_config" { type = object({ From 3b9ecddfee024a15fb469d68a33ad94f5f94f065 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 09:09:53 +0100 Subject: [PATCH 065/132] fix tfdoc description --- examples/data-solutions/dp-foundation/01-lnd-datastorage.tf | 2 +- examples/data-solutions/dp-foundation/01-lnd-main.tf.tf | 2 +- examples/data-solutions/dp-foundation/02-lod-datastorage.tf | 2 +- examples/data-solutions/dp-foundation/02-lod-main.tf | 2 +- examples/data-solutions/dp-foundation/02-lod-vpc.tf | 2 +- examples/data-solutions/dp-foundation/03-orc-composer.tf | 2 +- examples/data-solutions/dp-foundation/03-orc-datastorage.tf | 2 +- examples/data-solutions/dp-foundation/03-orc-main.tf | 2 +- examples/data-solutions/dp-foundation/03-orc-vpc.tf | 2 +- examples/data-solutions/dp-foundation/04-trf-datastorage.tf | 2 +- examples/data-solutions/dp-foundation/04-trf-main.tf | 2 +- examples/data-solutions/dp-foundation/04-trf-vpc.tf | 2 +- examples/data-solutions/dp-foundation/05-dtl-datastorage.tf | 2 +- examples/data-solutions/dp-foundation/05-dtl-main.tf | 2 +- examples/data-solutions/dp-foundation/06-sec-main.tf | 2 +- examples/data-solutions/dp-foundation/main.tf | 2 +- examples/data-solutions/dp-foundation/output.tf | 2 +- examples/data-solutions/dp-foundation/variables.tf | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf index b9d7716a..066aea51 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Landing storage resources (Bigquery, Cloud PubSub, Cloud Storage) +# tfdoc:file:description Landing storage resources (Bigquery, Cloud PubSub, Cloud Storage). locals { lnd_bucket_retention_policy = { diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf index 60bab8f1..0436de94 100644 --- a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf +++ b/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Landing project +# tfdoc:file:description Landing project. locals { group_iam_lnd = { diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf index af293f9a..d6922c0b 100644 --- a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf +++ b/examples/data-solutions/dp-foundation/02-lod-datastorage.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Load storage resources (Cloud Storage) +# tfdoc:file:description Load storage resources (Cloud Storage). # Cloud Storage diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-lod-main.tf index 7b303b9c..33d827e4 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-lod-main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Load project +# tfdoc:file:description Load project. locals { group_iam_lod = { diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf index 8b00eee8..1bc60cf2 100644 --- a/examples/data-solutions/dp-foundation/02-lod-vpc.tf +++ b/examples/data-solutions/dp-foundation/02-lod-vpc.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Load VPC +# tfdoc:file:description Load VPC. module "lod-vpc" { count = var.network_config.network != null ? 0 : 1 diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-orc-composer.tf index bd87b3ac..c877a769 100644 --- a/examples/data-solutions/dp-foundation/03-orc-composer.tf +++ b/examples/data-solutions/dp-foundation/03-orc-composer.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Orchestration Cloud Composer definition +# tfdoc:file:description Orchestration Cloud Composer definition. module "orc-sa-cmp-0" { source = "../../../modules/iam-service-account" diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf index 63c60e85..efc674e4 100644 --- a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf +++ b/examples/data-solutions/dp-foundation/03-orc-datastorage.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Orchestration storage resources (Cloud Storage) +# tfdoc:file:description Orchestration storage resources (Cloud Storage). # Cloud Storage diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orc-main.tf index 468987bc..afd407b4 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orc-main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Orchestration project +# tfdoc:file:description Orchestration project. locals { group_iam_orc = { diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf index 09d2db1f..d4ac7252 100644 --- a/examples/data-solutions/dp-foundation/03-orc-vpc.tf +++ b/examples/data-solutions/dp-foundation/03-orc-vpc.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Orchestration VPC +# tfdoc:file:description Orchestration VPC. module "orc-vpc" { count = var.network_config.network != null ? 0 : 1 diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf index 96a0ae31..ea451cbc 100644 --- a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf +++ b/examples/data-solutions/dp-foundation/04-trf-datastorage.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Landing storage resources (Bigquery, Cloud Storage) +# tfdoc:file:description Landing storage resources (Bigquery, Cloud Storage). # Cloud Storage diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-trf-main.tf index 8bca4f5b..ace04109 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-trf-main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Trasformation project +# tfdoc:file:description Trasformation project. locals { group_iam_trf = { diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf index 6250ab45..a6a786e1 100644 --- a/examples/data-solutions/dp-foundation/04-trf-vpc.tf +++ b/examples/data-solutions/dp-foundation/04-trf-vpc.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Trasformation VPC +# tfdoc:file:description Trasformation VPC. module "trf-vpc" { count = var.network_config.network != null ? 0 : 1 diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf index 8d9308dd..ec15e817 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Datalake storage resources (Bigquery, Cloud Storage) +# tfdoc:file:description Datalake storage resources (Bigquery, Cloud Storage). # Bigquery diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-dtl-main.tf index b0a6e5ab..30c8ac1c 100644 --- a/examples/data-solutions/dp-foundation/05-dtl-main.tf +++ b/examples/data-solutions/dp-foundation/05-dtl-main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Datalake projects +# tfdoc:file:description Datalake projects. locals { group_iam_dtl = { diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-sec-main.tf index dfd11d48..2095a503 100644 --- a/examples/data-solutions/dp-foundation/06-sec-main.tf +++ b/examples/data-solutions/dp-foundation/06-sec-main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Security project +# tfdoc:file:description Security project. locals { group_iam_sec = { diff --git a/examples/data-solutions/dp-foundation/main.tf b/examples/data-solutions/dp-foundation/main.tf index 02373c7c..5f61b214 100644 --- a/examples/data-solutions/dp-foundation/main.tf +++ b/examples/data-solutions/dp-foundation/main.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Core locals +# tfdoc:file:description Core locals. locals { groups = { for k, v in var.groups : k => "${v}@${var.organization.domain}" } diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/output.tf index 5738c3b4..72cc7483 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/output.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Output variables +# tfdoc:file:description Output variables. output "bigquery-datasets" { description = "BigQuery datasets." diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 59d4906c..4cdf53a3 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Terraform Variables +# tfdoc:file:description Terraform Variables. variable "composer_config" { type = object({ From b81841fc99d2da416d3230514e232c38393fe4c3 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 09:16:51 +0100 Subject: [PATCH 066/132] fix demo README --- examples/data-solutions/dp-foundation/demo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data-solutions/dp-foundation/demo/README.md b/examples/data-solutions/dp-foundation/demo/README.md index 299ff8e0..78297f7a 100644 --- a/examples/data-solutions/dp-foundation/demo/README.md +++ b/examples/data-solutions/dp-foundation/demo/README.md @@ -1,3 +1,3 @@ # Data ingestion Demo -In this folder you can find an example to ingest data on the `data platfoem` instanciated in [here](../). See details in the [README.m](../#demo-pipeline) to run the demo. \ No newline at end of file +In this folder you can find an example to ingest data on the `data platfoem` instantiated in [here](../). See details in the [README.m](../#demo-pipeline) to run the demo. \ No newline at end of file From 8e7436f4fc2790a0e7eed478346a14359844f493 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 09:19:32 +0100 Subject: [PATCH 067/132] fix sample files --- examples/data-solutions/dp-foundation/terraform.tfvars.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data-solutions/dp-foundation/terraform.tfvars.sample b/examples/data-solutions/dp-foundation/terraform.tfvars.sample index 3efcb0df..2d43e583 100644 --- a/examples/data-solutions/dp-foundation/terraform.tfvars.sample +++ b/examples/data-solutions/dp-foundation/terraform.tfvars.sample @@ -4,5 +4,5 @@ project_create = { billing_account_id = "111111-222222-333333" } organization = { - domain = "caggioland.com" + domain = "example.com" } From 6e9b884dced8f1deee176ba676ee933051b36eeb Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 09:30:29 +0100 Subject: [PATCH 068/132] rename tf files --- .../{01-lnd-main.tf.tf => 01-landing.tf} | 0 ...-datastorage.tf => 01-storage-services.tf} | 0 .../{02-lod-main.tf => 02-load.tf} | 34 ++++++++++++- .../dp-foundation/02-lod-vpc.tf | 47 ----------------- ...-datastorage.tf => 02-storage-services.tf} | 0 .../{03-orc-composer.tf => 03-composer.tf} | 0 .../dp-foundation/03-orc-vpc.tf | 51 ------------------- .../{03-orc-main.tf => 03-orchestration.tf} | 38 +++++++++++++- ...-datastorage.tf => 03-storage-services.tf} | 0 ...-datastorage.tf => 04-storage-services.tf} | 0 .../{04-trf-main.tf => 04-transformation.tf} | 34 ++++++++++++- .../dp-foundation/04-trf-vpc.tf | 47 ----------------- .../{05-dtl-main.tf => 05-datalake.tf} | 0 ...-datastorage.tf => 05-storage-services.tf} | 0 .../{06-sec-main.tf => 06-security.tf} | 0 15 files changed, 103 insertions(+), 148 deletions(-) rename examples/data-solutions/dp-foundation/{01-lnd-main.tf.tf => 01-landing.tf} (100%) rename examples/data-solutions/dp-foundation/{01-lnd-datastorage.tf => 01-storage-services.tf} (100%) rename examples/data-solutions/dp-foundation/{02-lod-main.tf => 02-load.tf} (71%) delete mode 100644 examples/data-solutions/dp-foundation/02-lod-vpc.tf rename examples/data-solutions/dp-foundation/{02-lod-datastorage.tf => 02-storage-services.tf} (100%) rename examples/data-solutions/dp-foundation/{03-orc-composer.tf => 03-composer.tf} (100%) delete mode 100644 examples/data-solutions/dp-foundation/03-orc-vpc.tf rename examples/data-solutions/dp-foundation/{03-orc-main.tf => 03-orchestration.tf} (73%) rename examples/data-solutions/dp-foundation/{03-orc-datastorage.tf => 03-storage-services.tf} (100%) rename examples/data-solutions/dp-foundation/{04-trf-datastorage.tf => 04-storage-services.tf} (100%) rename examples/data-solutions/dp-foundation/{04-trf-main.tf => 04-transformation.tf} (68%) delete mode 100644 examples/data-solutions/dp-foundation/04-trf-vpc.tf rename examples/data-solutions/dp-foundation/{05-dtl-main.tf => 05-datalake.tf} (100%) rename examples/data-solutions/dp-foundation/{05-dtl-datastorage.tf => 05-storage-services.tf} (100%) rename examples/data-solutions/dp-foundation/{06-sec-main.tf => 06-security.tf} (100%) diff --git a/examples/data-solutions/dp-foundation/01-lnd-main.tf.tf b/examples/data-solutions/dp-foundation/01-landing.tf similarity index 100% rename from examples/data-solutions/dp-foundation/01-lnd-main.tf.tf rename to examples/data-solutions/dp-foundation/01-landing.tf diff --git a/examples/data-solutions/dp-foundation/01-lnd-datastorage.tf b/examples/data-solutions/dp-foundation/01-storage-services.tf similarity index 100% rename from examples/data-solutions/dp-foundation/01-lnd-datastorage.tf rename to examples/data-solutions/dp-foundation/01-storage-services.tf diff --git a/examples/data-solutions/dp-foundation/02-lod-main.tf b/examples/data-solutions/dp-foundation/02-load.tf similarity index 71% rename from examples/data-solutions/dp-foundation/02-lod-main.tf rename to examples/data-solutions/dp-foundation/02-load.tf index 33d827e4..1510aa65 100644 --- a/examples/data-solutions/dp-foundation/02-lod-main.tf +++ b/examples/data-solutions/dp-foundation/02-load.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Load project. +# tfdoc:file:description Load project and VPC. locals { group_iam_lod = { @@ -80,3 +80,35 @@ module "lod-prj" { storage = [try(local.service_encryption_keys.storage, null)] } } + +module "lod-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.lod-prj.project_id + name = "${local.prefix_lod}-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.load + name = "${local.prefix_lod}-subnet" + region = var.location_config.region + secondary_ip_range = {} + } + ] +} + +module "lod-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.lod-prj.project_id + network = module.lod-vpc[0].name + admin_ranges = values(module.lod-vpc[0].subnet_ips) +} + +module "lod-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.lod-prj.project_id + region = var.location_config.region + name = "${local.prefix_lod}-default" + router_network = module.lod-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/02-lod-vpc.tf b/examples/data-solutions/dp-foundation/02-lod-vpc.tf deleted file mode 100644 index 1bc60cf2..00000000 --- a/examples/data-solutions/dp-foundation/02-lod-vpc.tf +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# tfdoc:file:description Load VPC. - -module "lod-vpc" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc" - project_id = module.lod-prj.project_id - name = "${local.prefix_lod}-vpc" - subnets = [ - { - ip_cidr_range = var.network_config.vpc_subnet_range.load - name = "${local.prefix_lod}-subnet" - region = var.location_config.region - secondary_ip_range = {} - } - ] -} - -module "lod-vpc-firewall" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc-firewall" - project_id = module.lod-prj.project_id - network = module.lod-vpc[0].name - admin_ranges = values(module.lod-vpc[0].subnet_ips) -} - -module "lod-nat" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-cloudnat" - project_id = module.lod-prj.project_id - region = var.location_config.region - name = "${local.prefix_lod}-default" - router_network = module.lod-vpc[0].name -} diff --git a/examples/data-solutions/dp-foundation/02-lod-datastorage.tf b/examples/data-solutions/dp-foundation/02-storage-services.tf similarity index 100% rename from examples/data-solutions/dp-foundation/02-lod-datastorage.tf rename to examples/data-solutions/dp-foundation/02-storage-services.tf diff --git a/examples/data-solutions/dp-foundation/03-orc-composer.tf b/examples/data-solutions/dp-foundation/03-composer.tf similarity index 100% rename from examples/data-solutions/dp-foundation/03-orc-composer.tf rename to examples/data-solutions/dp-foundation/03-composer.tf diff --git a/examples/data-solutions/dp-foundation/03-orc-vpc.tf b/examples/data-solutions/dp-foundation/03-orc-vpc.tf deleted file mode 100644 index d4ac7252..00000000 --- a/examples/data-solutions/dp-foundation/03-orc-vpc.tf +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# tfdoc:file:description Orchestration VPC. - -module "orc-vpc" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc" - project_id = module.orc-prj.project_id - name = "${local.prefix_orc}-vpc" - subnets = [ - { - ip_cidr_range = var.network_config.vpc_subnet_range.orchestration - name = "${local.prefix_orc}-subnet" - region = var.location_config.region - secondary_ip_range = {} - secondary_ip_range = { - pods = var.composer_config.secondary_ip_range.pods - services = var.composer_config.secondary_ip_range.services - } - } - ] -} - -module "orc-vpc-firewall" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc-firewall" - project_id = module.orc-prj.project_id - network = module.orc-vpc[0].name - admin_ranges = values(module.orc-vpc[0].subnet_ips) -} - -module "orc-nat" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-cloudnat" - project_id = module.orc-prj.project_id - region = var.location_config.region - name = "${local.prefix_orc}-default" - router_network = module.orc-vpc[0].name -} diff --git a/examples/data-solutions/dp-foundation/03-orc-main.tf b/examples/data-solutions/dp-foundation/03-orchestration.tf similarity index 73% rename from examples/data-solutions/dp-foundation/03-orc-main.tf rename to examples/data-solutions/dp-foundation/03-orchestration.tf index afd407b4..45aa4d61 100644 --- a/examples/data-solutions/dp-foundation/03-orc-main.tf +++ b/examples/data-solutions/dp-foundation/03-orchestration.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Orchestration project. +# tfdoc:file:description Orchestration project and VPC. locals { group_iam_orc = { @@ -102,3 +102,39 @@ module "orc-prj" { storage = [try(local.service_encryption_keys.storage, null)] } } + +module "orc-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.orc-prj.project_id + name = "${local.prefix_orc}-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.orchestration + name = "${local.prefix_orc}-subnet" + region = var.location_config.region + secondary_ip_range = {} + secondary_ip_range = { + pods = var.composer_config.secondary_ip_range.pods + services = var.composer_config.secondary_ip_range.services + } + } + ] +} + +module "orc-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.orc-prj.project_id + network = module.orc-vpc[0].name + admin_ranges = values(module.orc-vpc[0].subnet_ips) +} + +module "orc-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.orc-prj.project_id + region = var.location_config.region + name = "${local.prefix_orc}-default" + router_network = module.orc-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/03-orc-datastorage.tf b/examples/data-solutions/dp-foundation/03-storage-services.tf similarity index 100% rename from examples/data-solutions/dp-foundation/03-orc-datastorage.tf rename to examples/data-solutions/dp-foundation/03-storage-services.tf diff --git a/examples/data-solutions/dp-foundation/04-trf-datastorage.tf b/examples/data-solutions/dp-foundation/04-storage-services.tf similarity index 100% rename from examples/data-solutions/dp-foundation/04-trf-datastorage.tf rename to examples/data-solutions/dp-foundation/04-storage-services.tf diff --git a/examples/data-solutions/dp-foundation/04-trf-main.tf b/examples/data-solutions/dp-foundation/04-transformation.tf similarity index 68% rename from examples/data-solutions/dp-foundation/04-trf-main.tf rename to examples/data-solutions/dp-foundation/04-transformation.tf index ace04109..d2d059f8 100644 --- a/examples/data-solutions/dp-foundation/04-trf-main.tf +++ b/examples/data-solutions/dp-foundation/04-transformation.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Trasformation project. +# tfdoc:file:description Trasformation project and VPC. locals { group_iam_trf = { @@ -74,3 +74,35 @@ module "trf-prj" { storage = [try(local.service_encryption_keys.storage, null)] } } + +module "trf-vpc" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc" + project_id = module.trf-prj.project_id + name = "${local.prefix_trf}-vpc" + subnets = [ + { + ip_cidr_range = var.network_config.vpc_subnet_range.transformation + name = "${local.prefix_trf}-subnet" + region = var.location_config.region + secondary_ip_range = {} + } + ] +} + +module "trf-vpc-firewall" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-vpc-firewall" + project_id = module.trf-prj.project_id + network = module.trf-vpc[0].name + admin_ranges = values(module.orc-vpc[0].subnet_ips) +} + +module "trf-nat" { + count = var.network_config.network != null ? 0 : 1 + source = "../../../modules/net-cloudnat" + project_id = module.trf-prj.project_id + region = var.location_config.region + name = "${local.prefix_trf}-default" + router_network = module.trf-vpc[0].name +} diff --git a/examples/data-solutions/dp-foundation/04-trf-vpc.tf b/examples/data-solutions/dp-foundation/04-trf-vpc.tf deleted file mode 100644 index a6a786e1..00000000 --- a/examples/data-solutions/dp-foundation/04-trf-vpc.tf +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# tfdoc:file:description Trasformation VPC. - -module "trf-vpc" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc" - project_id = module.trf-prj.project_id - name = "${local.prefix_trf}-vpc" - subnets = [ - { - ip_cidr_range = var.network_config.vpc_subnet_range.transformation - name = "${local.prefix_trf}-subnet" - region = var.location_config.region - secondary_ip_range = {} - } - ] -} - -module "trf-vpc-firewall" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-vpc-firewall" - project_id = module.trf-prj.project_id - network = module.trf-vpc[0].name - admin_ranges = values(module.orc-vpc[0].subnet_ips) -} - -module "trf-nat" { - count = var.network_config.network != null ? 0 : 1 - source = "../../../modules/net-cloudnat" - project_id = module.trf-prj.project_id - region = var.location_config.region - name = "${local.prefix_trf}-default" - router_network = module.trf-vpc[0].name -} diff --git a/examples/data-solutions/dp-foundation/05-dtl-main.tf b/examples/data-solutions/dp-foundation/05-datalake.tf similarity index 100% rename from examples/data-solutions/dp-foundation/05-dtl-main.tf rename to examples/data-solutions/dp-foundation/05-datalake.tf diff --git a/examples/data-solutions/dp-foundation/05-dtl-datastorage.tf b/examples/data-solutions/dp-foundation/05-storage-services.tf similarity index 100% rename from examples/data-solutions/dp-foundation/05-dtl-datastorage.tf rename to examples/data-solutions/dp-foundation/05-storage-services.tf diff --git a/examples/data-solutions/dp-foundation/06-sec-main.tf b/examples/data-solutions/dp-foundation/06-security.tf similarity index 100% rename from examples/data-solutions/dp-foundation/06-sec-main.tf rename to examples/data-solutions/dp-foundation/06-security.tf From 679b20533e4091e996305ad091d967ab8e15d4f3 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 10:09:25 +0100 Subject: [PATCH 069/132] Fix outputs file name, fix README, remove dependeces on composer resource --- .../dp-foundation/03-composer.tf | 26 ----------------- .../data-solutions/dp-foundation/README.md | 29 +++++++++++++++++++ .../dp-foundation/{output.tf => outputs.tf} | 2 +- 3 files changed, 30 insertions(+), 27 deletions(-) rename examples/data-solutions/dp-foundation/{output.tf => outputs.tf} (98%) diff --git a/examples/data-solutions/dp-foundation/03-composer.tf b/examples/data-solutions/dp-foundation/03-composer.tf index c877a769..bdf0a133 100644 --- a/examples/data-solutions/dp-foundation/03-composer.tf +++ b/examples/data-solutions/dp-foundation/03-composer.tf @@ -102,30 +102,4 @@ resource "google_composer_environment" "orc-cmp-0" { # } # } } - depends_on = [ - module.dtl-0-bq-0, - module.dtl-1-bq-0, - module.dtl-2-bq-0, - module.dtl-exp-bq-0, - module.dtl-0-cs-0, - module.dtl-1-cs-0, - module.dtl-2-cs-0, - module.dtl-exp-cs-0, - module.lnd-cs-0, - module.lnd-bq-0, - module.lnd-ps-0, - module.lod-cs-df-0, - module.trf-cs-df-0, - module.orc-vpc, - module.orc-vpc, - module.dtl-0-prj, - module.dtl-1-prj, - module.dtl-2-prj, - module.lnd-prj, - module.lod-prj, - module.orc-prj, - module.trf-prj, - module.lod-sa-df-0, - module.lod-sa-df-0, - ] } diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index c982ae59..25de37e5 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -176,6 +176,35 @@ Description of commands: - 03: copy the Cloud Composer DAG to Cloud Composer storage bucket impersonating the `orchestration` service account. - 04: Open the Cloud Composer Airflow UI and run the imported DAG. - 05: Run the Bigquery query to see results. + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [organization](variables.tf#L76) | Organization details. | object({…}) | ✓ | | +| [prefix](variables.tf#L83) | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | ✓ | | +| [composer_config](variables.tf#L17) | | object({…}) | | {…} | +| [data_force_destroy](variables.tf#L40) | Flag to set 'force_destroy' on data services like biguqery or cloud storage. | bool | | false | +| [groups](variables.tf#L46) | Groups. | map(string) | | {…} | +| [location_config](variables.tf#L128) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | +| [network_config](variables.tf#L56) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | +| [project_create](variables.tf#L88) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | object({…}) | | null | +| [project_id](variables.tf#L97) | Project id, references existing project if `project_create` is null. | object({…}) | | {…} | +| [project_services](variables.tf#L117) | List of core services enabled on all projects. | list(string) | | […] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [VPC](outputs.tf#L61) | VPC networks. | | +| [bigquery-datasets](outputs.tf#L17) | BigQuery datasets. | | +| [demo_commands](outputs.tf#L70) | Demo commands | | +| [gcs-buckets](outputs.tf#L28) | GCS buckets. | | +| [kms_keys](outputs.tf#L42) | Cloud MKS keys. | | +| [projects](outputs.tf#L47) | GCP Projects. | | + + # TODOs Features to add in futere releases: * Add support for Column level access on Bigquery diff --git a/examples/data-solutions/dp-foundation/output.tf b/examples/data-solutions/dp-foundation/outputs.tf similarity index 98% rename from examples/data-solutions/dp-foundation/output.tf rename to examples/data-solutions/dp-foundation/outputs.tf index 72cc7483..5d45114c 100644 --- a/examples/data-solutions/dp-foundation/output.tf +++ b/examples/data-solutions/dp-foundation/outputs.tf @@ -68,7 +68,7 @@ output "VPC" { } output "demo_commands" { - description = "Demo commands" + description = "Demo commands." value = { 01 = "gsutil -i ${module.lnd-sa-cs-0.email} cp demo/data/*.csv gs://${module.lnd-cs-0.name}" 02 = "gsutil -i ${module.orc-sa-cmp-0.email} cp demo/data/*.j* gs://${module.orc-cs-0.name}" From a193dcc0b44d5ec4b023b513ffd39d40c401edb2 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 10:41:27 +0100 Subject: [PATCH 070/132] Add test. --- .../data_solutions/dp-foundation/__init__.py | 13 ++++++ .../dp-foundation/fixture/main.tf | 25 ++++++++++++ .../dp-foundation/fixture/variables.tf | 40 +++++++++++++++++++ .../data_solutions/dp-foundation/test_plan.py | 27 +++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 tests/examples/data_solutions/dp-foundation/__init__.py create mode 100644 tests/examples/data_solutions/dp-foundation/fixture/main.tf create mode 100644 tests/examples/data_solutions/dp-foundation/fixture/variables.tf create mode 100644 tests/examples/data_solutions/dp-foundation/test_plan.py diff --git a/tests/examples/data_solutions/dp-foundation/__init__.py b/tests/examples/data_solutions/dp-foundation/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/examples/data_solutions/dp-foundation/__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/data_solutions/dp-foundation/fixture/main.tf b/tests/examples/data_solutions/dp-foundation/fixture/main.tf new file mode 100644 index 00000000..a5f7378d --- /dev/null +++ b/tests/examples/data_solutions/dp-foundation/fixture/main.tf @@ -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. + */ + +module "test" { + source = "../../../../../examples/data-solutions/dp-foundation/" + organization = { domain : "example.com" } + project_create = { + billing_account_id = "123456-123456-123456" + parent = "folders/12345678" + } + prefix = "prefix" +} diff --git a/tests/examples/data_solutions/dp-foundation/fixture/variables.tf b/tests/examples/data_solutions/dp-foundation/fixture/variables.tf new file mode 100644 index 00000000..7dfa52e5 --- /dev/null +++ b/tests/examples/data_solutions/dp-foundation/fixture/variables.tf @@ -0,0 +1,40 @@ +/** + * 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 "organization" { + type = object({ + domain = string + }) + default = { + domain = "example.com" + } +} + +variable "prefix" { + type = string + default = "prefix" +} + +variable "project_create" { + type = object({ + billing_account_id = string + parent = string + }) + default = { + billing_account_id = "123456-123456-123456" + parent = "folders/12345678" + } +} diff --git a/tests/examples/data_solutions/dp-foundation/test_plan.py b/tests/examples/data_solutions/dp-foundation/test_plan.py new file mode 100644 index 00000000..11d4fd88 --- /dev/null +++ b/tests/examples/data_solutions/dp-foundation/test_plan.py @@ -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. + + +import os +import pytest + + +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) == 39 + assert len(resources) == 281 From 1e63c0f44aa4227f8170f739c52ca0154ed66af1 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 11:01:33 +0100 Subject: [PATCH 071/132] Fix README. --- examples/data-solutions/dp-foundation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 25de37e5..18f1c306 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -199,7 +199,7 @@ Description of commands: |---|---|:---:| | [VPC](outputs.tf#L61) | VPC networks. | | | [bigquery-datasets](outputs.tf#L17) | BigQuery datasets. | | -| [demo_commands](outputs.tf#L70) | Demo commands | | +| [demo_commands](outputs.tf#L70) | Demo commands. | | | [gcs-buckets](outputs.tf#L28) | GCS buckets. | | | [kms_keys](outputs.tf#L42) | Cloud MKS keys. | | | [projects](outputs.tf#L47) | GCP Projects. | | From bf0b240e1a4152e4142ba0254b4efe11f1b3cd27 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Tue, 1 Feb 2022 18:02:06 +0100 Subject: [PATCH 072/132] Initial README update --- .../data-solutions/dp-foundation/README.md | 117 ++++++++++-------- 1 file changed, 62 insertions(+), 55 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 18f1c306..9a33d87d 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -1,66 +1,72 @@ # Data Platform -This module implement an opinionated Data Platform (DP) Architecture that create and set up projects (and related resources) to be used for to create your data platform. +This module implements an opinionated Data Platform (DP) Architecture that creates and setup projects (and related resources) to be used to create your DP. The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. -The following diagram is a high level reference of the resources created and managed here: +The following diagram is an high level reference of the resources created and managed here: -![Data Platform Architecture overview](./images/overview_diagram.png "Data Platform Architecture overview") +![Data Platform architecture overview](./images/overview_diagram.png "Data Platform architecture overview") -# Design overview and choices +## Design overview and choices Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. The approach adapts to different high level requirements: - boundaries for each step -- help identify actors -- help assign minimal roles +- clear and defined actors +- least privilege principle +- rely on service account impersonification -Additionally, a few critical benefits are directly provided by this design: -- adding a new set +The code in this example doesn't address Organization level configuration (Organization policy, VPC-SC, centralized logs). Those are aspects that we expect to be addressed on stages external to this script. -## Project structure -The DP is designed to rely on several projects, one prj per data stage. This is done to better separate different stages of the data journey and rely on project level roles. +### Project structure +The DP is designed to rely on several projects, one project per data stage. The stages identified are: +- landing +- load +- data lake +- orchestration +- transformation +- exposure + +This is done to better separate different stages of the data journey and rely on project level roles. The following projects will be created: -* **Landing** This Project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. +- **Landing** This project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. +- **Load** This project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. +- **Data Lake** Those projects are intended to store your data. It represents where data will be persisted within 3 Layers. These layers represent different stages where data is processed and progressively refined + - **L0 - Raw data** Structured Data, stored in adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in BigQuery). + - **L1 - Cleansed, aggregated and standardized data** + - **L2 - Curated layer** + - **Experimental** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers +- **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. +- **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest to rely on BigQuery engine to perform transformation. If BigQuery doesn't have the feature needed to perform your transformation you suggest using Cloud Dataflow. +- **Exposure** This project is intended to host resources to expose your data. To expose BigQuery data, we strongly suggest relying on Authorized views. Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. -* **Load** This Project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transofmation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. +### Roles +We assigned roles on resources at project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. -* **Data Lake** Those projects is intended to store your data. It reppresents where data will be persisted within 3 Layers. These layers reppresent different stages where data is processed and progressivly refined - * **L0 - Raw data** Structured Data, stored in adeguate format: structured data stored in bigquery, unstructured data stored on Cloud Storage with additional metadata stored in Bigquery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in Bigquery). - * **L1 - Cleansed, aggregated and standardized data** - * **L2 - Curated layer** - * **Experimental** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers -* **Orchestration** This project is inteded to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. -* **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest to rely on BigQuery engine to perform transformation. If Bigquery do not have the feature needed to perform your transformation you suggest to use Clud Dataflow. -* **Exposure** This project is intended to host resources to expose your data. To expose Bigquery data, we strongly suggest to rely on Authorized views. Other resources may better fit on particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed on use case where you need low latency to access data. +### Service accounts +Service Account creation follow the following principles: +- Each service account perform a single task having access to the minimum number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) +- Each Service Account has the least privilege on each project. -## Roles -We assigned roles on resources at Project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. - -## Service accounts -Service Account creation follow the following principals: -- Each service account perform a single task aving access to the minimun number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) -- Each Service Account has least privilage on each project. - -### Service Account Keys -Service Account Keys (SAK) are out of scope for this example. The example implemented rely on Service Account Impersonification avoiding the creation of SAK. +#### Service Account Keys +Service Account Keys (SAK) are out of scope for this example. The example implemented relies on Service Account Impersonification avoiding the creation of SAK. The use of SAK within a data pipeline incurs several security risks, as these are physical credentials, matched to an automated system, that can be distributed without oversight or control. -Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identify a structured process to mitigate risks associated with the use of service account keys. +Whilst necessary in some scenarios, such as programmatic access from on-premise or alternative clouds, we recommend identifying a structured process to mitigate risks associated with the use of service account keys. -## Groups +### Groups As default groups, we identified the following actors: -- *Data Engineers*: the group that handle and run the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issue with the pipeline. The team has also the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. -- *Data Analyst*: the group that perform analysis on the dataset. The group has Read access to the Data Lake L2 project and Bigquery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` -- *Data Security*: the project that handle security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` -## VPC design +- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issues with the pipeline. The team also has the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. +- *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` +- *Data Security*: the project that handles security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` +### VPC design The DP except as input an existing Shared-VPC to run resources. You can configure subsets for DP resource specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. If `network_config` variable is not configured, the script will create a VPC on each project that require a VPC: *laod*, *trasformation* and *orchestration* projects with the default configuration. -## IP ranges, subnetting +### IP ranges, subnetting To run your DP resources you need the following ranges: - Load project VPC for Cloud Dataflow workers. Range: '/24'. - Transformation VPC for Cloud Dataflow workers. Range: '/24'. @@ -70,14 +76,14 @@ To run your DP resources you need the following ranges: - Web Server: Range: '/28' - Secondary ip ranges. Pods range: '/22', Services range: '/24' -## Resource naming convention +### Resource naming convention Reousces in the script use the following acronims: - `lnd` for `landing` - `lod` for `load` - `orc` for `orchestration` - `trf` for `trasformation` - `dtl` for `Data Lake` - - 2 letters acronym for GCP products, example: `bq` for `Bigquery`, `df` for `Cloud Dataflow`, ... + - 2 letters acronym for GCP products, example: `bq` for `BigQuery`, `df` for `Cloud Dataflow`, ... Resources follow the naming convention described below. @@ -96,7 +102,7 @@ Service Accounts: PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER ``` -## Encryption +### Encryption We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. ![Centralized Cloud KMS high level diagram](./images/kms_diagram.png "Centralized Cloud KMS high level diagram") @@ -114,7 +120,7 @@ service_encryption_keys = { We consider this step optional, it depend on customer policy and security best practices. -# Data Anonymization +## Data Anonymization We suggest the use of Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. The implementation of the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to let you implement the DLP strategy. We expect you will use DLP templates in one of the following way: - During the ingestion phase, from Dataflow - During the transformation phase, from BigQuery or Dataflow @@ -123,8 +129,8 @@ We implemented a centralized model for Data Loss Prevention material. Templates ![Centralized Cloud DLP high level diagram](./images/dlp_diagram.png "Centralized Cloud DLP high level diagram") -# How to run this script -The Data Platform is meant to be executed by a Service Account (or a regular user) having this minial set of permission: +## How to run this script +The DP is meant to be executed by a Service Account (or a regular user) having this minial set of permission: * **Org level**: * `"compute.organizations.enableXpnResource"` * `"compute.organizations.disableXpnResource"` @@ -141,7 +147,7 @@ The Data Platform is meant to be executed by a Service Account (or a regular use * `"roles/compute.viewer"` * `"roles/dns.admin"` -# Variable configuration +## Variable configuration There are three sets of variables you will need to fill in: ``` @@ -157,15 +163,15 @@ organization = { For a more fined grained configuration, check variables on [`variables.tf`](./variables.tf) and update accordingly to the desired configuration. -# Customizations -## Create Cloud KMS as part of the Data Platform -To create Cloud KMS keys within the Data Platform you can uncomment the Cloud KMS resources configuraed in the [`06-sec-main.tf`](./06-sec-main.tf) file and update KMS keys pointers on `local.service_encryption_keys.*` to the local resource created. +## Customizations +### Create Cloud KMS as part of the DP +To create Cloud KMS keys within the DP you can uncomment the Cloud KMS resources configuraed in the [`06-sec-main.tf`](./06-sec-main.tf) file and update KMS keys pointers on `local.service_encryption_keys.*` to the local resource created. -## Assign roles at BQ Dataset level -To handle multiple groups of `data-analyst` accessing the same Data Lake layer Projects but only to the dataset belonging to a specific group, you may want to assign roles at Bigquery dataset level instead of at project level. -To do this, you need to remove IAM binging at Project level for the `data-analyst` group and assign roles at Bigquery dataset level using the `iam` variable on `bigquery-dataset` modules. +### Assign roles at BQ Dataset level +To handle multiple groups of `data-analyst` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project level. +To do this, you need to remove IAM binging at project level for the `data-analyst` group and assign roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. -# Demo pipeline +## Demo pipeline The application layer is out of scope of this script, but as a demo, it is provided a Cloud Composer DAG to mode data from the `landing` area to `DataLake L2` dataset. Just follow the commands you find in the `demo_commands` Terraform output, go in the Cloud Composer UI and run the `data_pipeline_dag`. @@ -175,7 +181,8 @@ Description of commands: - 02: copy sample data structure definition in the `orchestration` storage bucket impersonating the `orchestration` service account. - 03: copy the Cloud Composer DAG to Cloud Composer storage bucket impersonating the `orchestration` service account. - 04: Open the Cloud Composer Airflow UI and run the imported DAG. -- 05: Run the Bigquery query to see results. +- 05: Run the BigQuery query to see results. + ## Variables @@ -205,14 +212,14 @@ Description of commands: | [projects](outputs.tf#L47) | GCP Projects. | | -# TODOs +## TODOs Features to add in futere releases: - * Add support for Column level access on Bigquery + * Add support for Column level access on BigQuery * Add example templates for DataCatalog * Add example on how to use Cloud DLP * Add solution to handle Tables, Views and Authorized Views lifecycle * Add solution to handle Metadata lifecycle -# To Test/Fix +## To Test/Fix * Composer require "Require OS Login" not enforced * External Shared-VPC \ No newline at end of file From 4d04933a57550e3a4a9dcfd58e6dd524e59610cc Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 09:48:03 +0100 Subject: [PATCH 073/132] README review --- .../data-solutions/dp-foundation/README.md | 81 +++++++++---------- .../data-solutions/dp-foundation/variables.tf | 4 +- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 9a33d87d..5df5a280 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -4,14 +4,14 @@ This module implements an opinionated Data Platform (DP) Architecture that creat The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. -The following diagram is an high level reference of the resources created and managed here: +The following diagram is a high-level reference of the resources created and managed here: ![Data Platform architecture overview](./images/overview_diagram.png "Data Platform architecture overview") ## Design overview and choices Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. -The approach adapts to different high level requirements: +The approach adapts to different high-level requirements: - boundaries for each step - clear and defined actors - least privilege principle @@ -28,25 +28,25 @@ The DP is designed to rely on several projects, one project per data stage. The - transformation - exposure -This is done to better separate different stages of the data journey and rely on project level roles. +This is done to better separate different stages of the data journey and rely on project-level roles. The following projects will be created: - **Landing** This project is intended to store data temporarily. Data are pushed to Cloud Storage, BigQuery or Cloud PubSub. Resource configured with 3 months lifecycle policy. -- **Load** This project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization/DLP PII data can be applied at this stage or in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. +- **Load** This project is intended to load data from `landing` to `data lake`. Load is made with minimal to zero transformation logic (mainly `cast`). Anonymization/tokenization Personally Identifiable Information can be applied at this stage or in the transformation stage depending on your requirements. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. - **Data Lake** Those projects are intended to store your data. It represents where data will be persisted within 3 Layers. These layers represent different stages where data is processed and progressively refined - - **L0 - Raw data** Structured Data, stored in adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in BigQuery). + - **L0 - Raw data** Structured Data, stored in the adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in BigQuery). - **L1 - Cleansed, aggregated and standardized data** - **L2 - Curated layer** - **Experimental** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers - **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. -- **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest to rely on BigQuery engine to perform transformation. If BigQuery doesn't have the feature needed to perform your transformation you suggest using Cloud Dataflow. +- **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you suggest using Cloud Dataflow. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. Anonymization/tokenization Personally Identifiable Information can be applied at this stage or in the transformation stage depending on your requirements. - **Exposure** This project is intended to host resources to expose your data. To expose BigQuery data, we strongly suggest relying on Authorized views. Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. ### Roles -We assigned roles on resources at project level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. +We assigned roles on resources at project-level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. ### Service accounts -Service Account creation follow the following principles: +Service Account creation follows the following principles: - Each service account perform a single task having access to the minimum number of resources (example: the Cloud Dataflow Service Account has access to the Landing project and to the Data Lake L0 project) - Each Service Account has the least privilege on each project. @@ -59,13 +59,13 @@ Whilst necessary in some scenarios, such as programmatic access from on-premise ### Groups As default groups, we identified the following actors: -- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issues with the pipeline. The team also has the ability to impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. +- *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issues with the pipeline. The team also can impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. - *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` - *Data Security*: the project that handles security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` -### VPC design -The DP except as input an existing Shared-VPC to run resources. You can configure subsets for DP resource specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. +### Virtual Private Cloud (VPC) design +The DP except as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) to run resources. You can configure subsets for DP resources specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. -If `network_config` variable is not configured, the script will create a VPC on each project that require a VPC: *laod*, *trasformation* and *orchestration* projects with the default configuration. +If `network_config` variable is not configured, the script will create a VPC on each project that requires a VPC: *load*, *transformation*, and *orchestration* projects with the default configuration. ### IP ranges, subnetting To run your DP resources you need the following ranges: - Load project VPC for Cloud Dataflow workers. Range: '/24'. @@ -74,14 +74,14 @@ To run your DP resources you need the following ranges: - Cloud SQL. Range: '/24' - GKE Master. Range: '/28' - Web Server: Range: '/28' - - Secondary ip ranges. Pods range: '/22', Services range: '/24' + - Secondary IP ranges. Pods range: '/22', Services range: '/24' ### Resource naming convention -Reousces in the script use the following acronims: +Resources in the script use the following acronyms: - `lnd` for `landing` - `lod` for `load` - `orc` for `orchestration` - - `trf` for `trasformation` + - `trf` for `transformation` - `dtl` for `Data Lake` - 2 letters acronym for GCP products, example: `bq` for `BigQuery`, `df` for `Cloud Dataflow`, ... @@ -103,11 +103,11 @@ PREFIX-LAYER[2]-GCP_PRODUCT[2]-COUNTER ``` ### Encryption -We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belongs to a project external to the DP. +We suggest a centralized approach to Keys management, to let the Security team be the only team that can access encryption material. Keyrings and Keys belong to a project external to the DP. -![Centralized Cloud KMS high level diagram](./images/kms_diagram.png "Centralized Cloud KMS high level diagram") +![Centralized Cloud Key Management high-level diagram](./images/kms_diagram.png "Centralized Cloud Key Management high-level diagram") -To configure the use of Cloud KMS on resources you have to specify key URL on the 'service_encryption_keys'. Key location should match the resource location. Example: +To configure the use of Cloud Key Management on resources you have to specify the key URL on the 'service_encryption_keys'. Keys location should match the resource location. Example: ``` service_encryption_keys = { @@ -118,19 +118,19 @@ service_encryption_keys = { pubsub = "KEY_URL_MULTIREGIONAL" ``` -We consider this step optional, it depend on customer policy and security best practices. +We consider this step optional, it depends on customer policy and security best practices. ## Data Anonymization -We suggest the use of Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. The implementation of the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to let you implement the DLP strategy. We expect you will use DLP templates in one of the following way: +We suggest the use of Cloud Data Loss Prevention to identify/mask/tokenize your confidential data. The implementation of the Data Loss Prevention strategy is out of scope for this example. We enable the service in 2 different projects to let you implement the data loss prevention strategy. We expect you will use [Cloud Data Loss Prevention templates](https://cloud.google.com/dlp/docs/concepts-templates) in one of the following ways: - During the ingestion phase, from Dataflow -- During the transformation phase, from BigQuery or Dataflow +- During the transformation phase, from [BigQuery](https://cloud.google.com/bigquery/docs/scan-with-dlp) or [Cloud Dataflow](https://cloud.google.com/architecture/running-automated-dataflow-pipeline-de-identify-pii-dataset) -We implemented a centralized model for Data Loss Prevention material. Templates will be stored in the security project: +We implemented a centralized model for Cloud Data Loss Prevention resources. Templates will be stored in the security project: -![Centralized Cloud DLP high level diagram](./images/dlp_diagram.png "Centralized Cloud DLP high level diagram") +![Centralized Cloud Data Loss Prevention high-level diagram](./images/dlp_diagram.png "Centralized Cloud Data Loss Prevention high-level diagram") ## How to run this script -The DP is meant to be executed by a Service Account (or a regular user) having this minial set of permission: +The DP is meant to be executed by a Service Account (or a regular user) having this minimal set of permission: * **Org level**: * `"compute.organizations.enableXpnResource"` * `"compute.organizations.disableXpnResource"` @@ -140,7 +140,7 @@ The DP is meant to be executed by a Service Account (or a regular user) having t * `"roles/owner"` * `"roles/resourcemanager.folderAdmin"` * `"roles/resourcemanager.projectCreator"` -* **Cloud KMS Keys** (if Cloud KMS keys are configured): +* **Cloud Key Management Keys** (if Cloud Key Management keys are configured): * `"roles/cloudkms.admin"` or Permissions: `cloudkms.cryptoKeys.getIamPolicy`, `cloudkms.cryptoKeys.list`, `cloudkms.cryptoKeys.setIamPolicy` * **on the host project** for the Shared VPC/s * `"roles/browser"` @@ -161,28 +161,27 @@ organization = { } ``` -For a more fined grained configuration, check variables on [`variables.tf`](./variables.tf) and update accordingly to the desired configuration. +For a more fine grained configuration, check variables on [`variables.tf`](./variables.tf) and update accordingly to the desired configuration. ## Customizations -### Create Cloud KMS as part of the DP -To create Cloud KMS keys within the DP you can uncomment the Cloud KMS resources configuraed in the [`06-sec-main.tf`](./06-sec-main.tf) file and update KMS keys pointers on `local.service_encryption_keys.*` to the local resource created. +### Create Cloud Key Management keys as part of the DP +To create Cloud Key Management keys within the DP you can uncomment the Cloud Key Management resources configured in the [`06-sec-main.tf`](./06-sec-main.tf) file and update Cloud Key Management keys pointers on `local.service_encryption_keys.*` to the local resource created. ### Assign roles at BQ Dataset level -To handle multiple groups of `data-analyst` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project level. -To do this, you need to remove IAM binging at project level for the `data-analyst` group and assign roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. +To handle multiple groups of `data-analysts` accessing the same Data Lake layer projects but only to the dataset belonging to a specific group, you may want to assign roles at BigQuery dataset level instead of at project-level. +To do this, you need to remove IAM binging at project-level for the `data-analysts` group and assign roles at BigQuery dataset level using the `iam` variable on `bigquery-dataset` modules. ## Demo pipeline -The application layer is out of scope of this script, but as a demo, it is provided a Cloud Composer DAG to mode data from the `landing` area to `DataLake L2` dataset. +The application layer is out of scope of this script, but as a demo, it is provided with a Cloud Composer DAG to mode data from the `landing` area to the `DataLake L2` dataset. Just follow the commands you find in the `demo_commands` Terraform output, go in the Cloud Composer UI and run the `data_pipeline_dag`. Description of commands: -- 01: copy sample data to `landing` storage bucket impersonating the `load` service account. -- 02: copy sample data structure definition in the `orchestration` storage bucket impersonating the `orchestration` service account. -- 03: copy the Cloud Composer DAG to Cloud Composer storage bucket impersonating the `orchestration` service account. +- 01: copy sample data to a `landing` Cloud Storage bucket impersonating the `load` service account. +- 02: copy sample data structure definition in the `orchestration` Cloud Storage bucket impersonating the `orchestration` service account. +- 03: copy the Cloud Composer DAG to the Cloud Composer Storage bucket impersonating the `orchestration` service account. - 04: Open the Cloud Composer Airflow UI and run the imported DAG. - 05: Run the BigQuery query to see results. - ## Variables @@ -190,9 +189,9 @@ Description of commands: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [organization](variables.tf#L76) | Organization details. | object({…}) | ✓ | | -| [prefix](variables.tf#L83) | Unique prefix used for resource names. Not used for project if 'project_create' is null. | string | ✓ | | +| [prefix](variables.tf#L83) | Unique prefix used for resource names. Not used for projects if 'project_create' is null. | string | ✓ | | | [composer_config](variables.tf#L17) | | object({…}) | | {…} | -| [data_force_destroy](variables.tf#L40) | Flag to set 'force_destroy' on data services like biguqery or cloud storage. | bool | | false | +| [data_force_destroy](variables.tf#L40) | Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage. | bool | | false | | [groups](variables.tf#L46) | Groups. | map(string) | | {…} | | [location_config](variables.tf#L128) | Locations where resources will be deployed. Map to configure region and multiregion specs. | object({…}) | | {…} | | [network_config](variables.tf#L56) | Shared VPC to use. If not null networks will be created in projects. | object({…}) | | {…} | @@ -213,11 +212,11 @@ Description of commands: ## TODOs -Features to add in futere releases: +Features to add in future releases: * Add support for Column level access on BigQuery - * Add example templates for DataCatalog - * Add example on how to use Cloud DLP - * Add solution to handle Tables, Views and Authorized Views lifecycle + * Add example templates for Data Catalog + * Add example on how to use Cloud Data Loss Prevention + * Add solution to handle Tables, Views, and Authorized Views lifecycle * Add solution to handle Metadata lifecycle ## To Test/Fix diff --git a/examples/data-solutions/dp-foundation/variables.tf b/examples/data-solutions/dp-foundation/variables.tf index 4cdf53a3..065d7766 100644 --- a/examples/data-solutions/dp-foundation/variables.tf +++ b/examples/data-solutions/dp-foundation/variables.tf @@ -38,7 +38,7 @@ variable "composer_config" { } variable "data_force_destroy" { - description = "Flag to set 'force_destroy' on data services like biguqery or cloud storage." + description = "Flag to set 'force_destroy' on data services like BiguQery or Cloud Storage." type = bool default = false } @@ -81,7 +81,7 @@ variable "organization" { } variable "prefix" { - description = "Unique prefix used for resource names. Not used for project if 'project_create' is null." + description = "Unique prefix used for resource names. Not used for projects if 'project_create' is null." type = string } From 3c99074b3ff652827d277217e4f84b48a713b224 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Wed, 2 Feb 2022 15:31:54 +0100 Subject: [PATCH 074/132] Fix issues & readme --- .../dp-foundation/03-composer.tf | 8 ++- .../dp-foundation/05-datalake.tf | 10 +-- .../dp-foundation/05-storage-services.tf | 12 ++-- .../{06-security.tf => 06-common.tf} | 20 +++--- .../dp-foundation/07-exposure.tf | 42 +++++++++++++ .../data-solutions/dp-foundation/README.md | 17 ++++-- .../demo/{gcs2bq.py => datapipeline.py} | 57 +++++++++++------- .../dp-foundation/images/overview_diagram.png | Bin 64758 -> 80324 bytes .../data-solutions/dp-foundation/outputs.tf | 36 +++++++---- .../data-solutions/dp-foundation/variables.tf | 32 ++++++---- 10 files changed, 160 insertions(+), 74 deletions(-) rename examples/data-solutions/dp-foundation/{06-security.tf => 06-common.tf} (85%) create mode 100644 examples/data-solutions/dp-foundation/07-exposure.tf rename examples/data-solutions/dp-foundation/demo/{gcs2bq.py => datapipeline.py} (86%) diff --git a/examples/data-solutions/dp-foundation/03-composer.tf b/examples/data-solutions/dp-foundation/03-composer.tf index bdf0a133..80c5e1ce 100644 --- a/examples/data-solutions/dp-foundation/03-composer.tf +++ b/examples/data-solutions/dp-foundation/03-composer.tf @@ -51,9 +51,6 @@ resource "google_composer_environment" "orc-cmp-0" { software_config { image_version = "composer-1.17.5-airflow-2.1.4" env_variables = { - DTL_EXP_PRJ = module.dtl-0-prj.project_id - DTL_EXP_BQ_DATASET = module.dtl-exp-bq-0.dataset_id - DTL_EXP_GCS = module.dtl-exp-cs-0.url DTL_L0_PRJ = module.dtl-0-prj.project_id DTL_L0_BQ_DATASET = module.dtl-0-bq-0.dataset_id DTL_L0_GCS = module.dtl-0-cs-0.url @@ -63,6 +60,9 @@ resource "google_composer_environment" "orc-cmp-0" { DTL_L2_PRJ = module.dtl-2-prj.project_id DTL_L2_BQ_DATASET = module.dtl-2-bq-0.dataset_id DTL_L2_GCS = module.dtl-2-cs-0.url + DTL_PLG_PRJ = module.dtl-plg-prj.project_id + DTL_PLG_BQ_DATASET = module.dtl-plg-bq-0.dataset_id + DTL_PLG_GCS = module.dtl-plg-cs-0.url GCP_REGION = var.composer_config.region LND_PRJ = module.lnd-prj.project_id LND_BQ = module.lnd-bq-0.dataset_id @@ -77,6 +77,8 @@ resource "google_composer_environment" "orc-cmp-0" { ORC_GCS = module.orc-cs-0.url TRF_PRJ = module.trf-prj.project_id TRF_GCS_STAGING = module.trf-cs-df-0.url + TRF_NET_VPC = module.trf-vpc[0].self_link + TRF_NET_SUBNET = module.trf-vpc[0].subnet_self_links["${var.composer_config.region}/${local.prefix_trf}-subnet"] TRF_SA_DF = module.trf-sa-df-0.email TRF_SA_BQ = module.trf-sa-bq-0.email } diff --git a/examples/data-solutions/dp-foundation/05-datalake.tf b/examples/data-solutions/dp-foundation/05-datalake.tf index 30c8ac1c..14ebd5a4 100644 --- a/examples/data-solutions/dp-foundation/05-datalake.tf +++ b/examples/data-solutions/dp-foundation/05-datalake.tf @@ -63,7 +63,7 @@ locals { module "dtl-0-prj" { source = "../../../modules/project" - name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-0" + name = var.project_id["datalake-l0"] parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -92,7 +92,7 @@ module "dtl-0-prj" { module "dtl-1-prj" { source = "../../../modules/project" - name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-1" + name = var.project_id["datalake-l1"] parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -121,7 +121,7 @@ module "dtl-1-prj" { module "dtl-2-prj" { source = "../../../modules/project" - name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-2" + name = var.project_id["datalake-l2"] parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null @@ -148,9 +148,9 @@ module "dtl-2-prj" { } } -module "dtl-exp-prj" { +module "dtl-plg-prj" { source = "../../../modules/project" - name = var.project_create == null ? var.project_id["datalake"] : "${var.project_id["datalake"]}-exp" + name = var.project_id["datalake-playground"] parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null diff --git a/examples/data-solutions/dp-foundation/05-storage-services.tf b/examples/data-solutions/dp-foundation/05-storage-services.tf index ec15e817..1a3e879e 100644 --- a/examples/data-solutions/dp-foundation/05-storage-services.tf +++ b/examples/data-solutions/dp-foundation/05-storage-services.tf @@ -40,10 +40,10 @@ module "dtl-2-bq-0" { encryption_key = try(local.service_encryption_keys.bq, null) } -module "dtl-exp-bq-0" { +module "dtl-plg-bq-0" { source = "../../../modules/bigquery-dataset" - project_id = module.dtl-exp-prj.project_id - id = "${replace(local.prefix_dtl, "-", "_")}_exp_bq_0" + project_id = module.dtl-plg-prj.project_id + id = "${replace(local.prefix_dtl, "-", "_")}_plg_bq_0" location = var.location_config.region encryption_key = try(local.service_encryption_keys.bq, null) } @@ -83,10 +83,10 @@ module "dtl-2-cs-0" { force_destroy = var.data_force_destroy } -module "dtl-exp-cs-0" { +module "dtl-plg-cs-0" { source = "../../../modules/gcs" - project_id = module.dtl-exp-prj.project_id - name = "exp-cs-0" + project_id = module.dtl-plg-prj.project_id + name = "plg-cs-0" prefix = local.prefix_dtl location = var.location_config.region storage_class = "REGIONAL" diff --git a/examples/data-solutions/dp-foundation/06-security.tf b/examples/data-solutions/dp-foundation/06-common.tf similarity index 85% rename from examples/data-solutions/dp-foundation/06-security.tf rename to examples/data-solutions/dp-foundation/06-common.tf index 2095a503..9cfdc7a5 100644 --- a/examples/data-solutions/dp-foundation/06-security.tf +++ b/examples/data-solutions/dp-foundation/06-common.tf @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# tfdoc:file:description Security project. +# tfdoc:file:description common project. locals { - group_iam_sec = { + group_iam_cmn = { "${local.groups.data-engineers}" = [ "roles/dlp.reader", "roles/dlp.user", @@ -25,35 +25,35 @@ locals { "roles/dlp.admin", ], } - iam_sec = { + iam_cmn = { "roles/dlp.user" = [ module.lod-sa-df-0.iam_email, module.trf-sa-df-0.iam_email ] } - prefix_sec = "${var.prefix}-sec" + prefix_cmn = "${var.prefix}-cmn" } # Project -module "sec-prj" { +module "cmn-prj" { source = "../../../modules/project" - name = var.project_id["security"] + name = var.project_id["common"] parent = try(var.project_create.parent, null) billing_account = try(var.project_create.billing_account_id, null) project_create = var.project_create != null prefix = var.project_create == null ? null : var.prefix # additive IAM bindings avoid disrupting bindings in existing project - iam = var.project_create != null ? local.iam_trf : {} - iam_additive = var.project_create == null ? local.iam_trf : {} - group_iam = local.group_iam_trf + iam = var.project_create != null ? local.iam_cmn : {} + iam_additive = var.project_create == null ? local.iam_cmn : {} + group_iam = local.group_iam_cmn services = concat(var.project_services, [ "dlp.googleapis.com", ]) } # Uncomment this section and assigne key links accondingly in local. variable -# if you want to create KMS keys in the security projet +# if you want to create KMS keys in the common projet # module "sec-kms-0" { # source = "../../../modules/kms" diff --git a/examples/data-solutions/dp-foundation/07-exposure.tf b/examples/data-solutions/dp-foundation/07-exposure.tf new file mode 100644 index 00000000..44c7f946 --- /dev/null +++ b/examples/data-solutions/dp-foundation/07-exposure.tf @@ -0,0 +1,42 @@ +# 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. + +# tfdoc:file:description common project. + +locals { + group_iam_exp = { + #TODO add group => role mapping to asign on exposure project + } + iam_exp = { + #TODO add role => service account mapping to assign roles on exposure project + } + prefix_exp = "${var.prefix}-exp" +} + +# Project + +module "exp-prj" { + source = "../../../modules/project" + name = var.project_id["exposure"] + parent = try(var.project_create.parent, null) + billing_account = try(var.project_create.billing_account_id, null) + project_create = var.project_create != null + prefix = var.project_create == null ? null : var.prefix + # additive IAM bindings avoid disrupting bindings in existing project + iam = var.project_create != null ? local.iam_exp : {} + iam_additive = var.project_create == null ? local.iam_exp : {} + group_iam = local.group_iam_exp + services = concat(var.project_services, [ + ]) +} diff --git a/examples/data-solutions/dp-foundation/README.md b/examples/data-solutions/dp-foundation/README.md index 5df5a280..12a88631 100644 --- a/examples/data-solutions/dp-foundation/README.md +++ b/examples/data-solutions/dp-foundation/README.md @@ -2,12 +2,14 @@ This module implements an opinionated Data Platform (DP) Architecture that creates and setup projects (and related resources) to be used to create your DP. -The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Security, etc.), and then allow easy customizations to complete the implementation of the intended hierarchy design. +The code is intentionally simple, as it's intended to provide a generic initial setup (Networking, Cloud Storage Buckets, BigQuery datasets, etc.), and then allow easy customizations to complete the implementation of the intended design. The following diagram is a high-level reference of the resources created and managed here: ![Data Platform architecture overview](./images/overview_diagram.png "Data Platform architecture overview") +A demo pipeline is also part of this example: it can be built and run on top of the foundational infrastructure to quickly verify or test the setup. + ## Design overview and choices Despite its simplicity, this stage implements the basics of a design that we've seen working well for a variety of customers. @@ -37,10 +39,10 @@ The following projects will be created: - **L0 - Raw data** Structured Data, stored in the adequate format: structured data stored in BigQuery, unstructured data stored on Cloud Storage with additional metadata stored in BigQuery (for example pictures stored in Cloud Storage and analysis of the picture for Cloud Vision API stored in BigQuery). - **L1 - Cleansed, aggregated and standardized data** - **L2 - Curated layer** - - **Experimental** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers + - **Playground** Store temporary tables that Data Analyst may use to perform R&D on data available on other Data Lake layers - **Orchestration** This project is intended to host Cloud Composer. Cloud Composer will orchestrate all tasks to move your data on its journey. - **Transformation** This project is intended to host resources to move data from one layer of the Data Lake to the other. We strongly suggest relying on BigQuery engine to perform transformations. If BigQuery doesn't have the feature needed to perform your transformation you suggest using Cloud Dataflow. The use of [Cloud Dataflow templates](https://cloud.google.com/dataflow/docs/concepts/dataflow-templates) is suggested. Anonymization/tokenization Personally Identifiable Information can be applied at this stage or in the transformation stage depending on your requirements. -- **Exposure** This project is intended to host resources to expose your data. To expose BigQuery data, we strongly suggest relying on Authorized views. Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. +- **Exposure** This project is intended to host resources to expose your data. To expose BigQuery data, we strongly suggest relying on Authorized views. Other resources may better fit a particular data access pattern, example: Cloud SQL may be needed if you need to expose data with low latency, BigTable may be needed in a use case where you need lower latency to access data. For the porpuse of this example, no resources will be deployed on this project, please customize the exple as needed. ### Roles We assigned roles on resources at project-level assigning the appropriate role to groups. We recommend not adding human users directly to the resource-access groups with IAM permissions to access data. @@ -60,7 +62,7 @@ Whilst necessary in some scenarios, such as programmatic access from on-premise ### Groups As default groups, we identified the following actors: - *Data Engineers*: the group that handles and runs the Data Hub. The group has Read access to all resources to be able to troubleshoot possible issues with the pipeline. The team also can impersonate all service accounts. Default value: `gcp-data-engineers@DOMAIN.COM`. -- *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `experimental` project. Default value: `gcp-data-analyst@DOMAIN.COM` +- *Data Analyst*: the group that performs analysis on the dataset. The group has Read access to the Data Lake L2 project and BigQuery READ/WRITE access to the `playground` project. Default value: `gcp-data-analyst@DOMAIN.COM` - *Data Security*: the project that handles security features related to the Data Hub. Default name: `gcp-data-security@DOMAIN.com` ### Virtual Private Cloud (VPC) design The DP except as input an existing [Shared-VPC](https://cloud.google.com/vpc/docs/shared-vpc) to run resources. You can configure subsets for DP resources specifying the link to the subnet in the `network_config` variable. You may want to configure a shared-VPC to run your resources in the case your pipelines may need to reach on-premise resources. @@ -74,7 +76,7 @@ To run your DP resources you need the following ranges: - Cloud SQL. Range: '/24' - GKE Master. Range: '/28' - Web Server: Range: '/28' - - Secondary IP ranges. Pods range: '/22', Services range: '/24' + - Secondary IP ranges. Pods range: '/22', Services range: '/24' ### Resource naming convention Resources in the script use the following acronyms: @@ -130,6 +132,11 @@ We implemented a centralized model for Cloud Data Loss Prevention resources. Tem ![Centralized Cloud Data Loss Prevention high-level diagram](./images/dlp_diagram.png "Centralized Cloud Data Loss Prevention high-level diagram") ## How to run this script +In order to bring up this example, you will need + +- a folder or organization where new projects will be created +- a billing account that will be associated to new projects + The DP is meant to be executed by a Service Account (or a regular user) having this minimal set of permission: * **Org level**: * `"compute.organizations.enableXpnResource"` diff --git a/examples/data-solutions/dp-foundation/demo/gcs2bq.py b/examples/data-solutions/dp-foundation/demo/datapipeline.py similarity index 86% rename from examples/data-solutions/dp-foundation/demo/gcs2bq.py rename to examples/data-solutions/dp-foundation/demo/datapipeline.py index d5851fe9..6581348b 100644 --- a/examples/data-solutions/dp-foundation/demo/gcs2bq.py +++ b/examples/data-solutions/dp-foundation/demo/datapipeline.py @@ -29,26 +29,39 @@ from airflow.providers.google.cloud.operators.bigquery import BigQueryInsertJob # -------------------------------------------------------------------------------- # Set variables -# -------------------------------------------------------------------------------- - -LND_GCS = os.environ.get("LND_GCS") -ORC_GCS = os.environ.get("ORC_GCS") -LOD_GCS_STAGING = os.environ.get("LOD_GCS_STAGING") -DTL_L0_BQ_DATASET = os.environ.get("DTL_L0_BQ_DATASET") +# ------------------------------------------------------------ DTL_L0_PRJ = os.environ.get("DTL_L0_PRJ") -DTL_L1_BQ_DATASET = os.environ.get("DTL_L1_BQ_DATASET") +DTL_L0_BQ_DATASET = os.environ.get("DTL_L0_BQ_DATASET") +DTL_L0_GCS = os.environ.get("DTL_L0_GCS") DTL_L1_PRJ = os.environ.get("DTL_L1_PRJ") -DTL_L2_BQ_DATASET = os.environ.get("DTL_L2_BQ_DATASET") +DTL_L1_BQ_DATASET = os.environ.get("DTL_L1_BQ_DATASET") +DTL_L1_GCS = os.environ.get("DTL_L1_GCS") DTL_L2_PRJ = os.environ.get("DTL_L2_PRJ") +DTL_L2_BQ_DATASET = os.environ.get("DTL_L2_BQ_DATASET") +DTL_L2_GCS = os.environ.get("DTL_L2_GCS") +DTL_PLG_PRJ = os.environ.get("DTL_PLG_PRJ") +DTL_PLG_BQ_DATASET = os.environ.get("DTL_PLG_BQ_DATASET") +DTL_PLG_GCS = os.environ.get("DTL_PLG_GCS") +GCP_REGION = os.environ.get("GCP_REGION") +LND_PRJ = os.environ.get("LND_PRJ") +LND_BQ = os.environ.get("LND_BQ") +LND_GCS = os.environ.get("LND_GCS") +LND_PS = os.environ.get("LND_PS") LOD_PRJ = os.environ.get("LOD_PRJ") -DF_ZONE = os.environ.get("GCP_REGION") + "-b" -DF_REGION = BQ_REGION = os.environ.get("GCP_REGION") +LOD_GCS_STAGING = os.environ.get("LOD_GCS_STAGING") LOD_NET_VPC = os.environ.get("LOD_NET_VPC") LOD_NET_SUBNET = os.environ.get("LOD_NET_SUBNET") LOD_SA_DF = os.environ.get("LOD_SA_DF") +ORC_PRJ = os.environ.get("ORC_PRJ") +ORC_GCS = os.environ.get("ORC_GCS") +TRF_PRJ = os.environ.get("TRF_PRJ") +TRF_GCS_STAGING = os.environ.get("TRF_GCS_STAGING") +TRF_NET_VPC = os.environ.get("TRF_NET_VPC") +TRF_NET_SUBNET = os.environ.get("TRF_NET_SUBNET") TRF_SA_DF = os.environ.get("TRF_SA_DF") TRF_SA_BQ = os.environ.get("TRF_SA_BQ") -TRF_PRJ = os.environ.get("TRF_PRJ") +DF_ZONE = os.environ.get("GCP_REGION") + "-b" +DF_REGION = BQ_REGION = os.environ.get("GCP_REGION") # -------------------------------------------------------------------------------- # Set default arguments @@ -77,7 +90,7 @@ default_args = { 'serviceAccountEmail': LOD_SA_DF, 'subnetwork': LOD_NET_SUBNET, 'ipConfiguration': "WORKER_IP_PRIVATE" - }, + }, } # -------------------------------------------------------------------------------- @@ -131,9 +144,8 @@ with models.DAG( location=BQ_REGION, configuration={ 'jobType':'QUERY', - 'writeDisposition':'WRITE_TRUNCATE', 'query':{ - 'query':"""SELECT + 'query':"""SELECT c.id as customer_id, p.id as purchase_id, c.name as name, @@ -142,17 +154,18 @@ with models.DAG( p.price as price, p.timestamp as timestamp FROM `{dtl_0_prj}.{dtl_0_dataset}.customers` c - JOIN `{dtl_0_prj}.{dtl_0_dataset}.purchases` p ON c.id = p.customer_id + JOIN `{dtl_0_prj}.{dtl_0_dataset}.purchases` p ON c.id = p.customer_id """.format(dtl_0_prj=DTL_L0_PRJ, dtl_0_dataset=DTL_L0_BQ_DATASET, ), 'destinationTable':{ 'projectId': DTL_L1_PRJ, 'datasetId': DTL_L1_BQ_DATASET, - 'tableId': 'customer_purchase' + 'tableId': 'customer_purchase' }, + 'writeDisposition':'WRITE_TRUNCATE', "useLegacySql": False } }, - impersonation_chain=[TRF_SA_BQ] + impersonation_chain=[TRF_SA_BQ] ) l2_customer_purchase = BigQueryInsertJobOperator( @@ -162,9 +175,8 @@ with models.DAG( location=BQ_REGION, configuration={ 'jobType':'QUERY', - 'writeDisposition':'WRITE_TRUNCATE', 'query':{ - 'query':"""SELECT + 'query':"""SELECT customer_id, purchase_id, name, @@ -177,12 +189,13 @@ with models.DAG( 'destinationTable':{ 'projectId': DTL_L2_PRJ, 'datasetId': DTL_L2_BQ_DATASET, - 'tableId': 'customer_purchase' + 'tableId': 'customer_purchase' }, + 'writeDisposition':'WRITE_TRUNCATE', "useLegacySql": False } }, - impersonation_chain=[TRF_SA_BQ] + impersonation_chain=[TRF_SA_BQ] ) - + start >> [customers_import, purchases_import] >> join_customer_purchase >> l2_customer_purchase >> end diff --git a/examples/data-solutions/dp-foundation/images/overview_diagram.png b/examples/data-solutions/dp-foundation/images/overview_diagram.png index 093343f50c5d30e1da48b4be7754646590ec4a4b..4fe42c015a233e72a802af07b4ce53c556b32c76 100644 GIT binary patch literal 80324 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 Xaa`Qf#Xg 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