From 83b2c128dbd529724591e229616421c4f81ac4c5 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sat, 10 Aug 2019 12:28:49 +0200 Subject: [PATCH 01/24] add gitignore file --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..e2654ddeb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +**/.terraform +**/terraform.tfstate* +**/terraform.tfvars +.idea +.vscode +credentials.json +key.json From 315e30775c07d968cc03970f3524cecde7540643 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sat, 10 Aug 2019 12:29:14 +0200 Subject: [PATCH 02/24] data and infra skeletons --- data/README.md | 0 infrastructure/README.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/README.md create mode 100644 infrastructure/README.md diff --git a/data/README.md b/data/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/infrastructure/README.md b/infrastructure/README.md new file mode 100644 index 0000000000..e69de29bb2 From 4d4c61844c04961558d73d37f7445a2bb633c02b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sat, 10 Aug 2019 12:29:41 +0200 Subject: [PATCH 03/24] org skeleton --- organization/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 organization/README.md diff --git a/organization/README.md b/organization/README.md new file mode 100644 index 0000000000..e69de29bb2 From 5f013bfa403b8284e8b865ff98bd257eddf9151a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sat, 10 Aug 2019 12:30:07 +0200 Subject: [PATCH 04/24] org environments sample skeleton --- organization/environments/README.md | 0 organization/environments/main.tf | 31 +++++++++++++++++ organization/environments/variables.tf | 48 ++++++++++++++++++++++++++ organization/environments/versions.tf | 4 +++ 4 files changed, 83 insertions(+) create mode 100644 organization/environments/README.md create mode 100644 organization/environments/main.tf create mode 100644 organization/environments/variables.tf create mode 100644 organization/environments/versions.tf diff --git a/organization/environments/README.md b/organization/environments/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/organization/environments/main.tf b/organization/environments/main.tf new file mode 100644 index 0000000000..c8c57fdfbc --- /dev/null +++ b/organization/environments/main.tf @@ -0,0 +1,31 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################################################################### +# Terraform org-level project # +############################################################################### + +module "project-terraform" { + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.0.0" + parent_type = var.root_type + parent_id = var.root_id + billing_account = var.billing_account_id + prefix = var.prefix + name = "terraform" + lien_reason = "terraform" + owners = var.terraform_owners + activate_apis = var.project_services +} + diff --git a/organization/environments/variables.tf b/organization/environments/variables.tf new file mode 100644 index 0000000000..c99d240836 --- /dev/null +++ b/organization/environments/variables.tf @@ -0,0 +1,48 @@ +variable "root_type" { + description = "Type of the root for the new hierarchy." + default = "folder" +} + +variable "root_id" { + description = "Id of the organization or folder used as the root for the new hierarchy." + type = string +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "billing_account_id" { + description = "Billing account id used as default for new projects." + type = string +} + +variable "terraform_owners" { + description = "Terraform project owners, in IAM format." + default = [] +} + +variable "project_services" { + description = "Service APIs enabled by default in new projects." + default = [ + "bigquery-json.googleapis.com", + "cloudbilling.googleapis.com", + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "container.googleapis.com", + "containerregistry.googleapis.com", + "deploymentmanager.googleapis.com", + "iam.googleapis.com", + "iamcredentials.googleapis.com", + "logging.googleapis.com", + "oslogin.googleapis.com", + "pubsub.googleapis.com", + "replicapool.googleapis.com", + "replicapoolupdater.googleapis.com", + "resourceviews.googleapis.com", + "serviceusage.googleapis.com", + "storage-api.googleapis.com", + ] +} + diff --git a/organization/environments/versions.tf b/organization/environments/versions.tf new file mode 100644 index 0000000000..ac97c6ac8e --- /dev/null +++ b/organization/environments/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} From 820fe124c98084ff0e6be2baa96e8eb05c1e1416 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sun, 11 Aug 2019 12:37:14 +0200 Subject: [PATCH 05/24] Organization teams sample skeleton. --- organization/teams/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 organization/teams/README.md diff --git a/organization/teams/README.md b/organization/teams/README.md new file mode 100644 index 0000000000..e69de29bb2 From 0f523cd101051a684e1411ab3921b78b55eb3648 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sun, 11 Aug 2019 12:37:45 +0200 Subject: [PATCH 06/24] Organization env sample GCS for tf state. --- organization/environments/main.tf | 16 +++++++++++++--- organization/environments/variables.tf | 5 +++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index c8c57fdfbc..4d4355fac3 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -13,12 +13,13 @@ # limitations under the License. ############################################################################### -# Terraform org-level project # +# Terraform top-level resources # ############################################################################### module "project-terraform" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.0.0" + # source = "terraform-google-modules/project-factory/google//modules/fabric-project" + # version = "3.0.0" + source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" parent_type = var.root_type parent_id = var.root_id billing_account = var.billing_account_id @@ -29,3 +30,12 @@ module "project-terraform" { activate_apis = var.project_services } +module "gcs-terraform" { + # source = "terraform-google-modules/cloud-storage/google" + # version = "0.1.0" + source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=3f27f26" + prefix = var.prefix + names = ["tf-bootstrap"] + project_id = module.project-terraform.project_id + location = var.gcs_location +} diff --git a/organization/environments/variables.tf b/organization/environments/variables.tf index c99d240836..b6f3ab4db8 100644 --- a/organization/environments/variables.tf +++ b/organization/environments/variables.tf @@ -23,6 +23,11 @@ variable "terraform_owners" { default = [] } +variable "gcs_location" { + description = "GCS bucket location." + default = "EU" +} + variable "project_services" { description = "Service APIs enabled by default in new projects." default = [ From b70f2adcfa769c476f26a4911671c4b4e971feb0 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 08:35:08 +0200 Subject: [PATCH 07/24] org env: service accounts and GCS roles --- organization/environments/main.tf | 43 +++++++++++++++++++++----- organization/environments/variables.tf | 31 +++++++++++-------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index 4d4355fac3..ac32ce5a4a 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -16,12 +16,12 @@ # Terraform top-level resources # ############################################################################### -module "project-terraform" { +module "project-tf" { # source = "terraform-google-modules/project-factory/google//modules/fabric-project" - # version = "3.0.0" + # version = "3.0.1" source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" parent_type = var.root_type - parent_id = var.root_id + parent_id = var.org_id billing_account = var.billing_account_id prefix = var.prefix name = "terraform" @@ -30,12 +30,41 @@ module "project-terraform" { activate_apis = var.project_services } -module "gcs-terraform" { +module "gcs-tf-bootstrap" { # source = "terraform-google-modules/cloud-storage/google" - # version = "0.1.0" + # version = "1.0.0" source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=3f27f26" - prefix = var.prefix + project_id = module.project-tf.project_id + prefix = "${var.prefix}-tf" names = ["tf-bootstrap"] - project_id = module.project-terraform.project_id location = var.gcs_location } + +module "service-accounts-tf-environments" { + # source = "terraform-google-modules/service-accounts/google" + # version = "1.0.1" + source = "github.com/terraform-google-modules/terraform-google-service-accounts?ref=540ad25" + project_id = module.project-tf.project_id + org_id = var.org_id + billing_account_id = var.billing_account_id + prefix = var.prefix + names = var.environments + grant_billing_role = true + grant_xpn_roles = true + generate_keys = true +} + +module "gcs-tf-environments" { + # source = "terraform-google-modules/cloud-storage/google" + # version = "1.0.0" + source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=3f27f26" + project_id = module.project-tf.project_id + prefix = "${var.prefix}-tf" + names = var.environments + location = var.gcs_location + set_admin_roles = true + bucket_admins = zipmap( + var.environments, + module.service-accounts-tf-environments.iam_emails + ) +} diff --git a/organization/environments/variables.tf b/organization/environments/variables.tf index b6f3ab4db8..5d1f37f1c3 100644 --- a/organization/environments/variables.tf +++ b/organization/environments/variables.tf @@ -1,11 +1,16 @@ -variable "root_type" { - description = "Type of the root for the new hierarchy." - default = "folder" +variable "billing_account_id" { + description = "Billing account id used as default for new projects." + type = string } -variable "root_id" { - description = "Id of the organization or folder used as the root for the new hierarchy." - type = string +variable "environments" { + description = "Environment short names." + type = list(string) +} + +variable "gcs_location" { + description = "GCS bucket location." + default = "EU" } variable "prefix" { @@ -13,8 +18,13 @@ variable "prefix" { type = string } -variable "billing_account_id" { - description = "Billing account id used as default for new projects." +variable "root_type" { + description = "Type of the root for the new hierarchy." + default = "organization" +} + +variable "org_id" { + description = "Organization id." type = string } @@ -23,11 +33,6 @@ variable "terraform_owners" { default = [] } -variable "gcs_location" { - description = "GCS bucket location." - default = "EU" -} - variable "project_services" { description = "Service APIs enabled by default in new projects." default = [ From 79bcf5651e9b4e150d4f62a1bb213397fc836d7d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 09:32:24 +0200 Subject: [PATCH 08/24] org env: folders --- organization/environments/main.tf | 52 ++++++++++++++++++++------ organization/environments/variables.tf | 1 + 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index ac32ce5a4a..422ac0b3f5 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -16,6 +16,8 @@ # Terraform top-level resources # ############################################################################### +# Terraform project + module "project-tf" { # source = "terraform-google-modules/project-factory/google//modules/fabric-project" # version = "3.0.1" @@ -30,20 +32,12 @@ module "project-tf" { activate_apis = var.project_services } -module "gcs-tf-bootstrap" { - # source = "terraform-google-modules/cloud-storage/google" - # version = "1.0.0" - source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=3f27f26" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" - names = ["tf-bootstrap"] - location = var.gcs_location -} +# per-environment service accounts module "service-accounts-tf-environments" { # source = "terraform-google-modules/service-accounts/google" # version = "1.0.1" - source = "github.com/terraform-google-modules/terraform-google-service-accounts?ref=540ad25" + source = "github.com/terraform-google-modules/terraform-google-service-accounts?ref=857f394" project_id = module.project-tf.project_id org_id = var.org_id billing_account_id = var.billing_account_id @@ -54,10 +48,24 @@ module "service-accounts-tf-environments" { generate_keys = true } +# bootstrap Terraform state GCS bucket + +module "gcs-tf-bootstrap" { + # source = "terraform-google-modules/cloud-storage/google" + # version = "1.0.0" + source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" + project_id = module.project-tf.project_id + prefix = "${var.prefix}-tf" + names = ["tf-bootstrap"] + location = var.gcs_location +} + +# per-environment Terraform state GCS buckets + module "gcs-tf-environments" { # source = "terraform-google-modules/cloud-storage/google" # version = "1.0.0" - source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=3f27f26" + source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" names = var.environments @@ -68,3 +76,25 @@ module "gcs-tf-environments" { module.service-accounts-tf-environments.iam_emails ) } + +############################################################################### +# Top-level folders # +############################################################################### + +# TODO(ludomagno): move XPN admin role here after checking it now works on folders + +module "folders-top-level" { + source = "terraform-google-modules/folders/google" + version = "1.0.0" + parent_type = var.root_type + parent_id = var.org_id + names = var.environments + set_roles = true + per_folder_admins = module.service-accounts-tf-environments.iam_emails + folder_admin_roles = [ + "roles/resourcemanager.folderViewer", + "roles/resourcemanager.projectCreator", + "roles/owner", + "roles/compute.networkAdmin", + ] +} diff --git a/organization/environments/variables.tf b/organization/environments/variables.tf index 5d1f37f1c3..80ea02d111 100644 --- a/organization/environments/variables.tf +++ b/organization/environments/variables.tf @@ -37,6 +37,7 @@ variable "project_services" { description = "Service APIs enabled by default in new projects." default = [ "bigquery-json.googleapis.com", + "bigquerystorage.googleapis.com", "cloudbilling.googleapis.com", "cloudresourcemanager.googleapis.com", "compute.googleapis.com", From 6ad69365dcd715b6528d102c751228ee34a8fda8 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 11:41:02 +0200 Subject: [PATCH 09/24] org env: audit export --- organization/environments/main.tf | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index 422ac0b3f5..944c40a56b 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -98,3 +98,49 @@ module "folders-top-level" { "roles/compute.networkAdmin", ] } + +############################################################################### +# Audit log exports # +############################################################################### + +# Audit logs project + +module "project-audit" { + # source = "terraform-google-modules/project-factory/google//modules/fabric-project" + # version = "3.0.1" + source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + parent_type = var.root_type + parent_id = var.org_id + billing_account = var.billing_account_id + prefix = var.prefix + name = "audit" + lien_reason = "audit" + activate_apis = var.project_services +} + +# audit logs destination on BigQuery + +module "bq-audit-export" { + source = "terraform-google-modules/log-export/google//modules/bigquery" + version = "3.0.0" + project_id = module.project-audit.project_id + dataset_name = "logs_audit_${replace(var.environments[0], "-", "_")}" + log_sink_writer_identity = module.log-sink-audit.writer_identity +} + +# This sink exports audit logs for the first environment only, change the +# parent resource to the organization to have organization-wide audit exports + +module "log-sink-audit" { + source = "terraform-google-modules/log-export/google" + version = "3.0.0" + filter = "logName: \"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName: \"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" + log_sink_name = "logs-audit-${var.environments[0]}" + parent_resource_type = "folder" + parent_resource_id = "${element(values(module.folders-top-level.names_and_ids), 0)}" + include_children = "true" + unique_writer_identity = "true" + destination_uri = "${module.bq-audit-export.destination_uri}" +} + + From 79f45d93afc37785a0bb0d2b2550ed71c657b05b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 11:52:02 +0200 Subject: [PATCH 10/24] org env: shared project --- organization/environments/main.tf | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index 944c40a56b..80a6aad178 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -143,4 +143,24 @@ module "log-sink-audit" { destination_uri = "${module.bq-audit-export.destination_uri}" } +############################################################################### +# Shared resources (GCR, GCS, KMS, etc.) # +############################################################################### + +# Shared resources project + +module "project-shared-resources" { + # source = "terraform-google-modules/project-factory/google//modules/fabric-project" + # version = "3.0.1" + source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + parent_type = var.root_type + parent_id = var.org_id + billing_account = var.billing_account_id + prefix = var.prefix + name = "shared" + lien_reason = "shared" + activate_apis = var.project_services +} +# Add here further modules for resources that don't belong to any environments, +# like GCS buckets to hold assets, KMS, etc. From 8c6b46fb7905312f878ed1077d3ffff47011ab5c Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 19:39:59 +0200 Subject: [PATCH 11/24] org env: switch to released 3.1.0 project module version --- organization/environments/main.tf | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/organization/environments/main.tf b/organization/environments/main.tf index 80a6aad178..0b7f2d71e9 100644 --- a/organization/environments/main.tf +++ b/organization/environments/main.tf @@ -19,9 +19,9 @@ # Terraform project module "project-tf" { - # source = "terraform-google-modules/project-factory/google//modules/fabric-project" - # version = "3.0.1" - source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.1.0" + #source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" parent_type = var.root_type parent_id = var.org_id billing_account = var.billing_account_id @@ -106,9 +106,8 @@ module "folders-top-level" { # Audit logs project module "project-audit" { - # source = "terraform-google-modules/project-factory/google//modules/fabric-project" - # version = "3.0.1" - source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.1.0" parent_type = var.root_type parent_id = var.org_id billing_account = var.billing_account_id @@ -150,9 +149,8 @@ module "log-sink-audit" { # Shared resources project module "project-shared-resources" { - # source = "terraform-google-modules/project-factory/google//modules/fabric-project" - # version = "3.0.1" - source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.1.0" parent_type = var.root_type parent_id = var.org_id billing_account = var.billing_account_id From 8fbeebf8f02fcea4d12a25c0af90f5f9a30c7484 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 19:41:36 +0200 Subject: [PATCH 12/24] rename organization top-level folder to organization-bootstrap --- {organization => organization-bootstrap}/README.md | 0 {organization => organization-bootstrap}/environments/README.md | 0 {organization => organization-bootstrap}/environments/main.tf | 0 .../environments/variables.tf | 0 {organization => organization-bootstrap}/environments/versions.tf | 0 {organization => organization-bootstrap}/teams/README.md | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {organization => organization-bootstrap}/README.md (100%) rename {organization => organization-bootstrap}/environments/README.md (100%) rename {organization => organization-bootstrap}/environments/main.tf (100%) rename {organization => organization-bootstrap}/environments/variables.tf (100%) rename {organization => organization-bootstrap}/environments/versions.tf (100%) rename {organization => organization-bootstrap}/teams/README.md (100%) diff --git a/organization/README.md b/organization-bootstrap/README.md similarity index 100% rename from organization/README.md rename to organization-bootstrap/README.md diff --git a/organization/environments/README.md b/organization-bootstrap/environments/README.md similarity index 100% rename from organization/environments/README.md rename to organization-bootstrap/environments/README.md diff --git a/organization/environments/main.tf b/organization-bootstrap/environments/main.tf similarity index 100% rename from organization/environments/main.tf rename to organization-bootstrap/environments/main.tf diff --git a/organization/environments/variables.tf b/organization-bootstrap/environments/variables.tf similarity index 100% rename from organization/environments/variables.tf rename to organization-bootstrap/environments/variables.tf diff --git a/organization/environments/versions.tf b/organization-bootstrap/environments/versions.tf similarity index 100% rename from organization/environments/versions.tf rename to organization-bootstrap/environments/versions.tf diff --git a/organization/teams/README.md b/organization-bootstrap/teams/README.md similarity index 100% rename from organization/teams/README.md rename to organization-bootstrap/teams/README.md From e381d4db6647b501750b1c05eb13774bb28d2b9f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Aug 2019 21:09:48 +0200 Subject: [PATCH 13/24] org env: use folders module with better outputs, module outputs --- organization-bootstrap/environments/main.tf | 13 +++-- .../environments/outputs.tf | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 organization-bootstrap/environments/outputs.tf diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index 0b7f2d71e9..c5a6b2ec8d 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -52,7 +52,7 @@ module "service-accounts-tf-environments" { module "gcs-tf-bootstrap" { # source = "terraform-google-modules/cloud-storage/google" - # version = "1.0.0" + # version = "2.0.0" source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" @@ -64,7 +64,7 @@ module "gcs-tf-bootstrap" { module "gcs-tf-environments" { # source = "terraform-google-modules/cloud-storage/google" - # version = "1.0.0" + # version = "2.0.0" source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" @@ -84,8 +84,9 @@ module "gcs-tf-environments" { # TODO(ludomagno): move XPN admin role here after checking it now works on folders module "folders-top-level" { - source = "terraform-google-modules/folders/google" - version = "1.0.0" + # source = "terraform-google-modules/folders/google" + # version = "2.0.0" + source = "github.com/terraform-google-modules/terraform-google-folders?ref=26db794564" parent_type = var.root_type parent_id = var.org_id names = var.environments @@ -136,7 +137,7 @@ module "log-sink-audit" { filter = "logName: \"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName: \"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" log_sink_name = "logs-audit-${var.environments[0]}" parent_resource_type = "folder" - parent_resource_id = "${element(values(module.folders-top-level.names_and_ids), 0)}" + parent_resource_id = module.folders-top-level.ids[0] include_children = "true" unique_writer_identity = "true" destination_uri = "${module.bq-audit-export.destination_uri}" @@ -160,5 +161,5 @@ module "project-shared-resources" { activate_apis = var.project_services } -# Add here further modules for resources that don't belong to any environments, +# Add further modules here for resources that don't belong to any environments, # like GCS buckets to hold assets, KMS, etc. diff --git a/organization-bootstrap/environments/outputs.tf b/organization-bootstrap/environments/outputs.tf new file mode 100644 index 0000000000..e9167033a7 --- /dev/null +++ b/organization-bootstrap/environments/outputs.tf @@ -0,0 +1,56 @@ +# Copyright 2019 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 "terraform_project" { + description = "Project that holds the base Terraform resources." + value = module.project-tf.project_id +} + +output "bootstrap_tf_gcs_bucket" { + description = "GCS bucket used for the bootstrap Terraform state." + value = module.gcs-tf-bootstrap.name +} + +output "environment_folders" { + description = "Top-level environment folders." + value = zipmap(var.environments, module.folders-top-level.ids) +} + +output "environment_tf_gcs_buckets" { + description = "GCS buckets used for each environment Terraform state." + value = zipmap(var.environments, module.gcs-tf-environments.names) +} + +output "environment_service_accounts" { + description = "Service accounts used to run each environment Terraform modules." + value = zipmap(var.environments, module.service-accounts-tf-environments.emails) +} + +output "audit_logs_bq_dataset" { + description = "Bigquery dataset for the audit logs export." + value = module.bq-audit-export.resource_name +} + +output "audit_logs_project" { + description = "Project that holds the audit logs export resources." + value = module.project-audit.project_id +} + +output "shared_resources_project" { + description = "Project that holdes resources shared across environments." + value = module.project-shared-resources.project_id +} + +# Add further outputs here for the additional modules that manage shared +# resources, like GCR, GCS buckets, KMS, etc. From ba70fb2e275999440e68a826bc2bda66e80dc080 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 15 Aug 2019 08:12:18 +0200 Subject: [PATCH 14/24] org env: switch the service accounts module to v2.0.0. --- organization-bootstrap/environments/main.tf | 9 ++++----- organization-bootstrap/environments/outputs.tf | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index c5a6b2ec8d..8ae7cfa786 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -35,9 +35,8 @@ module "project-tf" { # per-environment service accounts module "service-accounts-tf-environments" { - # source = "terraform-google-modules/service-accounts/google" - # version = "1.0.1" - source = "github.com/terraform-google-modules/terraform-google-service-accounts?ref=857f394" + source = "terraform-google-modules/service-accounts/google" + version = "2.0.0" project_id = module.project-tf.project_id org_id = var.org_id billing_account_id = var.billing_account_id @@ -73,7 +72,7 @@ module "gcs-tf-environments" { set_admin_roles = true bucket_admins = zipmap( var.environments, - module.service-accounts-tf-environments.iam_emails + module.service-accounts-tf-environments.iam_emails_list ) } @@ -91,7 +90,7 @@ module "folders-top-level" { parent_id = var.org_id names = var.environments set_roles = true - per_folder_admins = module.service-accounts-tf-environments.iam_emails + per_folder_admins = module.service-accounts-tf-environments.iam_emails_list folder_admin_roles = [ "roles/resourcemanager.folderViewer", "roles/resourcemanager.projectCreator", diff --git a/organization-bootstrap/environments/outputs.tf b/organization-bootstrap/environments/outputs.tf index e9167033a7..dcf061ba54 100644 --- a/organization-bootstrap/environments/outputs.tf +++ b/organization-bootstrap/environments/outputs.tf @@ -34,7 +34,7 @@ output "environment_tf_gcs_buckets" { output "environment_service_accounts" { description = "Service accounts used to run each environment Terraform modules." - value = zipmap(var.environments, module.service-accounts-tf-environments.emails) + value = module.service-accounts-tf-environments } output "audit_logs_bq_dataset" { From 7b9d1fe7f721fad060b9e7c6cbb1ea7140267235 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 19 Aug 2019 20:00:56 +0200 Subject: [PATCH 15/24] Merge ludo's branch from forked repo (#2) * org env: update gcs, sa, project modules * Use correct folder ID in format without prefix --- organization-bootstrap/environments/main.tf | 38 +++++++++---------- .../environments/outputs.tf | 12 ++++-- .../environments/variables.tf | 19 ++++++---- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index 8ae7cfa786..790dc8f7f8 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -20,10 +20,9 @@ module "project-tf" { source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.1.0" + version = "3.2.0" #source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" - parent_type = var.root_type - parent_id = var.org_id + parent = var.root_node billing_account = var.billing_account_id prefix = var.prefix name = "terraform" @@ -38,21 +37,21 @@ module "service-accounts-tf-environments" { source = "terraform-google-modules/service-accounts/google" version = "2.0.0" project_id = module.project-tf.project_id - org_id = var.org_id + org_id = var.organization_id billing_account_id = var.billing_account_id prefix = var.prefix names = var.environments grant_billing_role = true grant_xpn_roles = true - generate_keys = true + generate_keys = var.generate_service_account_keys } # bootstrap Terraform state GCS bucket module "gcs-tf-bootstrap" { - # source = "terraform-google-modules/cloud-storage/google" - # version = "2.0.0" - source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" + source = "terraform-google-modules/cloud-storage/google" + version = "1.0.0" + # source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" names = ["tf-bootstrap"] @@ -62,9 +61,9 @@ module "gcs-tf-bootstrap" { # per-environment Terraform state GCS buckets module "gcs-tf-environments" { - # source = "terraform-google-modules/cloud-storage/google" - # version = "2.0.0" - source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" + source = "terraform-google-modules/cloud-storage/google" + version = "1.0.0" + # source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" names = var.environments @@ -85,9 +84,8 @@ module "gcs-tf-environments" { module "folders-top-level" { # source = "terraform-google-modules/folders/google" # version = "2.0.0" - source = "github.com/terraform-google-modules/terraform-google-folders?ref=26db794564" - parent_type = var.root_type - parent_id = var.org_id + source = "github.com/terraform-google-modules/terraform-google-folders?ref=2cd6a08" + parent = var.root_node names = var.environments set_roles = true per_folder_admins = module.service-accounts-tf-environments.iam_emails_list @@ -107,9 +105,8 @@ module "folders-top-level" { module "project-audit" { source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.1.0" - parent_type = var.root_type - parent_id = var.org_id + version = "3.2.0" + parent = var.root_node billing_account = var.billing_account_id prefix = var.prefix name = "audit" @@ -136,7 +133,7 @@ module "log-sink-audit" { filter = "logName: \"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName: \"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" log_sink_name = "logs-audit-${var.environments[0]}" parent_resource_type = "folder" - parent_resource_id = module.folders-top-level.ids[0] + parent_resource_id = split("/", module.folders-top-level.ids_list[0])[1] include_children = "true" unique_writer_identity = "true" destination_uri = "${module.bq-audit-export.destination_uri}" @@ -150,9 +147,8 @@ module "log-sink-audit" { module "project-shared-resources" { source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.1.0" - parent_type = var.root_type - parent_id = var.org_id + version = "3.2.0" + parent = var.root_node billing_account = var.billing_account_id prefix = var.prefix name = "shared" diff --git a/organization-bootstrap/environments/outputs.tf b/organization-bootstrap/environments/outputs.tf index dcf061ba54..cca71987d0 100644 --- a/organization-bootstrap/environments/outputs.tf +++ b/organization-bootstrap/environments/outputs.tf @@ -24,17 +24,23 @@ output "bootstrap_tf_gcs_bucket" { output "environment_folders" { description = "Top-level environment folders." - value = zipmap(var.environments, module.folders-top-level.ids) + value = module.folders-top-level.ids } output "environment_tf_gcs_buckets" { description = "GCS buckets used for each environment Terraform state." - value = zipmap(var.environments, module.gcs-tf-environments.names) + value = module.gcs-tf-environments.names +} + +output "environment_service_account_keys" { + description = "Service account keys used to run each environment Terraform modules." + sensitive = true + value = module.service-accounts-tf-environments.keys } output "environment_service_accounts" { description = "Service accounts used to run each environment Terraform modules." - value = module.service-accounts-tf-environments + value = module.service-accounts-tf-environments.emails } output "audit_logs_bq_dataset" { diff --git a/organization-bootstrap/environments/variables.tf b/organization-bootstrap/environments/variables.tf index 80ea02d111..a53b07113b 100644 --- a/organization-bootstrap/environments/variables.tf +++ b/organization-bootstrap/environments/variables.tf @@ -8,23 +8,28 @@ variable "environments" { type = list(string) } +variable "generate_service_account_keys" { + description = "Generate and store service account keys in the state file." + default = false +} + variable "gcs_location" { description = "GCS bucket location." default = "EU" } -variable "prefix" { - description = "Prefix used for resources that need unique names." +variable "organization_id" { + description = "Organization id." type = string } -variable "root_type" { - description = "Type of the root for the new hierarchy." - default = "organization" +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string } -variable "org_id" { - description = "Organization id." +variable "root_node" { + description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." type = string } From e0826a7154ad316166c9fa4e42b6a91e08de2554 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 19 Aug 2019 21:31:03 +0200 Subject: [PATCH 16/24] org env: update folders module version, improve comments --- organization-bootstrap/environments/main.tf | 34 +++++++++------------ 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index 790dc8f7f8..0c5e1974bd 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -19,9 +19,8 @@ # Terraform project module "project-tf" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.2.0" - #source = "github.com/terraform-google-modules/terraform-google-project-factory//modules/fabric-project?ref=32a539a" + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.2.0" parent = var.root_node billing_account = var.billing_account_id prefix = var.prefix @@ -46,12 +45,11 @@ module "service-accounts-tf-environments" { generate_keys = var.generate_service_account_keys } -# bootstrap Terraform state GCS bucket +# bootstrap Terraform state GCS bucket module "gcs-tf-bootstrap" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - # source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" + source = "terraform-google-modules/cloud-storage/google" + version = "1.0.0" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" names = ["tf-bootstrap"] @@ -61,9 +59,8 @@ module "gcs-tf-bootstrap" { # per-environment Terraform state GCS buckets module "gcs-tf-environments" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - # source = "github.com/terraform-google-modules/terraform-google-cloud-storage?ref=e7243fd" + source = "terraform-google-modules/cloud-storage/google" + version = "1.0.0" project_id = module.project-tf.project_id prefix = "${var.prefix}-tf" names = var.environments @@ -82,9 +79,8 @@ module "gcs-tf-environments" { # TODO(ludomagno): move XPN admin role here after checking it now works on folders module "folders-top-level" { - # source = "terraform-google-modules/folders/google" - # version = "2.0.0" - source = "github.com/terraform-google-modules/terraform-google-folders?ref=2cd6a08" + source = "terraform-google-modules/folders/google" + version = "2.0.0" parent = var.root_node names = var.environments set_roles = true @@ -101,7 +97,7 @@ module "folders-top-level" { # Audit log exports # ############################################################################### -# Audit logs project +# audit logs project module "project-audit" { source = "terraform-google-modules/project-factory/google//modules/fabric-project" @@ -124,8 +120,8 @@ module "bq-audit-export" { log_sink_writer_identity = module.log-sink-audit.writer_identity } -# This sink exports audit logs for the first environment only, change the -# parent resource to the organization to have organization-wide audit exports +# audit log sink +# set the organization as parent to export audit logs for all environments module "log-sink-audit" { source = "terraform-google-modules/log-export/google" @@ -143,7 +139,7 @@ module "log-sink-audit" { # Shared resources (GCR, GCS, KMS, etc.) # ############################################################################### -# Shared resources project +# shared resources project module "project-shared-resources" { source = "terraform-google-modules/project-factory/google//modules/fabric-project" @@ -156,5 +152,5 @@ module "project-shared-resources" { activate_apis = var.project_services } -# Add further modules here for resources that don't belong to any environments, -# like GCS buckets to hold assets, KMS, etc. +# Add further modules here for resources that are common to all environments +# like GCS buckets (used to hold shared assets), Container Registry, KMS, etc. From 7fcc20f5ee935179ee0e35e4805039ad978b3811 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 20 Aug 2019 20:20:14 +0200 Subject: [PATCH 17/24] org env: initial work on README, diagram, add variable for xpn roles --- organization-bootstrap/environments/README.md | 76 ++++++++++++++++++ .../environments/backend.tf | 21 +++++ .../environments/diagram.png | Bin 0 -> 57325 bytes organization-bootstrap/environments/main.tf | 2 +- .../environments/providers.tf | 1 + .../environments/variables.tf | 7 +- 6 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 organization-bootstrap/environments/backend.tf create mode 100644 organization-bootstrap/environments/diagram.png create mode 100644 organization-bootstrap/environments/providers.tf diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index e69de29bb2..0c43748a3d 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -0,0 +1,76 @@ +# Environment-based organizational sample + +This sample creates an organizational layout with a single level, where each folder is usually mapped to one infrastructure environment (test, dev, etc.). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and sets up the correct roles on those to enforce separation of duties at the environment level. + +This layout is well suited for small and medium-sized infrastructures, where a limited set of teams manages the underlying infrastructure, and the complexity in application resource ownership and access roles is mostly dealt with at the project level, and/or in the individual infrastructure services (GKE, Cloud SQL, etc.). Its simplicity also makes it a good starting point for more complex or specialized layouts. + +![High-level diagram](diagram.png "High-level diagram") + +This set of Terraform files is usually applied manually by an org-level administrator as a first step, and then reapplied only when a new environment needs to be created or an existing one removed, and serves different purposes: + +- automating and parameterizing creation of the organizational layout +- automating creation of the base resources needed for Terraform automation, obviating the need for external scripts or hand-coded commands +- anticipating the requirement of organizational-level roles for specific resources (eg Shared VPC), by granting them to the service accounts used for environment automation +- enforcing separation of duties by using separate sets of automation resources (GCS, service accounts) for each environment, and only granting roles scoped to the environment's folder + +## Managed resources and GCP services + +This sample creates several distinct groups of resources: + +- one folder per environment +- one top-level project to hold Terraform-related resources +- one top-level project to set up and host centralized audit log exports (optional) +- one top-level project to hold services used across environments like GCS, GCR, KMS, Cloud Build, etc. (optional) + +The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging the full array of [Cloud Foundation Toolkit modules](https://github.com/terraform-google-modules), especially in the shared services project. + +## Operational considerations + +As mentioned above this root module is meant to be run infrequently, only when the number of environments change or a new service needs to be added to the shared project, so the advantages of automating it in a CI pipeline are very limited. + +Regardless of how it's run, the credentials used need very specific roles on the root node, and some roles at the organization level if Shared VPC usage is anticipated in environments: + +- Billing Account Administrator on the billing account +- Folder Administrator +- Logging Administrator on the root node (folder or organization) +- Project Creator +- Organization Administrator (if Shared VPC roles need to be granted) + +### Prerequisites + + +### State + +### Gotchas + + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| billing\_account\_id | Billing account id used as default for new projects. | string | n/a | yes | +| environments | Environment short names. | list(string) | n/a | yes | +| gcs\_location | GCS bucket location. | string | `"EU"` | no | +| generate\_service\_account\_keys | Generate and store service account keys in the state file. | string | `"false"` | no | +| organization\_id | Organization id. | string | n/a | yes | +| prefix | Prefix used for resources that need unique names. | string | n/a | yes | +| project\_services | Service APIs enabled by default in new projects. | list | `` | no | +| root\_node | Root node for the new hierarchy, either 'organizations/org\_id' or 'folders/folder\_id'. | string | n/a | yes | +| terraform\_owners | Terraform project owners, in IAM format. | list | `` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| audit\_logs\_bq\_dataset | Bigquery dataset for the audit logs export. | +| audit\_logs\_project | Project that holds the audit logs export resources. | +| bootstrap\_tf\_gcs\_bucket | GCS bucket used for the bootstrap Terraform state. | +| environment\_folders | Top-level environment folders. | +| environment\_service\_account\_keys | Service account keys used to run each environment Terraform modules. | +| environment\_service\_accounts | Service accounts used to run each environment Terraform modules. | +| environment\_tf\_gcs\_buckets | GCS buckets used for each environment Terraform state. | +| shared\_resources\_project | Project that holdes resources shared across environments. | +| terraform\_project | Project that holds the base Terraform resources. | + + diff --git a/organization-bootstrap/environments/backend.tf b/organization-bootstrap/environments/backend.tf new file mode 100644 index 0000000000..deda6810e3 --- /dev/null +++ b/organization-bootstrap/environments/backend.tf @@ -0,0 +1,21 @@ +# Copyright 2019 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 after initial run once bootstrap bucket has been created + +# terraform { +# backend "gcs" { +# bucket = "" +# } +# } diff --git a/organization-bootstrap/environments/diagram.png b/organization-bootstrap/environments/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ac6d79364a2490ad01572194d48d65ef971ff4ae GIT binary patch literal 57325 zcmeFZWmuG5_cu&Q4qZccN(|jCCEZHNNT)PNBQPM+2qHbAfRuE1hlF&e(k&_VoO9jp zeckbY-cRqR_jn$T`7p;hGkdSK_FBKalDFEL%DC8+*hol7xT-2obdiuyVMs_QT$t#< zld`ZnJ|rZ1B-JN!`o8A-Ss1)xHWm z#+634lm3q{1HaVhOIF2-;anOqR7|^VOmJ(TK`2h3JEL|^*=d5VYYAu9;4?V3FiE#XhRSJ z$M_Fv16=%n5cnlwVX>pEMj)!g9@W^`YO7nhEs-k=GB0m?-vSQnE8eU0X45&L8^*u* zu?qtPQGkIs)%pb4*9J6kDKojb&17CAVLZ&EWoIg~o=~d98c=XUVc|irjdlMtPs|m$SMoR+w{5P>`jUw+S{Qqv+|D;u2Ar5L;=n%0jmX zmcKXyf^lJ!mD~0vcSSmg33*Nr!$@CcaVy~Zhs1aofbS7~-wy|b5QWC<EtmI zoN!4igNB}7?#t3AR06X8B?Sz~sSbje^0&aDY1BXah>+_fVPRpJf{pp3-XBMke6bL}T6nF&_wW zA)Z(^-txeClb|Fy*=PlQ(fR%*i+Pm@C9Cs}fNFOusdW|$A#QLAK(>yQRuiDdf(!{C zP2x_C%~j!8ros--P*{0+&-TD*bgyhUg!Y+p*8ob=0V|oL=955(A3Jm~-S0CUk4SVw zbuyai(_BY=q))zmaJ7$B0ss}3gn&V%w%Kz79K+Du-xVd8=;lH3ozl@_)ad!!!(I47 zdmqUTAq+^?63|LYRGZ(x4!sQR5Z#Td^wh@ekO3UH7Da8VPFv#h0!?R1CT6W>PVXTZvj^35yHym z^QQ;s{)gd?#R1~>cADb{Rtp8RfUD*3`=SMeGT}-|umS=?0zO9-{mA;4fI>R8{d@OA|2Aw{ z31Ff53hN<&GIT}&zUXZy^y`3SJgNM=)d5i<0iUD0@@p_76OCm9bX>&Dq5h4mA>P13 zHI_rE2p*pRJj#ybe|2F3tVH;1KMk-FB;a%0RjYIr6y{th1f3LB82>`r1At10zEo}m zk5Rzdp_v+bOG>~p&f9Z*fH05|!e}25Oazp{A3>*p4L-B%KaqDHpmH*f+XTTQ0fI-d zjQqw}V3~!nDocbg0EHI}Nw#MMR=PmYY5Sd4ae2B<8gF{wrHNRQyrU0B153RtEN zZrg_t20b7QB({ksz)A-I!w#cqQvX6`J7qwS617f%VYka7c>L*i3r00%h7i}&_88XF=^-EvKQC5LC-3Q?B^b_V@1h& zu4{uDesY?oEUrDFid<=a^e_t;s6civcd<;Lk($xVzC>LvigU0oog~fG1(syJ1@HJ_ z#=DiiSz3qZIjv}IJomU60^Uq@C6rTgZc!SPv7tuh@s5p=!WCcK_P^P%ep;b===>bD z*^~@#kXb+PlWAWFV|%${NzQcIoorobdLji-gKa+-siRL9zS<212h4fZFPst6=F}cv zQFJj3JI(1!(`;-yNk&*_X<=*n+j6?#IK61fGj0U~`u=&S76D2KH)C5MmUqXZ zo_K{FucVH;$8V^KVP5adJ7r{Eyff7!VD!hAKT>(&OSkYg=DR70Z(rvTeCAl`>8u;m zJ|Qfk|88``*GmI$Fo20*rH*SEy2}KCe6^LoqX%*=||v56((P$UH57F zT03_S-hDaV2^LrCM$7Femhf8+qR{*ehX{`HU@H_fm(5}#?x$x65P197(Q6{K@Hu!y z<(py)2N{EpblBGDwl^a*#%G#0?ct9)mjqcHtZ$`zA*_=Gdezy;|0SsVV03k^ifYe) z2IXAZ)vj+$B#=ScbMq)g)WdEzJ)X#&SS4uvdgs?? zUV908RdKbLEa#k}F}Iv*6JzB0v+Us+Tv|FS(Yn-+zae)r6723pBt7@p7%&LhK3zBQ zElOrrSO|xDnz7#ddh$zi>_#wrQ}2>;i*9t%#dbTciG(=o@kPMHUg2UZU?88mAq?T) z94*mM06(pvR}MC|%+36$qn>sgsa7JTI@-hBG;vk7QV||2w2|b~*PTZ@b7?L0~z;A1UpA z_LqCg1;BbW7l&&TUnMA-oq?7^lEFlbMmkNdmwed zc9-AC(nK!o(JM3t*JJuKJjms9*sTv;-(-qdp%yse#}-Iog+2I+oP1Tgm(t0&4Hd(Y zDIHUH!5idnJ<>(Y8x+Pnk{&&YrNDJB&3G9U`c<#3J@Yp^h~{;uHdtIfeiXP_6J!?< zxTEmbd1C=@v#KoZ-a+*DggwZ9yx8FF%;VMUR(aTDtl8~qI@x}3;KJ@Y6u6II`;;*w z!*CjRMBv`1UCKuNO&Pi^s!;n^Xi*A4;?G5Ze|luLe=yB*9m8!4sv&^6nb=y6KgOw8 zjVVa*J1Vq66`HboIrnqmYF*_SG4$CII6s{-ru=KwaT1aF{AYHC(vQ#FZX2=1EU&h@ zv|ef&1cUrf9%b`+b{>7LVFmn`@S%Lf6|Mz9#j#!p62L{KTu5iR7nJ?E(%sQ(bl!Ne zk12wy?YiIn&$(G1f8F^Zl2Xk0evi`$lf_`KF88)4?flZGoZW%z(D#rplWw^l)vieF zP0Nu~WN_&S@|fUzlH;2-ti|63tnNzc-7y%vFtN%hnFEjJ^FIMjUwg5j>_*Ol!%zJ# z^N8JCehxsU#~*!L|MlprVO0{0D*(MN_#_&#)1k&8@9^T*=JOGy+Q zA4Vo7x;dsu=w$lTaMG~v42|5hgx<;Kyu$DyAJ<$KjOLovQ`8Y6pZd+=cMSB|MZB-` zAcl$}f&9&-Vos$Bn&b(+3)ZF-Y;#T#y8ZD;%UBU`jPL0y!~%}l}-`q{5^`{&x|S);QE-ZV<+3F_F!?Vj6Af7jK0s|bYq zi#6FeKS}8oZ5!9~IA@GFIDs6dCN-?%gIVN>PBEY5Aa(PK(kX>CxkuMlTHUJAXKT8y zisOoU*Lll&)`ZT4&ho4_dNClF4)sPTj9LBLFodTlfsEZX9S&vu({jSA9_{+3-rg-9 zzPyFjw-tc0$mc1oR||NAvM4qH=b1PNSX@N|-j)%=z7TJ%Z&ZY89#dx6y#kJQA)`;! z99aDV@Gi@xUca*h+>J4A;G9b%phBlBfk=8rsqnRv*S`J)x2q8we zCu|_Kwv4!Ie|I<6-!4`WaXr5#ULTHN>D8JE%Ry&(mU`uY8?u@hpyQxhhnf}e8e0*5 z9~&sX)knnK_<2-%SsujdRw`{wKz$`SG;bmXKqVWyISArP0{qJ4zasohgsYE5Fo%Zq z42Tx{Ie>5r@cI9*tNMR%Y!ccCLw6eH5lH9}OhGf%qUYct2n}ML@%c^*VD5j!NUM}6 zCitnJMuf~N$cZ4#p+Cbdu6+F|#=R*u5Nc$M2p!WQ3qT480t!7_25OgT&F1%uSY->@ zK8ve#-_|kT?1CPEhBAPLZO-xiNPtNKw7AsiRSReegVUEpL{NRg(7Fv?J1RPy#s4ei zkOUYL!;)qmAd+fEAT<~g)AdVTK9AI*)=?CtQg!wlQ&l46$cZ2#s{Dn({J2aS;NJBW z53Sr4-ybYSVtOjIG*LZDiq@$7h@P>(kJPLJAK^Se1Oc>AXv*mYtlLhJzO4iu0~@Sc zMEauE5rhI_5Q62{vjDa(4M2qOiGr826!ws#j+s$Bkb#+;5(o{K&gW2G!$wIO@31=6 z<%!2T6=LA(k0e)>z?A;>nQ~5X3a_@aPqjS_?NeC57z<~6TCc>w?))Eg5`)Q4MU8k< z=aps1%!^qOXfg8bKM%f?IHRbWM`4~uX%Df#xQJ=8LVeU))o^c9lvE(wYm(`^c`GuH)S!cy05r9*$)wbY!s-z|38wW)Hx6_q@3 zTjRU>gF(;8KiFIFd`SnTb+Cc%)z!ylf^KcENY|%m@1HRL{4gUviATqZoJWLtF2cYk zj%;@UkM359&kP6Z87Yz5LN%eq!VkZTLo| z)Xt;YE{2x&b5M+WF1i9M{88E%6YJP40g~kbn3l7ob8VcmOCk$NrJVxJuLu3~J6u4v z61NVKe8chf-nBycVi4<-UkO|pqMEsug}m#yw1n1aG+qD5n)w7wmp=luHXm zy`#H6U?j?0WwrlDfYVa;08N+owgg^7V#pJ@FI%Ysp|oshSfYutx~2pucQF~agOE^j zihQ0V=uBve(7f#_=OD5JRQ1uB7UpIdgRNo{lLZ;9;zuG_FvJ#E0aeW6GYzK#_XL#( zYeL8b7Wld_h9P`yZJm>vb8}9<;K6;C9t&xkkS3&DmaY=;3ltH4LBqQ+XSv+wBD{~1 zCjGDFP|^vaa4zKJtwLCx_gZumFuMSAFDzU;C-KXPp?-10OhEH9j7O(8AhcF1-fdDI zxL$wfN)cP|w_NN?#hD$UF-N|6qCimrmIu3tKXZ7pGHyfUiWQ1P$9r~Q@twR-4XKJ) z1Cx4WNO?X^9r9kExYyOP6x zj!5ZDQV3Ri`v`-v_nMpY%;aQlW8*xuskynC01U>wu6rCEV{-5IS!Uq#_Q64H2-4*t zzaF^pWp6otDW;0>_@J?pynWAygCyCbdRera0qnkKJsS~FMK2ouxZ^lFOASZkJqHw2 z2|nal%FUFNl&ibTl?tyt!^s&5Tb-?~tw=TA77|reRR@@bQC4Lo|Lw)XD-|uRy1mzG z_&wd-tI?0kWSyK!Cg!WZz>_;6*kR~)eIu!S6+BplEagH?ATL~8hvF()0MKI5i1JVg zT-tB3Xn8^J9y?U9wT<`gwC@7ITuZP$!+?1&-HX#u9+5a{*5W6W%57q^XKA=Mgtu(w zvlLNS$X@Jq@b!2jUgqUOt@DNC{H6eFG8Vq{Rj!FQJ_ji4P*ZrD5v6jkRjlc|VoL=T z5rj3AC-FWC??Nc^1lUlGbFch{-3Np#Qe|JRhIMvCfF^_8CHxU!(eiMyK@*FiW>P1f zR?79DW`+}*KEkCOPixT2&G6ayy}*bls%3A?k54f7Eq3QZ&O|?q)6*cbISU*_!i^RkDErGTCIUwThYQ0ZD>32WXa(mC zFOR$MWu8Ex@QHb+xrDYhnZt%HTC_)x?588Yz&Jo=(HU7{AAuVvIkZv?SV|w$wJ$*i zVTh3ke>@fBRMb!i$yEjGSZ zR#a33u^pkNmhk?*R$C&py0`a^>Qysgmbh1?;go%_Or{Tsy@WR-4=Xn6jlgxxM0+K} zKXdvoEdZ~Tb~ga~-}k5py7ObnD=2>I>Iyk*dk9jo`^xm|!;2s}2ZtgijoC9NQ&VbI z^)!nKfjIqqh-Wx$j+Y2gc-1&fE8;WmF;Z|x`5mr3Mn;xKx`-JRb4XJ++>P;6^lI2{*)Q1x(gq z%wq_bIZ_kz-{{m^V!*N`<28@b$Pmst?F+j512?I28NRXV5I zOXZA(WH(;ZdIAaW{qpPQ%5+h2afaTwG)o8i0(jU8Lhqj`e*-Y?e3v3_NDsKp{i4}S zX62yU@VGC}8v$3uw@3nyPP&9Sv%+(@>-APvBq5WMp#OHu<#JadlQP7j9jTub&td5M zM}f7C4Uebihf7V5w=SHxC43I``3ncl?8|uE>GJl$*}?bM={_3JLa~?LxeNenxNDX6 zL`cb_zCbwya`tRy96|&5`%Z@-LW7mSOB#NDelEX(BuJ)^BN-lz_-D+#A>KCA8lN2h zLor3LF;OIuW_o63Nh9}H_P6f9pS}!JQ*w*n`5P;;*tc(W_&=qlrgA#>pHpMt(^fyX z8Z3inicMAmk%{UDXGaYnQ|j31w6cv>Z@CQ@0AddX8nr4<1m-qKj89CELT8Qb)xf~M zBu1ug5e^U0koPZDse5~SU*2AwkSXSU!K-zedy^CNN7ZY^U8=XDB;&hQo<=+>cI!^i#c1%$wD2& zh2(Gn_m3^6-{ARC+s%iKdn7vbe&-HOi}i$$9zBXidat%8;kg~+F(QUx*5n<5X)RV) zXhJ8o1qx*~x3r|BH}7~`>!-v#&bD;|&VDEt z^>W9m)J`n8G~q7Pz3MZ15jeF;Ax|*`AX!XqF8%Gz%0q2hOy79>6IpZbIcwpZME#qt zeyOBL=ba`3I;z;8L5qC)L$T*)Kwz#-7qrSQ`2k^le^UQVM}E+>-T!$k>@=G@FTx0V zxc#kFa0pKSwxzf5&t3qDSDbj(7$>bahYGH)~5c5UwotFTzsNb2Lsrvv+noMx(%iX^R%l~S~jChp}>?3EGS zBgapdMIAr<+O?92D=GVjh(SvcR1DSKBB|G#ZDh6Klv&$4CbDh91;Zfp* zF!!xb`{2QchcN|w4j9S@_Z2myhMa*B;o*gw@LI+QxUp_Yfike4-x+q_PgMjN0~s1l z@P8S)lV@L%LGgQS)tVmar;V0>M@j3IAz9%n>4OpCh53uDpolAGo1zpLh zGUwIylY-q{w24Ml@^$9;-a85yV|TT;%Ld#W)YbplEoNY-tg;#f3EV)OlQOG;Gu3Yyj#7{)xytr3M6(y)k5y z>e}}=@E(7bU=}>98kN@bvKb}q)Rq@|xW8@V5XRU_gAGp(l7g8CJpTV6HlQbRc`nkzi!d}ox7Rhgto4&xZ#tOMG^KU@9F6M zD}aChVWPF_^O*hZx1!Y20riz0^&VTARyH=AP5G53>}%yj#+)y84xQc;jBCl<>CZiP zDoyKwR|n3R`7rSFkI$DM+}{9r8}>7(<-jEpKPf~4z}vK)bAiv2D2*qfQf8g{A61={_Iu~+B6wJJOpkf}?~gcJ|zhn?RZQ}bJ4>gwteP*E-H zbLC>n+G|xNL~Dd$;2Vzc4SrN`T-QdkHGzL~aH=qj>>Q(HxHlUGq;_x~=QyqKk80jO z%LMaJq-NFHv$xiTM>{|7$XIqf(n}N%ijvZ(ZnFI=Xqv=L>(mytww(B>dPUzv`&dab z+yFld$4ewii2vfxCYI#!k~xVagghq>PanhFw&r$F_hjhj)`_doY1Y(GQSqE+#|p<_1qa(bk=(+)sSiK z{oup_*owT0V${=4ih=KqwxhJaT%J^DI3<-7BD{p~?4YZ)$b>8dHAQ*tU*a)iW7;M# z3lFm@$UXsi7;<2PrJaDR^r|7CsTb=$PEwz1jj$DJKA)MycWSq_y4hI)P;!NGc^}%d z_fE&>;)|%n^bl))Ha#fMs1+u$e1}SrN87oGG5Q5uZIAx!n>y5&Gw;(UMg*idn^W_` zlekC6&JPyIZL?qJdu{hQR>R}ct{~F)R~t4jr;Mo>@1IcDvR?drUGY0f zjVH9a4e;~~72(X8_6MzsjvM8_jsVLZdui4JSa$UaQ(Q^p+0b$2A;u+q0jHe*oXIkTy(Tp9!y2h#kxJr@X(>`^-hA*~A7aAUplY z`C`op5Z4In?&1Yd$r+lUDb)eAyAkJ)Q%+|&fxme(&V&z(Wp_!R>)qh1l_4Hk zRavx8#E~!iS&Y_-e&VrVa`M7)vB07m_KTH&JOXMwS-hz^dXjYF8O}Q z9<(-G1;#YGjzz6x_tlG0X`Sf)UC0$Yk~GY7@0odawdd}%nDg$F-;3~WW=5N<>#HGa z^4#F<0h*_d8`T#+*2wAq@5N>jd>O}Kbb3tO_|dL~FwN{1@wvkw>Y~EXU|+T;EIDs0 zK9~YojQgVJ8P406y;X>s1J|t=MJ+9-;%i^F?yMl1UNb)QrGO!w@+*OWHcuA*IFj7& zB6w>YTWjkX578BCwFldctF~4(I6(0wig{;nvB4w5%wnZ7ACq&=zREK;%;30Gzkc-% zj?=uC(_9R5LZSo)%S0h}51|`-NpCz|{BnyK@w6(`?w##(qgQA#OrY}$TJfC+{`a@{ zLah&hq_sA53M>HxBst-S1uvrC@lYfGcIzbWJf@pCuDio2cdaN{!^`_Ff&x{wU9YBc z>9x*Er`xTtcFwaYWxi&aEE}dgc&zX%`sk!9$zbr=yWKE?Oi@r6ZxfzRMo{O*1lef} zeAyh0GN!Ls?%r{~EscTbW<}&l34QF$}(g!}mKr>v(WJD&-`UO^aE`J5(MmM;+!yv}%C|TL>b<3Iv60_OV<+ZU_Q+bogM(Vh8uIC% zT+oIuC$=6-*Ai0z_4?zuh!urzYq9Uki3J*ezygn@stJdDXcZLWbwul`qLg1eF?AQ= z+2h*a?5f~3?{t)LQ3%x{_~dt?_{}a@(8|_g;T};pQfxOH-8ko?ajx#bFfsboUiUx` z!sO~(J20vER*k&Sw-`|u7}UA{>$U7rs7l<4{Bs*h@1I5tZ3{xa`_~JQlB_I)0*KQc zOL}5r=p!7mXMsbti(=J(WFM0PPBX-9{YfNm#7ezep2mz(TBPMSQEEZE1ov0)V(bWb zu}T#kHzrsE+~_Ygx!vWUA8^lne^k)IjvcKq9>Uyx^*&Eh3U>jgC_!jj^LFdU5UHCT z8H}SVFrk4J!$2yDk4qZKtErChT!n*EUe9&&w;rE#{)s|)sUwx6Ba6w5jjqh4jMm1K zkXW>D?9l?b6pEvEqn?uh!`fL8q;xTacf|UXf;(u`&mp-}TWeXAFSVD~N?_bk=T)>~ zf7a;gwLoK*3V0*y9A~plns_KKB5&Th^>xP4DExMv$7T`Y4x&xo za_RYjL))QO7bkU6qi&x2?4HN`EP-q=XxCoO^+?{#kIte_$5!z_Iw$$IHRQWW~gk?-Nbym$Q*dXLuqWhP0+ve(?~ z?YL!H%Fd&Sys8tW=teWM7WEY#hV$dg-=W_IFr=kfr=CO3`GdIo7*>{BuhwyXHKIHA zP^=%rs9a>--79O#O76{ixo6b+TVgIq1|;XFKX&Cii&Oq_7`+0nSa`4PqK51;A*sRC ztjQ$O=zYm6*3z8wgs(ORK1UfRg+%i5bs!_+(}r4on1hIty=#I`73J>VeuacHpg`nGJAKuE) zX2tk4kMx?w-;suCaLd!TU?-mE6r6P2^K74vgCwVRJ{LMTXKsElEA|##+cD9Jw)`a| z`wUG!utl4H&qcCf5Z}x^Ky9bk-d+RjH)9PwCl2o0Uk?IbtmD*gU5(gG*eV#dgtD#N z(~-plfz*KhqN%=~QUj{8w7AK=jL05?KwlBsyBD;EvA&pXQj?NcnzNN3+(t1RkK6X{ zV{whV>l`=h_u%WO654tmrw*_Xq~Irt;Y|FD)ISo}2F<*ccp@Sqp0bWaM`IU*sj(iX zl;qYe`xR?E*I1w&TW;}BaTM9-VC4GMh$pmud^Vxg7eQ{meaut9y#aK!M3C(@rpr1z z)40d~gqKvS6TTreFc=i#_410Ph{+?m=^9d75)v$$C;i>=qt}*JPvyz*jFsH=yOZUvqt!0jy7fD9N||&KZii`M=h74>vkZ-$a<%6} zfx4483g#bepfvhXVc(>}?K7f*{G&vJ*BoU8oM&nLiZMV4Xe~AW7-}n?Noizvzc%{6 z?N#Xv=1Rv>AjWI;mdJPJlr}3w2(_)u&cHBG`=$)G`%+H~Ws@=)#ZgYA!aKG2AD6!ATv+!TRALC?P zJcq>|7nkWhhqtaG6+JE{VbC_v^BC0okx01xo*;g9n>~fw&%7ol_@06&`)huhK)^iF z4jlnOh-xw`gQTRX26@r0%ERxi(LNwyWia*XxoUP@YN?>OI3>Q!?J>vP^Mlw# zd*rLn`guvKO3+qLz4t|ZEw7$)h2^sk4U`MmTr|A6@~f+I+cd6~qd-rA=cc6O$-2n1 zwp7CAZM|sk-=`rc-AEy z`R5X@<-(nxL|vTw#+4(iotrQ1rq|@ae2$zv8FRvW!p?R~pN+!TPzA`K z9WU~kYO=7pQf)zx9~URQ(2jc3$w>W}OS~dudu!_*4;Iaj1`p*c@Jk(!(+|8aZfu9s zBa)Jcioc-}>bo0i9U|+S04Y&eaarTwPLm7&q0dSnxwW;mL*#f#wve0elGh9a3rkN~ zEzRqw&M`v2fay6jD`}f8S|zuWfp9G#WgYb{&WYp=^8KL9eP14LjCXH2A4Sg1jKZK; z*VGHkwe?@K)okQFgI*=A$CSRbBaJ8CrZY8saCMWt5WNWafn!GPe4UrG@I^n{pmABL zm-f$PUWnH&d71~Uf@;>?!iWY>4h$tus~Y!@Ymu9F}0@%R!!!8X{8CM-AVlnVGwd4Vv!nw{n8lFIR%= zOgw8@@tVB$U_kV{#0~GYfzI+tlielz&Gkg!Cx+yqJ$?E#vDT7p@bTl6e05f0D@||J z?U3Bu>Ctp54e&gr+?Of8!mn)_QxWVcD{r`qgL{)mC0rpENSgkKkJPtkeVy`a_^L@@ zwsiI}t(Df!6NWdnWy9ak8!TsUXTI-oQU^BeoLygJrsyY)&bs$%7n4IxY(WnJSE>QJ z=^q=hDsw<+(&RI6f~+6wSp5{N0e1 zrqNlxL(%o#{-HbYN5SC;tmHMZR%;E(O01J4IlC>01C;TwI%UH$JjC0+e22mCj)CK( z;>Y$(K><8z>T`bn%aiq_-l z;BNt3#BO$_fCIK`EoC*|f?TWsvv?Bl8)Du@m)#-t;tRCr#Y61mP}2f4hh9>*-Ib?hKp$E4h5y;-Hjh5-56e#35kn8zw#TPw$!k(M$k z5{N8)Ma$GHUr}n*@flk%LS8uK|>bfs{I^d#nl0C7EknpG>9;lp7g?p{=S0hFu@$R|F zSi%UXrSPlE(n$FUneK;{V^m#Y-oC7OEzt^eokN`4pQ@4fj<22J%4Vae=c2& z9OoHO*`bbJ&GWBNccdCzS5m+^d>E+tL(_%#pTgTAamDHx$@;19M%;LXK6 zP+EEEFj%%Weem&VtyJ;{a=-KaxDhNG&+B(Ud=oEw0q;^-8K6-Z{aw<^omBXi=3~=) zj~RCx*D<7Pb~2~mxv@aX;Qi#?9-RN@aP}Lcpiy~MS!yD5yeHq@Jlb;_cu-J)#Bb>m_@)KHxb4P)rMy7s0Th3)u0AQqGEKR@Stck@lg}@9$Mkg`w z;kL<(OCX!e6KF#QQw3sNtHoA(ASB;de9Y55#>$cP zSHMQqr6<$%S6d!KLx411BI;=S#~Jg8s-R%9_*;F8NQk5~);JmTX^V>C6_01f)dA z311VIO8yV$9f_1J%RHtZGGFW5kBX&wd^@#y0b%NMKTy8lHv3ZI6TKTqNR%8^54T+d z2$iZ>uJ{!Lf1)?nSX)txXP>FJJ7W`jKBQf}?tfcpC00u#3X?};5Oo8izwoB%RW9v~ z;qW+?P9B~8?cr=Rstw*9H?x7JMFqXO#Ytw&B;ecxlCdnTcuz&pfWek(K?^Lc#i}Fv z$_qOE`^5sIPf5O^5opzjWIu~p?NU-m!0mjc7X~G>_$Or%&jYL0<3(x1USgOj{+x*3 z)%zB-w18f7ck$pdDYl%~LKD}#o#SskPEKa01+sP)v#7H*u|yd`!>j{KW*?g_I3HfQ zw@E*JNl8XDhA2}STFtqrp(rY{%+`A~CDq$oJV-~qbGuzqf^N4DK$7zMNIK-}rAxf%R@|NRJ7}| z^?@cNS;H4GZi4TgAE&|b_;FvOhJ9giv7R?}lb5FJpdAP3pr`*lq2*`H1vOA^@mt*P zEI(`7No!*x#}GMEGTeSTBp;oDo*BAS+rL-*cIQUP6GrmsB!V*+XTEMAc47OsUG6S8fFcBi;E*Mjs0nylNYKL3 zxG||zP*fC?35KUzR&Bw6Y)c5i=?cFRJyUaE){Lv_?em@&2_3fI5%nuUVqoY1OZ$ct zsqHye%!+o7k#Ux&p^PBLg(OoO9o}|o8y&7%NWKrLihx@r35XR>zmhjF5Nh;NmHpQ3 zLi;q{rXegO{IS|BaBvda{@yv85bNZD@E=|1k1t8ZoBkm$rqE49jlxq2C;%5)+NYLb z+A)B7WCu3uf;`e+?j3gwVB+IXr7Tga->|sKgT12^WZ1Ir9NM0Uy4w;I78Z8T<7ViX zyokS7Z5iW}o~9Vi9!HOWvp$<+$I1j|4Yq+6fqy33WgkP?l@8~t@Ne)o+gDcb3vjjE z{8UoQWZfY=2&VWaVLK{^Rw*74U%ptUXFhhqPU;+kOhm&P5o3WF?T$;_5%B5oIcY2; zV{8SnSM@F%WGeHQ0CFZwK2GdcZHEF$NIWJhRBp_DiG(mS|I3ciQS!XVFYE(V)tn?S z<3#Hw<}qMaW=z2r<)5Yckkn3XPzvv{qp*vP6RnVELedyuebk7_MNBE_g(O*PFCoKH z4XL2Ffkf*sKM6)WffjO@3u__@8R8g#h zp3MX0ZlD86kOqG)phwfn7YxIr&AnFP00}97#{=eBC4fpRGNMrRSEbdI0aWbhSmAJ> zG&w!ZF(40InLE+Q7!$3S)cpgB`Bp5}rynnViPFSPXELjmO;QL+J_-#*PLEIO)b{A! z|7*-t$`j@9bF7yKM|aFML?&i8D2p`-*mP@OU8Q;UgofG6py653$!{J4J@pqG{AO|b zL?b27+pw%jQ?{3D1pK^@)|1?HfmQ?o2|ftIa=oPgmR4~%9L)Xv0q`q7MkyHygv>#04M=wK)c%!YBqWOcSWv@1h)A^ zu=18t$Q|X zEzr~4q9W|a)A{lv;A~;ZM{;tZJS-hF3Ln;OY|#N%>cshj`{c4W*r? zv%IWKQ*4E4=GkY^e&lvPrWxOw#hHdh@CC#EV%DQW@a}?^AGO)**N+TKSI~HQc{7FG zF;VXB%%s(^6+ST8MxrAO3XbLl6$o(wnR-O2p7&qkP($5N)tf&X^AM*D`Q;S)b!1vnK<0`rBNLK-fbV@-Ha?6LPfEt%HROGjt_~J(U=IigSQM^35VI~UyWIYju`pX{ zNko7mg_u&_Ze6KwYF<6*EU#@vwimNLTptQjZQsC3Yi@>=>y?`vyx$K=V$n`|zSX<@ z&t3po6@JV^%El?0gHIknd1;Z4R<*h8liAhxrLe{N*HqZi(LeG`$eF$O_NUjZsCkqX zHCjyi+DBB|ld+h49x;rKE+lJI+hz3OC{*}4dUNbvL5E$FGtir0vboXWk?|E7>1Sg5QS;0+DTS zO>cs^WP`GWYiwY2FWoHHF+z=ADS&nQ3I1!J@=L=E_4_e zvMAXq^xdOWE$fRn9SdfrxD23c6t9`Hmu*V)V$rp9q^*7?KHF&H@|yIaDbM^95nHM7J!R&ae#jJsZ#8pFJ( z>e!niER}G>a;Qeh()&33tZJ=4z&m`YD5n>D+LA-M(8MEd7=$NB*SzN~QDFqIN{hv8 zJ-llX9H8;6bZ4nc)@{Qirw7 zN+&ou1BaJQ!}G7oJuuG>m#C!tFFZ9ZHJz-7YCV7cBCnSswpUB8JbkSFtRr4s(^Dve z5jmngB(CFFh~jtgJBDz$=(U|aE*_bo+aO}XFj00iO*S08{<1_v&*SID=%5hQdw*OE zTorjI&-cWoUd1ay;7wQG?3a1__N~g(rNIi6BA;nU%RvT@?fDr z4O$Ph_{LF7!CbP4$BLCjmKydf3!|c%43XDCKPE_UKLJTDVXu(KLBO042p0W<6_2e#jq8Y>ffOLAVhA#?y8K& zE-|lDo#TG*k8>Az?lb2V7>Mdk^HugW_qVdKAMZD{=Q%>9D?%|(XS7J(OF$@}g0WZM zR8sCgwov&h!Af;=20}464{4S}y*s-(l}rf4{Mb}QKt}e4kOS2VHM>(=q-<5z^NH-Y zPFIsZnm=j9qRXfeOGnQ!IlR$}EQrG@NdiuVXR%XO6 zkB@b_j=<^2D(GE#EK~j5U)K)d5u z^(|a@4WuZS9#MzjVQp)Gfj$)IuSWfd$=O7OwWF2RLxIOdg^J!fq%r|}u4Nu7f9UC( z!rkCNCiJ&}=O+raWypRUR1Zv)!^=CqJ}Ca)a7KrDcf)5$r#>+xO78j%8RcqNzbswa z{7#_s4W5vz9g4GrCeMvOUAMO9SzQvjIT~w0v z;GZk;dZxSze<45d8hbXBhZTPH`mvFdBHDH5N#}lnXJU zF$M$fQDM_LP5`|$F@XypdTLL{$S5B0?3qMG`<!3*}u zc4yD2DS;o20DjQ!cLr)a1pHy`kIcuH+|5PB#&zZ*tBxGWTE8QKamrPs!!frN!GlW$ z?idb{^?zGAR%b;fX~r&aUc_y4g8w|mQXYQ3+PG*b$8Dbs73JQ zKCncQCs{E?bO>!Y{B<2fT9^{J7r@anv53KWWC6fm89_7}D-aZlwK8b~kq05i049#d zT?>o^21|1g99Kd)B!dAZmS;7>r0%uH)oUD>*y}FvSRN}c;CJ{4&z>RfGX=oLG?t3D z?tm^OEiG&<4C7gF`+yMOeS+Q(zeYp$!Rex*XiD_B!W2mK>vsA?2z);dm`ZSx*J>pb z&+{FKaOL3~p{xSUq-=N-(t0bP3}a4CO|6XzQR$eNy!(gQAL_~j&T7x_$UGLZ4|dn- zDRzl*o9gw^3Sb(mM@JNwWWru8Rnu5yrcBj!d`1Qa@bYr)IWOq|{>f=WB3e?#_}Ydbu*cwf=EnW@fe%wh>lGDFyme(8_@pYp&;& z-8)sE9cRkwfu>cUYkTjkR*{6qr1tyHL2qB*Mt+9NY5%u;&Bz{fpf7bmyx!f6^XEt< z(5Xt#!#Z#64S4V%AqfX%v_%$>+TdWhcNZBj^JV$H5T1TpH*e;`&d%Q6{Q5VE&h~dA zGBVi7MjsP<|Mg|Z=GNB6gqDogpET8>9E9bv~!KP7?OmR4-_Y`c9J#G}nT~+&}B} zeeYF~)j|URw(BCs?CH}#I$an|TrufDiM%Sr@&^vG4_4PC_kvqVIi^&jm)y&`5xIFhmIq!S#xZfD}jQxj)vG)@z=9+7+Uy!G53?%+&Y~=e@ zHC>qjg}FJ-T13jrnqc9bc9VF8W=TZw6!aSJpY2V5p3ZPEf-j-=Fa7H%);iKAum1;! zNi&d$*YWE{WlxXfoWrEnCpVuy#K$D7=05;-&rVR`o)2L)D;v$9P6u?%DIp=xTQg8R zP!pqqxKd@>3BMvpt!|<7%eddW%HBk`4qzEzrfJJ9nvGp0{uP10qx5IVQm*%>xUggu z9XY&J?ea@WrI?Doi6V_y>yJgjf%FK&d-Dx7qxDnO7D?zAzm4jSy9i>lq!^$?Hh*mY z>FH_dsqcY|j3Il?DQ*XKpSJZMAMT;Z*Uv*()g0+Q&tI+llVM(ur~Bm`7Pb`yO)ohu z<}(?>DI5-H=f3XURkNeyd`;xsH3(EpW2 zdjCS7`fmdj1h-ryD7e^xUEsfNbwKx*h!#(LY*t(chn_474YNM|wXp_+KM!5XcCOGt0 z+um&FeHa0X+PfF`#hw`3D4qe3_@U8Su6vF{X+0XLUzFD|d-%xzZJuwQ>u&~9E{7(& zL>FLaDgdCcHNdA*kdn3@98kK1>@|K-R-Ty$kL2&`OJfk{HLKxU5js5`lMSpc^=z!aWGu=@!qA8 z0Twk_KA%rf#4iN+(;$j5hDac`FPJPf3PgR4nfOA(Ur(Yjc_5UkLU+XM z8E-nN)acCZ`bonNB3CYNn{$sSG)}~$8r636>|%fRm6RzYCT5Lvu$n|}HY%vy<(k+f z@xG+xA@~ge=hwaxqg8;}?Zh)l)>BCHvTmJ;rZZ_eFp9Jv&XL>RtNKLse^Iij&-vuFb79VJq$c9|j0w`VE5PF;X~W3EE} z6Ocom)2kF$x(uMFF_{pqTc(eVDJAKMZ+z5NU+DhIEGHlVJ48Bib`$(ma?@_$t>;t6 z6=;N~8ji5ofcVRjEDset`j<%GJePS8>ebyef-~>G1n9ggquSy(D*L$DSXc)48y0BEi`0MB%g=B7h*NbWvOef2t=Np{pd(SF|PDgA9HrBW=C>^3YyZ}l%@=a4gRD>PU}@PqZj9me*~qe|PE6k60P4!GJg!X=zNGgv7*ro~Cj>HvpOE zT_^Nmhp(L?g89WU{Pimq_jv!FM={$t|09oS9MgPn zx^4h??mecX#q_><79LHIAl0_++cQuqO5rgz2qjSG(2*k}oL8XyEtwf&PFtQDwdQ4N zOWorJ);m0O#cAcJl=r=ubd!;4^yT7^Y^G{Vyb8_du6V-4pQz+~0o!t0ZTIWpBSMH-o?L0#`OEn>>-x%eoO+K>8RgKI5{diJcFum0 zzn0>7tiNJ>4CQ)B+4p{*H>UDTyVa<6rFHX)y|q76r)H7$S(fLPKiRBc=Xb${PQ*jc z0G;Z6F-J+hm7#iM6#i2<;%d8(Z+RKo{+5^++$0|!?Z(;fodk;nESoH?ydvoi5J*d3 z%o){4W-SC?S?m%t_VVe}sEGW${;DMPhs_4n>QT?4WyiH%oW)!?FoytiyX1>cVV|=yxsNhDt~74nP?F(!B}et_bVYaEF8x~n7@*foZ{V;@lf^J- z*m^2xOUs4?3b~L;rNn*9p(?7ndnMMUghIqKmYaCuC!_5MR~Rm# z=F2bBn38*pkbkE>#gi*TMkFhXtQ&|;;}QACmym$-OHDs|fyM8Tk@si(rwo(8<)+!TVeImH zBzqX0cfkp5ICUzjVqw>jr6BXo3V0)~n||M`IM>^&xOlR7IbC7Wz==8K==l9Z*ZjJ><(AVIQ5UimhstdG+ z^@DLNtSYjR>}FtsM5fO!)Aizw!<5^YEJRqwf zA{9&2UUK>T4~4f?vvBWJSAk!nS%mIxM**wr1GetTAx+}HgrAGb1g4940nKur5$zNt zt-B;Gz76&4%>_+nA*CKOM754LC1Gk>5s2+SFT2UMk98uQ9=la9hWlC0ItrRzW2MP; z`CSfo<+A);px^mA{wlxdlOfkG z0FeJl|L~(QTx@~>!uCf!igVpHD7V+*LvpY)Q%789P@MZI_(E_;l&JT3`G;T1emqNb5~cZB-E%BT?bZ3@l}FJrFGr9mM+ zBbp0-ERya1Td%qlTM}t^DDOL&y*$5cvRu}v@ZMlQ;wpbMpI7?>;zKx0O-sZX!KUuy zfWVby-pvi(q0SFbO7zvkI5tULB-2>zm>Ny{(--m>7RvlKy<(8lzWpxylr`s$U9)mU z!rh~fNrza`iVw%+flCI>-)OkoV561oIB(Iy`8r9$x|233+2!g!)$F)6SG%=-QT|`n z{UN?7EmDoSjD)r)40(Gyi+!oZ=I!}IcytzNIdATT>+7H!g$mw@F-z`Omr!ntFd0eu z`c#l8O_lPRNX;I$vWkj^#b@7Hu>bHPN0$z7?Wq%$F@ZO_9B$O0HKU$hj&_}JWD(RuB{E7ATVMTB@Pz59cPLprjY zqA;zQsKFP-orRysluLhjgL33utKp_>8_pQ!^|HWm2Zlv3ryZ}YwgNJrOy9<$*Q3_; z>s;y|zubu+SB`dsuKhN>i0%JE+W%b{7#PIPH^%Y$mo%P8SFrj1+XNIIH))J_mO&Qr z|Ki@qTu@OrTP-%5vXmnuRLa1j>o?iXQyZb7VqgV*cn(Q+B<{`f)9X*6PkX-<+_JKL zOVai^3m?P7cQ4rw(O)2Zktr=Gbl6H+RI`6Ys|KCfp#hWmWq5a^*gfl8F^_v3$7Y!8 zGo#7y$g0UqnF(Ygs}zvEhmTD9$qzzv=cSp z^~mjrc^bmr&=%jBL2s);&v*<^Kz$tS$U2W$%q$H5Jz-VvqLjM^NYnfRYJVOT)u+b90gaNBXkj_z%tG86DDkghiN6Gu)@2>_N;FRnbA`GgLnR{=K6rYGQc3cJsQfs|~L+%WpYom+qu= z<;KqX*b1xF%kx7oT-VfU4TkZYH-C{iFZ$m& z5#A75-8fIvBDMQqd8Hc)ozio_zHVu+WvbD8NF3JN9`@?yHcPhpu_v7>VEkJ7;|=l~ zl@cpM`-R2h-juM6g18V8atWgUHXWPu_;7hAaGjF7c=PB+wXV_ z@M92hPU5zq!uQ_eU53>mu_Ymdqmx74d>J`{vR2R9t~ds-O#GGczRSEFd&HJkIYON znJ5=ifkRyrbm(x8ls4NuQx}e%vUgd|yDm5CB|G`2SYbyK5X-2gN~g0mhV9}a%^TPD zhLZ5Z5J_N9>?5h_Z;FOgMwIV09G<;=)D00Jd)tPiOv<3lin^2l_@KW{W^V&Nat5>FlmnP%X+&Mg*kjBwE0ml!EKGEI_FTy`h0g31A&O|RM>j@jTvKMc~yK&W49r{ zuEk|RQG9ZtS>ZmRv_2+6nz`BCE<}|Q0$HSJD8weL%UN}YkmwAo7z>q(iy~L~8AFo4DcK1ce%1rT@g3vAK>67D zfnu!&I7$>QhN!jG`~v8~IE?%Df2_I<*ml5MCo z!ZRN6%awBfe0p0*Hxu>xi2QSh4}=$QH|W)*bRMtN=EfQ5w|Q~Bc1Ib@Av4N4gjh!x z=Yc=L<`^5&tNJrQT3Im`aO;oEt1>Q9cK=@Xib*T~+fHlr4I+}$b}RFmfzv!WSus|V zk~3u8_d-m)vwxYoZS-kF!EY|{a{1(Kv#@L5=#~BSXKcu5WsAum1+hBxFncojWI%8d$I$7Ytt&8!0rf(eKh<3o&;) ztw}2M>o+@!be&9_q^W6YXnS6-OK&)%-NzlU#guWqp^r5$D$o6YCe24cy3QkKj7xYnPJ_|sm=miY;?}NH)8fCQf!?06;RN}{X`$KCB zyoWBtX?qYix$6`=xdXSBL-({~R{Qm54HcfJmz3!^uyxmdQ_Z9G0cYE>K@Z@ln6l>{ zZq-?zxU=mY(j8+9bLk#^LU+)40W9xfl3)ICv9LmdnG4c<199N=@w*{BCoX{? zNwGHCRn`phH$jK@1k7DdQQaeN^tKLgDfc_;xi^>h#58ond)p$DZE#6cBECB;uADB4 z*o=+8rC5vgb96ys?y5U1{4pKlPt47o5UCW=S0uR7&*<{(6t{YoXYFv|;YZ!o{^j$& z#n!?Fi?=(&|BD4k#GPW6VV(_CJ?Cy*{~SPkdZyqRS@$XeN;A^&*b22>1q z&&t^PGa6L_RiY^5Xt+&Jw|}M8k8Of)enRjxphy3r9lk4>{2Walokd(=PKMpt<}&7y zkzA_K^5SyJX+c}J*KG($a$tp|dPH5aASA2~xP4p*qPz+dZH;HGX0$y}fU65MvT@NW z)%9t==@3&6zn6?m845+`bkar}M4uJr>r#~zl7)pKL!x3Yb+@oJdG5eLmtV^g=x$rkByOKCcX?y)OHQCz5O7M10Crjl1s>Jc-66Yq?qt!dPG34?MK~v_byn)@7bd(s zGZasc4YXXzOCRNl9q)}DOoBs#l!ut=l4y8Gd^1XeuMccKzTZ_S#Ixo&(79$m2(AjQ zs2@K)UZF=kUbvCHDmr3mb2ZLbT*Iqf^XIP3?-ZV>7oKZ+INFR@MtO8c(Xj$jmBWZO zk50_hl@=`rb5*!A2Tmn&STqky4t&=W!tPZr%ol{=Vly_s^mb&OR%2u2qS%(LRP9zJ zSOiTG`#uM>$!WJES}}KY6I`t58ZnM+(io<35Iol2OD899Jp>>W30N(tB2lTYPwY3y zE9-?R=Np&O6rCuhlHi;Y&UP(@vtY_(Cl5w1QJ?&oj$(;%B~n|Y#EOR}l=%D3sF?DL zU&Y)&wLr*GS*KX!C4)=N_8vubw)Iy6A#w6F>ENIt>DlIxS24-o?CrvS^+|TaXiW;??WL}yYH-3Eo3Wkp-fX?Mfbs{0J<{LhVna7g0mrM04Egq)n5 zWR#S+)6Z||nKO&ADS)6!_t36nIvZ^2YY`iw*i&CfqxOUNo2^-bCbtXtguLk!Jrz-Z zdXmU>{A+Iu#DRa~rZHyg6cwtQYlKl}i^?6|EL88LZM zOfGgqd1!N@y_Oe9kmpWXgN0@{bRs>=5I|naBSS=ePQ+smzf!*&dRnU24y~H6B$}jt zd+XWhN_VIM^6DL1N~GJ1{es%eKJJfoxdr5hZY%HT^`rgZ#;8$2F@cloT9GO#g~Tkh2mb=b%PktUqtcUPnDPv3rHZ>bYA~Lzilx&l9F*x!uqTRi%)d@f(1`Ja&(4(2cA~=b z@_If6i0kEDs+l}`d}#fmr>db5AaebpXPEiBdwLYAK$F|0=wqQa%&ww}ynK4?$NC+` zt%6JZV^4H?W#R9q?os+^(OK_`F>Q=uNj<_>@Fnrzl?_R`0#PsNs3oT2mxs{_2rHgD z(k%Q4Ym>2NzbzPSITd}A+{dg4-U{;-ufaz&#!n-P1l?Ny6~pi#P;no{SzGF&r!SMdUuUni&~Onx(eZNdPe zh_#ekRf(63c;zdxVsn`D<- zpi6?l4Mc6}7Gr~x8v-IItjYkkGsmbe#7xiTHeD_X4J{H!^;)jkS;O?V%~q>tc23TT z2qF#!0wEb`oAu+vb4l4@`OJ3{6BAk0#mM?(h51Dm-L%}SF#PZSAthEWjJ_Z{q=Dv| zGTg8h7CjB#SuyabXBj5Lsln@mDda0kD`bJ=!)j>EG(q-s-8$xC3_tY$MAyEmuEe%A zl!VMToUrO-x-5W0)S;gTM{eBDw-{>E&LohY{p;phbg$5=53R-umd9jfwh4=M4;lKG z1t@2zQ4PnPn16K6O{^r;BJ+6K&H>!xV2GEd_NS=-& zCFIXCnJUxn*~kcn6gpTb7N!=rq?Uv$#N+~ZQ1p}*MxK>9P*r2KMGV&ERN-#CvNqsX zL|>&rs!Nbe4`bnrOp1tPi58(=Nli&i{atI8)YjfR81Bs|8|w^PcdDHgw=O?NN|oQ# zAhsaE6S8Kh6K|neO&I#m=~_-StEk8AC!38?gR(w#C;`{dJQKb0r!q$#dUS7zAtq@r z$`t>mki1HMmlH#PtrfDBijRqTU!YhPI-_ZvqCJ2p*(w4H^A^c3dTlG})ZxIB@l=`c zQ{p?ZvKbde%Dy~zv^@qTJCpBM04ywkTR)XHr1@yX9k3_4j^80)g(uVI? zAv4KXGttuGmzI`MP*(pwwkG?F>$f#OnaNK z)BpkuT7a!3GdQeeR9*-c2@)hlo0vz-u11!gMaPs^RH#*7vIp-2x5lPvwi250f$Gcs zTdD=6l?ysrNdPDerWWRc5ft2Z$J_lyVFW$~*Ssl1S(HqEDHRy2K0dN$h z017i!YygUJ*!V2!zXl&O1<)^Y3m9FYcvq4(jWGr*PA%Aju`njk0!P zUyclf>BKM=i0wR>f}BJWEvjMdB<|lEZT{}ON#=0i^$BDq5go#^GT5geX?H(AvOJWV zMV80?%}WGPUIv`Y^E^eB1q}0V7#;d~%2A~3Ly8}!YN$N0Shs`IV+3UCnSzqim7^Z& z@%g%`kyD!3W-eu6rzF|kz&eWqePCEMWd8nT6P1>m$`o7`PAUXWia(%fSK^sA;Wej` zh%B;)I+mx(46rUyq0V0p2*A3btI!P=BUj)%hrvVWr9cyoiQlVyUT30l?21b>zWbx- zgv)jDy{||T_n{`oyLLFW7T;za$E^u375GYTG!g;=-czBT4(hO;-IxsxrHPpy^}3k^?uS>9X(ex?_pzf zKr+Ajo5moxdg^pFJ={uy!!$m;Npph0LaXLBWgO8^h0PhIW_*;;v~USzj%Nn8)(Vfh z?nGV)6Au!LZSouR8w~+0H=%FKjt5MRMl%aouSz+MquS)}T~Ko&y7S}dOc#gnCyzu; zZv)q5VTYVC8gnZC_9a3!pN99d-VF)->p!G=OcRy=($F*A1K7xu>!uB^CgvJJwc*l7 zf;Ln}WH4Sp1CtNZEPV2Kce7hUY?1j+&!RJf?+O4mYLD6wp(n>_1HWn^0Of?_cCmMy zHyqwkC^`UD*dV4EiJq70w+$HuS&#_4ic zPwj~XrsfH@r^mPl>r{1#dO5E8?H8XgHo1;$<~Hx)WFds#yvl-!IavLkC><9}*spYsz%6yKB*M%CcFt2eWkESjX^SsY%>%t6M0ne2OK(oZ zhLZ$lZCB_3UUCfFSFP%AC8=%IPb9cZ zk{)d2um~RW4{$l-NU6KEp$4j*qJ#?Er%k_J8WVXWbitQTzMC?(u93%N*xNUA^ zK5gRj8GbsT-Ll(YYuOo(Q zs`xxVzE&vP4&#kU%8 zT{of~O^|lMR}h_#&Mz^I>l~Kh@vYeb+M1wdUI61)S5@i@2P5R~1T`!BU`BXsV`|>* z!Zu=$-kV6#BGr9?I&gS%LNy-^h^G+INqwG?rc3G79*G-jHlV&hDe}F(sQybwZ@omK zqPO!|N6*U{znq#dpzK@J*Qq9A`iXrXZey0CyIU5W_d1vFCK>o{DiSxysKxre%h zMC0CXx&@w{BI1zN+6YbzCI(gDSom<0AwGW_)}FWJis|vlsC*F7nIDhbKIHURf~c2g z>9rTHF;HW*oIBB}>!1=10b#uXkuUv;7PT&e}<8vd*{Na9>!Zm3MV} zoF4yR#q)cM#rUwSNc1NdqwWcz~j6F-yR=PYs-P7#G9c-Bp5Q~!L_m@$-*`bu77U$Re z5f7)VDM}6Va&e+`6w^{?Et!h;lwa_KFs zI;MY%CKv1~PA80@JEC3n7u>ErSG>>Si(w_ho(L^W3F|CgI@Y83d_NtiiGvzpAE(Zd zK7F>mcmbnh`Q1fx*QL?%JmnGH)1%wQgYo--545YC$&vUEe{e+T*w-uqDuJDBHB6?FEdr*Tr z;IKgjY%$KkqYA-#lBbL5B{Gfe>ISYaqDdb=e8mw+D2rk2RY=pjqoeh)smgtbjofv zsJ;q#HJ#slK4@*24BgJ`UzX%MVE-tMC_s)>Bs?}J#?+S}cPQFLRg%86@c8F$vUulW z_ygO9FpJSq2&4?;+s2HJ* zWxJTT&LWp-#o#|A2Du+|+>qX3#E4+@k0x#n9zNVcWt{j6vFG3y?@w;2e7+%5WVevw z*H+5AU10$xAo^EmlZ^krO@eD+N8eb@QRmU`Qoll|d2;{CIY*_N%|8mja?mPt{)BH& zxI6dZwIk=_73$8WvNjsGdmX1?q738y9!o3Eo!!P0LAf2V?HaLtNBwZ*^TRGl0x~>1 zg!@48$CbLwoaSgyg@wc-EwmZ7rRj`L0MCoVk{~u4sh2~KWPEwSzpZ@h zSGO>?TZ7f2z}fC33V^vjn8xjcJ_MgB&^~Jr@bIKCf@mX|EJ#IKKLTE*21%?GcNho? zh9$KXDtr=taAWk>Y>^L=zBvOy#G)ZD+s>xb$ZH!nwh}{+<~%5t?$&A|FZ*!d7Gp@p zo{KKO{(jrMuOVD|M~g(AydoS#gXUYBhpBezNcz&p;{uy51D)y+=^6A+`>C9((L1kI ztLGL`db^R2|IXjvrix9m?l32`oD54_a7xgzNF%~~wkd^s@{f3_N|r_Es~uxu>kv@;*{LXy$K22- zZfEk%SYy?68T_c(sCt5n+%+X7Vc=}f|9a9h#ORg2OJdP2vfvzCy4A0EJ^=HDBixp zfb1yLolCgaQK4AJbeD#3N3teqCxm6YwRm6uiDg;*%W0J(9WwXB^egMxGkmM3C{6-4 zPiHVn%2LpY6%PLu%z8q16gG*WKW$Oqc)BlOCPzF^gP(-Cxg7PdXwi4^H3~(c3HFU_ zs*j9i!jB|%2GwG<0#Q{W_e&SP>kUQsnZ8F#;XxW1PwnkMuW%z^hxGA5*0Q+6lw&q;fvMk>e zM5f#=O$8Wbtq)H;r8%I(BF%iD-E}7_RM3W+Co;X5tJiZnt8ltyn_zX+LX|HT*>uD< z5Px|#8VaY#=0M}JB^>I0;+Bx6I?ZS|hi3sc@9F`B^dMb`gSz{;I8Jeau^@srQp^I^ zRxq(zhcVo#bv^yw_4@5qu}CS#VJ^AzzB}K|9oN-)v&!*ClzUJS2Qfn9i5TwDAWuTK z>M3n`o&+g0DPNxZRcbttcD#+_CH|?~S$KzI!&nx0fWoe>KZRxr5TylR`%Rx-CDl95 zFXL>y^1S7$y}{xAEwjqX*n5X8h-f1pLDQjn@ONY%u!tP*oWDOG5}UHw(>dCnoIz(4 zSL@6zKX<;(=)Aff3OEk@K~pi$4VHp~>|va!u@V58U;}2q*K~-8EE>ysZx;gSzbY;rpNh{MYl~Zxv1n6apfY@M zXLknPcLoqs@>1$_@bq1{cd0{TP%;2`L-p?5QVOV+zGohAk^Wt3u;E$56Z^8d~ ze2}3qppIV;=>we58+&7m6oXg< zbtXmp0d1B_s9rb0X{$EqL%3()XU}yZ+@dw0ed@zPxuDV4TMo3w4IDi^ltSba4j#OY zPO|mb?hl*4CX@$6IdTvF*5HLa^&FZCgO0!@#TeA97X$aIu|C#+dLKOSK4}?7P@^77 zsIx1X3LL_SehvU2-82(w*9flq|96cx`4@qOPf3^(v{zt7m7km(i$P!7`=OU0u!F|& z;anwT1}~}%oudxMQ_SF&?O?@WpNp{-qJ!3nV8{!eh5#10AI@$$|dt*$?VP7|R5&_Go@YuCUj$5H@qhHpg8jSOvW;NOf;`0(&{Y+{mrGVR^AbNu0| zAGGVhM;9tGFP@?n9s<3hWcp`V`pLV%F7X1H!;a%_az4J$O6cFghcq3u=gYG*yCdlD zP@UoM@$pMd_iy5SB5-+dxAS{_UVWzke!9>b`fx(W72$usgbi-}(+GQ4Zzk8D@EL+` zeHNGj)9L-=iNXfH4K?T0#Q5)VV8nDs8-eo>!V+ep)cwF_o{@T+t$jT-US^V z7ZyTixc<}OtEHuKm-%ZHmG2`+dy}u!n%rI@y+V$8dRlL|udHkMPd^|+1rjSFA3ngC znwd2nScrQFe7_6lS9Tjf%n*bc|UD=zajS zPTT0QKy>RxC9>W>_GK6Z$ik>kTUmU;`a=Z=eG>=H(?s`dd0$~ zw&=k?a4az@w>DO~{B~L7B^d>y7{^9o;Qu_|83<>964luZ`V2SKyHo!U6z>F7700{j zR*w4UOGGs_lXo0;Guql#(9Nf-KTF9*jniW^TuhsW9PAxVuZ~xvqN7(UZmQJFl4&(>SH)F# zc37Ns1=H*Bc)f15(ELSSo_BW*&dRH6`&7nIh7!gVjq8=_{C8!_B{tAo0wVpv&~9nv zhXqoQ&Hx)WGBvfe5N4@KgpU4Qt-_d$jcqin;mm8>N5mxu8^IvrPe$W+$F>pu z{LY!SxzL;c#R8ls#iX-S07$j^>r?z4uL*A0rx66>1V(VZNC>b(0mB&ufkCSt6F3=M z>LE`2zP&t**dF}_Cd$;N#kUNfnHe7U$5Co_4x+BSD3Pe-gl9?em1;;iY2?^Wdo$0` zh7H8Sgv{+@uo42CYyGJ^fTc_aAI&DR zfqrJJo5=HyePxu>K7s!N#}$-JYgJpW41EdfAn)v7&ZmQ13TkTG|ITfrD1Vax3=bq) z9(@uA3ri26CsBR2KOKma;cEk&W?=BU1-1G~+o$Q4dVrSFIOz&SUI#SFX1i%iy}f}_z5ofusw4agEl76~Ncnw`?b^0#dotFhU2R8m&{ zfbNdeOB_hNo$uP&;rYKYO#~Wwai50h8@P^nyIu&@Cyk+@p)Nqsb*3ibKS~Mx_O|P<%mNR=Dj9Ns+d^MOiT5Rf~6siqZZRZ9uPwK!g=P zpY=(KTnJQv z<%tsWn_i*%`uRCdb2tI#-}3ucr==!`0_+@I^cHW3-pNau{dp?sfE&9)lgLXv!$%O! z>?L&P>m9B(lB_g$oJ`t|1Rt;E8X5z@@>_d2HGxidL{`(E!Em z(@Oq-WmyzZp_wEK6hL&ns|{nuxKo#r4i9`Le+Op zFpq?xvzinp4CF#%Q@*9Vpar_#9(7>I)y7B*OCsU3OQLy%2iIm|V`2);39+%U5vdna zUWF#Jnrn0vu`@6XET4zZDO7P%R8=`#x$HVhmg1pxLdCntBkMmmV)<8B2TS*rdTd&* zn`gn*Zzqid%!`R5{#jRwz=`^Q_eMX0s?T}-GuD6(yJt(s-6T$lB5491EM^mhj*}r( zAJL~n#!Iua;a0@y;ySmswvb^|))=xhO>{&Fd2H#&>sk0>RJ;LCo;L1Zk7P6?oSc(q z#wfKK5oKdzb#mSfEV%v|OmJ!2zkLAHe{Z3I1VH8bjPyAOr?otVdi&cdf7j@kVXf|IDm45@+hKfo_ZFpRq3XKTb2d=NWg1#?hU=c1uezuKF)F<=L zHJ@@RDSx4Vr2O;kCGu85Z6egf?*TX7p};MWpg1uztc>+UR31=5#Zqs3EY|!0u&K*(;4J5 z)1;=|o12UMkYRyaBtU*>8r>Bm1da$R;8e%y=i$gVBRXwT6NFA_nr)iwj_z*SO$6Cb zw(siyz0g4r(U=VjlZ6U7WD}u<*kYHPMA)D0jv_8E>lNEaP5$q@>wtH^!Qvu#N>u25 zKy24v?~7$xM%`WfqN3%uvt!x_PK-VsUbmAxc*n$GuFnk3N$%5waQG+xSL#lsrI#it znji{cPv^t)fS9c`V67J1DLW@I-dC|@ruTJK$)Zem@j(Qz;1v~319#0Lh`;shjANjo zEkCsO{mE8a)}nq3c2260q86_g0wb!+?HA| z3sbe_R*(y?#C-nr2QZ{Z{g1w#)_~HCBIQqMd)| z&GkLz#fq7}$_WKA)S?V;Ncf%s>b}6Ye~WD+oPj^_5ibq|;n7;FA&{6rfhM*nT$0V` zvU0`$nV;5vAOoO}aef0Gombx8@WN>RLxDo&uEUZ^_GWA1^=Xy*EyfgD zDANbSwcKJH{f$wMvUj!TDOMM}NL3J&C`?duz+lRh8zz?4CDI9>F9<(1{_ok^L0Zec zH)g8~johR_jQV$NOg$ak8t_Td$HV+U=3{Q&yA#i-;eWn6$!pK%&>_5254iSGsi_A} zO6aQM-z%m7=VAR_fm1Gd=$hAUy?qT!WmrjdGw~~wjb&Z!410Z#h_SUGiNU}2d;Moo-5~mW`afoSyZomEY&IYZ zYZy(Im<;aWwr0WU%vtZd*MA|yj~>Rx!_$|=GhQpz55|~4(10roGfBY7>sXiS*xFiL z!|WV#3XbU#Wo3@qivQV>?t{)8JD4jqfi|hQ?n0wWPk+Ci*;r~|duS*URbo>?ae<`+ zywGR63P_yZV+Nnn7qw9vU$_@!6clef+%A24$L_zenzxloxYGF>q1ZbGdbVf-fXxP0 zdgH&j;{CM_IK%8;fB6eP0f_QD${NEG8b`Bi2c8EYH+_$S8(o6)8h$yC9^6_jhDjYcT z)O4@z5}eT?&D!P{{uckBhjxM}Gm|2Rb&)S6B`GDPD4JQq(B7O%G4WqswWI@H^-V zzt7S9J2fp#g_1uz~7iXtm>r8&{llh^VN zl-;RHhObd1#Kq}cjkC?j(?fZGu>A&i7TRdZYzu9JC}QHow<)SjA}hGNGyH=@lYJAZ zL`CgMxn8aM?_GC5B~dAj*6gVqFpm*g5iCY!M8iO*tSCDrnyZ?k2tgmAppc!Entc8N zcZONm<{C@oGg^?vi)Kw6!1>G07Nw-3CIuRy^abSyID$YM4qO*T+PjTK=xu8IQx(Mx z7Gj)PS*eNkW5s`J%YPIZA=NXo-QL}nD?BtMj#laq_fLqhDAu{uso@_Jx{4l_QM ze!{@bOkYwSb!Bx`y)fwSUxf$tYI9)r4ZTJ;C?0trC5%J&Dc;!dlAeD65wGA@_ZuEQ zfyQTiNp%EnE*gCn4sKS`p?F}fR82>Kk1wyN3^r&$1n2Js#rHAbLzyd@EgKjiA}map zNkO4iknlgdudh&dzKvX8qNfP!0ME|JNe5#niqt35l>M$0s|+a^H)iYw5K4c1r3j4b z%&V!q!&K45Xry=0HRQ=PEYS0n;WF>yF-e9`gHrW2w4mc*gb`XYeZfoc)VPbNd9kPZ zkoMwQB`Md1i%xz%b|op>)Sr;T+W*7eTL#quH0^>wP6%$n3GNO-g4@9f7Th^NffWlY+8s z7>Jp%Z_+#oFxV)FV+`=Q2`)WS$DE_&4W$4+5rRBL!SDja!SXMkJI zhyHB>O81E~ZWICeE3~!H|1%VGaO5V| zvRwMr2_56bgO(gu#R>G!^o-1s7IbM$O7IkWn+bt4MWtqlnar1ypJre8$EOE=Mi=3~wS;q=a$vwM!k7T!dm5XvBD+eov|`)Yb6rCzdSTwiJ9LEnP_{VF$#$ z%-YhGzI{=q(NS9S>U!{D20$j^p`e(?)W2iG;c3O>FLDJg2h4T#>9I5eHI{1q$7}Iy z?|T#RQVBckJNF&I-2q+>wu7UUjg(9%2?5?%K^2}>k`|6_qaQAe32B@G-+cSsZt!o@ zB%mOHAVJTCW0`3VHuknV62I+xGhDV}31$1+Q=RV!dFJLHBVym99xLX=j+tKT5b~j{ zZxWf^jx5C|BnN@S;a7F~KWd)>KI zYkW}>3!h4~DoChbxeVii#kC3uXchJ0dPh{T<+Yg&3Bp05%LCWuWVsa9vLn zn*(#@t6W+a>}*9|KrfRZ~u+DX&g$ zWo0F_+RQdCI#wz#LfQ!$6QeHH-A^J*_#Z!u6(~y?1pc*JpoDUa3~m4oWP@I71Y?#c zkvY!@tA1Nv7F!FkRZa`Qu!bm~zECh$y+)x$s(kggm=~INHP}9v5+9G#-6u=S&ri4- z#Ii-$)z!r-F6$l-7mP2QDpbcp4}LiSL+w|3qoo|PO85;jSXAWddBH3+@p=7CqQ&AW z&7f7}tR9}%W0{$YKMq;klOrFmN*JBBD-}mrC~%`rekMFN5uzF`(tRq!1QJ>_{QPMg zMPlhB+Ao2Z#s@Ztemd?2uw@c1kZwL@CmoF-Q%$$sGD7w;8+Iu!EEudt%*x?;oNGj< zS~1UB_PgI%*42%qo8MUaRpqqbrK5jSW>YjxPc33<%DJ&jn?VD^&4x!xBm(ImQX1n& zEan@_Ysh(z!uBGa+9B^k#+aD?<-1~;`0r*lqSgC8a5o}lqZU6ic z=B2>Hv#VvHQ20ch+*84Y9CW430&e34wXrpvhvqZbB=2+6SRatDqqyD9+R3o!uf%5~dfG zefbjKEH6JPyK;Oyo~H-Hp(_alIXR3#F5YSE7fHatvm(WWW9RD|O2swAxw%it4^R`6 z|1!hHx#UKUJVtBXbSU7Ood39800oH##cvn}?|RCD<|1??Gf;X`iF>-J!V=Wnct-b_ zP$4wnicBKIE%p-xDIlOBWDq^wcOzvx=f-p|`I`KH|n|gSG80`uQi<(SS#9c_Qgi5Ed?9}AkN1b>pDDXa2d&%RSBru_RgTIO2>WLHF@;@$Ox?aL ztl|AEk!@BLaW{+9*i9z^i30BSIfOVTNE?ot3<@vxN^eoX%zXR z`F>qHpOjzZzna3~Wyj#<23%u4aQp$=^e8Pr8p5Ze!z%cQz&59G5SGc0Tht=04)Ol6 zuuTA@;um(#({_RAAkC)j=Ab8Hru;F^h=#D|JoDe#GYR;wiwG5_prAsC0I~AwSH#>e zT&0{JG~4~;nug*@5?JHvgQMHq@=d|sGU7*I@i1Bhr+-Emut!aaM6HJD>r9VVF{!K4 z=fvR6%R0ry$=bQ%uR}@xiJ?IW>`5cD7EnjgVGF+si$q{!X4eSf)6|)k7+DiwDL8s7 zbH}XRAz(X(EXW9olovrTb{0l)xQ~Sdx(DbC6PB8+UwPQv+{l|~)RSoQO9SmcR!^>~ zP?Co(n_mCt;BW<($nnMCinOB;LRZt=5TwXO{C<{ET=;1qx4Idk-H*o%Wv>9xhuA(P zEA~sEs)2%svShXEFoJ0fgLpFBsMrggO14M=%@O@5wdkZt67}I~tD48(kK+SUfIV!5 z^v?lTUmtZQ+%AQ4PFa~exU)aS)=L2u8yjk9S7L^0(dk)62QQ61W3;c^P(_M~iD7Tl z-!OD9OqTf_Zk|uTiBYT@ovYL)&KPN*5P%F;&*tr`8G5`iAw(zZ4?LWTnq#z4tcnrf9-?^%m{>0@K7nt~#kET|Dhftx;gx>tGlmxhWGd1EF(7k|Ss zG(uMHyZw59KtpD4pTgO;ZjV8Ty4pRT67WAK0aSPqh0>HXQW_nVfA^ehN#esoBde7~ zGZ;G|%MOjMe1{bTdr7?etQaeyFIp zI%>8w76wulxPf<_a|_k|7x(ak%jEO#*l|=Qj-p?DOhgH~%x2@2THMbkLP8pJ>AL%v z#q;xMVq(MAXb~6S`Q3$&m0ffQ8^hsRklDOpE!Lnh^<@MFWJ<j z(BQNFh;#!UfFDE?H`Gkw<;w^;DX9!+l>|zzN1o$vxBqASxSb(kWR&UrCjZrmGC3;k z;xbi8gEB->jkuVe3zgfQCI4l6A3$ST?)X~fX*%Um{X8VNpUc)ipP;%@ncn8pKiM5(T{rck-~B@-WJrXI z>p&2QS7m@3^b%=!LmFuTl$TTp7ga0csf7pJ`LVOG%UD|rDh{y<>prEAP*!xNnb6Si zyf7wTFQJzJo;q^Bv`gyl(|8`7)zv(@t5UA~-P-iPHlGba4RDG59OTTM9sei%F{@D8KKm-c9M!v$7BvKftgRq+Uy3sMo zK!xhNkpZ>V6I{oR4_R_BC=vW*hRf;oY1N#-)>K|Ces;ms4=Bg~Ec=MTi$A@Sq5lGs zHrmdAXBA&biA>1Q70}W0@=E8oTGq?w+=79-Q~K1EIQ_HIQfec7GtFU<=bYSPSg0o^ zh9%D0@nBITC?No}zS8ZMx%H=h;?jvYiklkuqH&~b^MaoP#P&Usn?A3J*<*uxm*@CSpMDYO>;6&ebnwr^CLDftxuDHB>v|keJtir`3iusA;YedBICrJ)U z;0rQwn7h)0OdZ&op#iNDA5jW|kuRX0RzAXR{IU89|X+bc0`i%d->$1Xt{mGIrH>Y(!T!@2uJsvFY_7?Dz zFaaqeTtd`sQ2mvAP{_sggv#X4yM@*#p*>&F5C+62g+akHGs+_u$+weq%iysL^hMyY zGpJl}hpcyf$$U5m5=KEBNNRaKr^3s} zM_uf}fP67TLyR{@4qqo_y=6DuyDJ&|sTce#Q{UCQ!l^_u9P$I}@dV89)Z!$-jra*( zO4>`ESLa3G5J0g*`taPgYqVWLB;&^NT&lffF%UB(fBL`rGIi6H&b6!h@#6>F@i?M> z^eI)WA{jwW2}um3;I^NZlzfCBWk~>y=YObzu5jIKuP6?(0WdmrFqYO~O@xc;S+nn; zJSmBaXXh(3F}JH4!mH(Yu$_E?Ssx?P)MEr@0%yjbO9%A>vx0C$;ucQz*u2EO+h~m14T;2 z&x`y!PN@+5;P)qeB99F@>+hS$KwKRW71hHnhY7W+=)N)gm&$QN0db|`E1Fn}@2&*9 zYOmRW67wS||M;PMd#|eK8}c-ctZN3vkhBS2_7`Q1R-!UBIc2sVM@E3Y^1d$yAn$vc zA}(9OCj+k+-HP}Pyj_t%Kt5v%c6IbC(j!($aRerea~>VUpFd0IJfx%sE)PBhet&fH zc(_4O4eY!JaCDzh+SW-U1uEv`<{}oiKcJ#uX_tiM=28bn2CYq@0y$>B)XZ_!kP>-V z@(^cAkn7PWChV=j4}5<4j1l!J5v(Y4o!u>gEB!g6Tb&G2pq%ObsDZjVtXI9OIw36s z-xEQ=%n0rx{Bk8}uTDyOQ!-aWz^s6(*g^6y2Lk*6G^mN%>r62H3rOAJ9VWQGCY}^W z_w#;@hl6UB7O)b!0&WMX3VaFuaN^f1=)b&Fqbrj8Ork(QGKP}meV+a+<{pVHH# z($YvNe~sAuVo=jEmxQTv=h4t6BqZm#pO=ivK>#N4ShI&IBPJ&Yhpd-ZV;oAgel4oJ z*yco)b9Zky6`!E`{;pnj97+g)DE3jdvMP$xAA3^^5UZ=4-hqm;S-Mfu zh<|PjOibbkShb1mWCfq$jWGlQRCJ2teEnZoNtj>akWBG&&4;t~?V1%2A%pw^M>BP+;eCqUrNbzQ3Xi{?zKSf$}x_b#(J` zF+bh~9rswU%c(dyvCnI3iC;K8`|ttjvSbPlpZXG}t!+XjAYcA-;9X+C8`O{Z1i65) zs4E;BTL3#W04K}|6_r@jJNgX~m9Q+5H$%l{vm-TObw zhwYl}|DClXgo2{#Z1jtc25^*u1zjt1jD0B#zAkl<1a-}P16jn0z{GHG5#Wa*XX;`p zX@H*rBCJ}f-z4x`&d=bUb9J+W7r6au8b5Q5EtxQWnGdAFVMXu(z^bp1JUDLQzj0qV zG6N44NnJ7F|CtH0DFfyD%r!X~WMPQ`LhJa>v5KhlM?dkwD3q1My)(fR!Z>EiUl3SquA?N83%sLWOMb{A9*hp6Kz@0aF;J)?z2m~L8<`xt{TLC52x4sX23IAH1 zFjEB{fQub@*i7c1IQu8?JJ8?}KF}Rwa@Pbt7`iq0|IP%hxz{zotrT#j;(^mTO*x14 z42Tao6R>0itFVQCVMeMUo-ix=`gmZ3I2ojRLUUn6FP;=IgGOm-Sm#2}|B3|Rt@;ar z(*f3u8bsM;;(+83h#ymE`oEXYiu-eqng;p@z=a7$iw5oDaKL&AUsXOn@?E#EPBqcw zlJ}s5^m9+VDk7>Yya)!Uh@roo@LRZB=32%Kx~qJaT!1oWCLfhsKu9NSuKRDX!;I#i zr51ojHa0?&e=tIG0cpTfP;4z9MV*a=w+-iB0~OMal*+-ZZtnQcT1`1F$Pdh3{k9)-(OH3 zwTph~2q0;2yB79rslO1HRwv%oBz7)GO>LE(eq{gC3k{k-{CPnDjL^{paVVeBeQr3l z&NsR+nOyqhwSPDLr&)j4HosaX@7{Cm7Dd<&OjMPpS%J~3$a)?d&7qWEyg;61qdBw;Fc z`4lijW<4ebdSx@b*-%N1YeVrreBk+etPUvhhCbIifgQ!aNgeOKazYV|_RRN|WWJvJ z>9n{DdvBd!0nS7X#lx;Bs(+a{r3+SDl;z7=cjhm^c42(BG-Ne*^D7@$jB7o75}JF7 z!KVkMLg}(k!R$kkrEMQ(`(( zd$c;9%)tcn^L_HI&-00St`Y4Tjxe7I?HXY;`tuDRZgxJWC9HF2IPfInh!ELE6hRE0?;IS>gspSAHjo@r{g$ zSojlTy@1hrN-6W+abl5MRnNa8)o)!r>77mdkuKX$aFh4NLMBq%w#Cn#=WpYL-|D5c z{;J06ITR1RH8Bd<5I_8$+w>NKEr6U@U=%2PkQU19RLUuTaQ5qL@MZTx3-Oy<#!gqP z0s^*9nkSsb;n~f|U6-moK5;MQvTs$THrwmfiEW(ac3L!K3B$_D{rwKPFK$0v-bTA$ zq^jsRjES+4z939-BoEJkW-muU(4c@geJF)f4y4)`HZwxNZOIS!-$sX!(tSQ8ZOTL5 z+26K?+0Y*x24Bp<1DY(#I3FUe&bJCeBkdy3mRcJh%On<>d5I&zXsH%!>F$bbm>U|9LfG}( z+_ad>wQ`A_(@tr^X{F?CiD3nLcwt7!>FSS}zZ(w~bw$Vx&K$|1?U6g>hmuF16P@0? z;rjOOjxu?l<#F#lIlA^e`PT16uOPJJwTxnxv$eYfI@>0W1a=-#U+Gie$8 zn*oJwA3t64dqHYU+|=~ft);K1Y1GOy&ieLaY9!HGT3hu|Z-;QD=^AaRkHw{^qEX$; zs9b`$T)+-`L7H2V=s_B@&v($mY3-Vx%Vh^R-H_Q4X)%_To=C?%O6CZCZC3R>ojT_6 zjPt^8;?pJN4wLB)=cP&%>)9Z$E|rdqpl2DCx8`gr*0p(`2F1IS^@vtO-^m%{P^8bQ z6Dtbi{_a3WS985ap{G)oryavb3uzI45?q!Z?q}}!J@}NeQ?%8fG#FN#)%m6RZ)_-^ zd3==(JytYIWh6V%?ZQbX!*pL<*UW3B%3a+*0?Iwvt7Vl$D<+LG67nX?`>!ir)<=%U z_h#xo-_SHXGv}#r6=o~cFZ~?DX&n96<6UfC`lk8$nx(lbdYI7Uk9PFLt(G86M$u5M z?4^n@FHbU?l6AR9&$#vxYhy=Xo;Z&}mQS0e(G4Au4jjsMcH;fbdiCR@F= z_ziC;3;_j-xyLtn8?3TuT~jyhdD8Z|2f&69+c;@!ZYP$7!Yp@KZVbca+*<>LV~?!8 zP~sxm-A#gDJdiO`Tw0cht$FQTZwt{^bgUGPLVH`6I_@TsbsZ=sof~&zSmT}U&G2V{ z?DnOJ7jG~lvodBl6u-7$jF5XFIk1w(y(ddq8!{#j{*@}$@EB^@9ztHdX+E0HqsD(5 z0as6>7@miM@$Ji9ZTg_yERQ!e=sBj|TZ-LAx75Sf<$)Uq36TO>>3$*(=*@U>{?1QE zu&e5q3CwzaZ7ttm1_vm!!brwsyPIZo(4Yhq#+h2VO0a&>f`!Ey6%fTv87)}u9tIR# z693893jYvpxtuE3E-^mtvG#)i+x*Svx&`?!z$+0V2osvM#aAx^ip74wchHt-bFL~1 z7>+G_hu-bz6JrUZ{tUBpmDf>!Dvs*!yQ!byEA7LKy%H|j5ouo$?U=pTp?POAyz50k zxGoZ^Wnxfj!d?cYeTaNr{NPZ^XhkU|xFcYAHdHBKK8LH6M+=+L6K>vIt--BdD|RvI z%aVzuoeSml3lH~s`5mgRvzD-A(xLg{cHx(REk$}{8+J+9^%rpZCe1nkDYifH_`{k(GH5^>+%%NhIX3h}C%pd8D{%B%*T1Mk=~B56)Vch$IW(nr35_}6 zra}8LY7$%N;UD5BI`X~NgrnQ#QLT?k>ApMgO*CLd`K_`j^(~B6cbT$LJ~51kO8R$+ zs<5ISJ@s7GCc-d|Fb{2tPVCh{H}Q_=dH0ka8rNpSitP||6>s9=wx(No-X>Z^8ZnH= z+V(*kGdQVr;xRvE7oy5J{B$ZenRAE9QM*QFuaDKEKV2d4&!3U7J*2IAZU(uVjTfOd z_^M)+G3kXEl#%Xfw>Og(`kBn>Der@d^m2`Z??D_clCo4zcbfDAEw|A-ir+fdii=rX z0USl&%zljAe6R5DEyD%Nv_ikv3)p;3eY`F1k9!wt5QLwr_I;sj51*Em{KK~)!>s7LXu-QhH-UqK`bydz-j~J~6k5Rr}+1izA01QgN6?*WIN3|W@By-T6rJDZV#XhWvX*UpGQ|F^e$ba z6=yL!)o#468l%P^%Hq_x?tn+|x4OYUr?lLy-+x6v!)Z%uPhj&@ZGwy?FyA&5evzD9!39;1Dvs<9hKzw+igO0hRZU#Z;Gw4k{(653r2PP zYgPKZbqQi0ea5vX<99GxX`j z+5|+?**-BGaK{-S+iG5rVD59{YGtn^ucIHeK)OiWghoilZpO>bt)t}1+uI}jF@L$^ zkOG4Ek<@mMmGyNq2Bzpam;(t;2ep-4eZJ^1zvFHxnF8P>V7sL<$A|TKHiTa$=%BU|pQk zA&cr7qUg<#+cLl2gfbnt;ZoeKXw5$zf?OJl&ybc#q;$snL?4EL&5#=LE$*%T$;*~; zt&f@mh|RuaY)|Lp>ZWG%0TLvzT0dK5y)?#pgk#(nEk*oHCT5L6C|XOR{#6{%WwKyq z{)!J+LMgdEFaUg^w2X{T5)u;L+3F}^?kEu3ye9*%un*w*wiGT`O9wA@R4X`Pc=8)) zPa6obQ4+NhB;c==K>#X7LAMOQSb`UHy|xR4Ab_~I4C%jvWB3pLmoeWM{3S5xik$A> zyJS#ss%vQ%3=fW*kl_*XzmOIlGXC#`{+Z>7S={TVl1OdfT0tJReXG?AipVT6(qo!X zz?Fnh^|O@Z|HQ}1Q?$2chX2OiYqap}6a3?%FTd3)Ie~yWEwt#y;JG?5u4n&`$0Y%; zj$_8Sc8w1Z2T}Md;v4hKN=%UG;N2z#`9gOy*7$77*yw0^N7jj+dr7bB{vX2<*j-so z=RbJi&Ar>J8m2u*$FBJP^W9+M|M-s7HI^Bp_0x>dM-gAFu+dW4qt%Uma>{+>*J6l4h|Jq4O_>2yxXfH_*uO5#qz}IK7)ct^O@fba$8J<4?2Y;4o ziS}_X|`|ADsr%pXn zlJa`Bcu!(cUT9p`H#iPAUiE!PUY<$hc9Gx`Q)<8$7ccaO6-6v+UBZSx_EoNw5LH+Y zxb=(eQMV&B$G~me8vS8-tE?^9Q89hRkKvM_D5p5!^DTEWKL*c65tUR^faLiN3cl3m z9S##!I*wQ}dAp;T#bz|}u-`m-{-*tXkkS_L+76<=*R_sM5Zz6^(=zQkb4smXd#3wW zle15N5;y;m!(VLma1)%mTrpg8jd z4*=kIX-Nrlbg*ea9WiNjUe zMrqC<9)?LY+Lz_g^0eI;abo<(zv-~+i0|{w`s%G2uMMRAlc~i*fDh)9?YyA?92Vl| z&<1|k!RW`G4l)79<#XM%Q+#bdKb~F+cs?YpEG>klQ$shT$y$NB+e=!&eaJDGkVlXH-BYDj8p=+#`Y|+@uSfA#% zr8cbDw=|q7%-`>tmCPqarF}YudHIDo>&gEl942!|+WeRmOX5ScvY&e4)}T6hqamZI?Tmt?^3~qUDcY;c!=t-MqmK7PD<^wa&#+?T%F`*k?t{C( z{cpvZIj=0Az=$;?+;mjC^bQ?@1o_FKE-Ib5PLyT= z3zGko?Zf4?@lW#Gn5uT*<5}prdSS!Wk}+!Vv{hFB=S4H-C8?@*$f*xtY~`8Q0KU$F zs=>}ZWA4^kJA%?hr?ae&`eYKTb$_!9*LIWF%!>;`5rGHl`Jc_dgox{QnGJ>h9O~-w z6ws%}v~r2u&G&5kPs=e091x1!9*8>`Q5NT*TbFdy$#HZw#`y>O;bCk>aM}4Do1L|v zy5KMHQWjH+ljR$nnUL>pE`<)b7=I5-%1?lUWg!`$V#dtfveSv#*qYl!tpfRk>* zYF~b2QLZkXi?wLqFx^m3XxIu8=UT6Xnqxc1?osF5&rPHCaGu!RAST;QhvZsM8)opY zy8F5v{F$k?s_V)KqgGy8S)tH4Qk(qcxqeB+RjW+VwHc9;x#)Pa%6}&6&vCJEN0L@P z_qvrruJen#(6Hyf^rh~Pz(M327*2&58Qa=iD$4%Bi_#WssA|3V=(gC9L}M^~|0UKQ zl(%kT2~WHE@4c9}716^TIfNS|*EU5V97~D8tcrvy$;HU^bPb$NeQpQ{ly(Zz zlA4ZXUY$Gl1wS5oyDKkOn+?RFr)O?ce+uV=A5X{Zn8oADKNBYq+|foaC{9pQ&Yq8q z-r>w!F1IypJk-6UMxG&uo6#&bFaLc$&QRo9pNsp-|EpPH^ogOta65A70(PBX<&FXk zx)tr}1(fghr~B2;X}r_n3$D>Q^dWlYeU@!aV)vAuqtD@$yT#20BRE22rNv}c=)u!q zf9d%kkIbTU1APR?PSpWe>Y_K?uIBI)aUk;TyMMd&RBPfGP4$9QhIn`R|4v?|JuxTP z`-yX`=@yJ$7zTGoQ7&%>R4LbD7O_T%Ia<#j=7fn%zed_(a*jRfE25H?Mxl~?k8>o0 zBa;w-f+R-Orsf+!CG8xXMyC9!iQ%i_Cl=)%A6gv_F)Ef5yyzUJAhe)CB^n4Xp0tbv zE6^ja!pz$+>#}`t`iHM*O+R zJ^yQAMDMR_nsDS2LvUG(`OI><@cZmqJ|C|@t&Fs^hbzNt2>&{ zi$KjXaepsCiNP!~^?p_Ief#;V7PF3Fi6oF~SUP74@wGyXl_c?|aD~s_>C#2j(*wUD z_j|7_t-I$~jFvuo)#8R0_6PcQ!UuZziDFyA;#lvnfnp5%Gr(+DVJ4^9)B}z*7`S2A zqjYwl+pE2mZZ3i158imN)Q7jf78-|T-FD6E?HtVQH-A(TXd$(%KiCs3vH4$LPHhA= z+pp|+%#;xMTP_s$jyhHTR;fV+otKPAcTZ`C8@DXVfMJ6t1eJWha*4`4aKmuP9YbK} z$aQxyeRebXK6|T6*tJ>bdHdRWZT6o;Z*HyYR_mJg%6QeaishcqI?{rH{mNXKN!!JX z?z=^ShQQn`h3X2LAZy2Bom_cq(Ol8n9TV(@C{&=Kin2WEQ#~YjD+|UK&vOF~8_lwG%?V|Vhaxuu(6_y7_j3VtBG zf87x49!6@lP@-2QDH%eD$ z<+>8izeevK;}j|Un}qU+_--sVe1LtV|9?OKUz86jOt-LQK8@GF&@BAAXb$(jbDm@N@UAzA|doe6)C!@f9Jusz-UF|?*^9EbpMrXOo` zu`D)ee^*v6vw7Xw zKGG7dz}-=oxj#!gcmg|DYfJPK2?>XZCa}rvPp`uhae^%XPOnV1gbzgE30X({{Ie5^ zEn4H5ByhFSj}CqE=_ehE1OgIH00M*x0TKSC3a$j$sLyzj|IZ)d1V~&#aKcu23T6La zZ_a?(mnG_h_TjX`eSg+{1{SVYs$ZKyYDfAA5v4&=V&JRC;iA!gFVpBNP|7s0sRp0N zXS}Q3;&KrKm*tK?o9_xGwiSH8I{Sxk-ev*IHC*SoR^a z??!hhnLRTb!i$51gAyJB2oS}nj@6#1)*gNC!LeNE{&s#GM{nUZ1B6P=3^G+_WUDH6ZK1C32VVW_|140B zIgw_!sB%qu(Q%p5+$Id6F-(* zc*^}?Uav-7^lS_&`sn8V(7)Zv?Fa_uZI3kTd2_q;;P7XvlvN+4VV-lGA${Rs z=iC}E{~74rG40W~9Odus_fM4rsUr2-E?W;j%}=`>#)ENn$8`X5YHKj-9P72_R2j+g zFlM@9#7KWWx3_ z8L&LXEaT+H7vP~Z$ViZ>02Dae3*^5bbdRfdq8 z&A1uE=L@wgRA?ARWqz#Cw@qPHbSi|vAe@LVTz7Y;qpjq3z9$h1Gdpc+Vh4qQXs`}Y z4t`jwpwdW*lj!h6LgVFem>1xrS1J1-j^raw)fwx;%r|5& z9BQUb{1*V!*2hH3hXgqaYw)e8*mdB*DHTL9C4fK9HC)f*Q*g3=t&LanAuySBfYf#l zDEjK-gsY=2Y?s;o4?miAzZLI25xZ%Y-Q@tDS>N5o)@DGm?WA4rurR2? zXcvR0#i>_C$Auxo2n@05fB(wtk#@(Y9PX#<4u5}U|DBBTiBEHuV&-uD@n)qnH1I`R zlK6QTnFEuuXALP5jsSRr9Fj|@yVe(1|0Df{WhQb{`so_w!^~~D*Xd>(QSn9^%lEGj zV)thnOU&*YNjl!mu5eXMgVY3GgV^#?>tElJwqkM~&YOcvGVfk2dUjRwIg^~a zSRei@jLQU-lj9*T%Ym(m>)88Uy`TMXXQ@qE#jZbUWkf5P+H(EzK*p$6uhqNO(srGE zW9yCgl%C~p!L5woTOk)bBzhGp{!Xr<;?NV!!3CZLB;{)?H#908&ij-!d3`q#0N8O+ z?tjnvrzW0BvyHnkMjdG0Httx^F2K$ioaJU8E^9t$su5Hn7|>+qh!t2x8I2LmEY+(p z4Ln56+xv7 zC7+ejzcJ;_5N(0u<-tOFrfkipW#3zgKG|heMf4>1s_|iG~)%XKf<5 z7ObvmSJm0~C4)is0Eva)W%K1yM(AwMw4V$burH!WCogrI-Af(&=w@r$6gY(4_m1q8 zX-s2~2AH;-zxjL>p{)6gjN(lBSH6bAZVO~_mm5d^1z=Qr>=POgEU>t&pQW8;phu; z{)A|!rh!`zfsSXo`hZNQMkiN9srne}LSg}YfC^NEom@_{TOO2h&{PHDzZ;zX-1}wp zoKpu4Yq8OtYX0%KKkB*)hL#jzlEA#@NlFy@VvC-|?}$~K4A5Z^R4`mSJNl#D=B3C{;afqSZW2|Z~iqbA0?=HiwTfC6czSgg#R1^?1YK+6itt>^!trsPI&s?HC4jD5Ik_qk5*nT!R znR8~tnk+Yeq+6n)B^XvUnk=twyr8)wNBRO?Ui3LUpO}F#>O7Cl zM*TF6AMdy?ZIEWCc{8S$s;51Zp@kn@UsRWY1wD?b$eJH=1p-K6`l$4kKd#ArKTUg& z-cTX9%od!1sr<;$9gV^#I1%6!MCk(CkM&5NB{I8PUue8=+hi7&h@R@Z{j>2s3vst? zd8_spvHg-SKE{_AZi)(EU4i?^Q73z#I~(#UWNk8!DKJuzB|R7CbmxGEwLj@VrI7G< znroRcT`tG{wG1{@JXKD^lYwc2yM7DB#4n~gO%C}#7(oj%t)^2Ex82E(I#~~VPJlJo z&T#Aj4s}WR+UCo~d~;1YB75{+tkHu3;9OqOpAoX_TPW%zFzI_TQ^vc`d^r3oa@6t2 z_-fmgK2zYMk4{r)u8HuqFz|IK5EnQxJ`8EHPh+*ZA8!Qak`ogY?_ z0Xphq=MefQabr^Kg7V7GV_a9`@4g=VAkv2f`l7x}=l`+jce&6}ZsNHTI@RXqbLh1} zH!1qY>xKP%!*Pxy)0faGKJmL1Y}RQ>w4xUt2lLT?(lqN|tujI=2q{7WT;OZ&7P*yT zz~yBD+=GM0x6A<{NRlmpz=kAP!HV$#&VfD zbG;>vSgsAVq~fnCjQ1y14BfdZ2>q{#t>isXkmw~P(xLi}pR=xB+q8(J?eP^ry5sx9@KKD;sR)y)G_+<#Mswmrsw^)ak`_w06!Ev|$YnmCi@gpF ziA#*yNBnPwiI-%uPX01IYst$lGj0`9BiF~VkyB~5{g0#OY9d!h_0z%EM)e8SGpk6E z8vti{!*K$D!!{Z}4kk03O2^ZbBWDs8FqC4QaEyYzb%3Ylo#C|B>qP+p)+2u-IqL_e z=rMUF*KXEw)$5^ekj6pH6uaoMRVG!Q$NzkTO5Ctny=g3;v7uSUY~QMSg96CJ@6J)y z$ghT~8xG$VnyI3`OB+9LRe~E!8Huu-s>gu8{9R5gb9~YVsi7ENBd$-~a`^aC9QsXN zML4IDb}{bzPcTU>K+u6UNk|a{_uB&u$LJS>@PFexZqW!iXVxgNa5YG2Zpum=(}+)( zPAju>+opSE%izrOgakD!FrXP?MvK7*asG+MvS_jHeR;5-2~m7jrdyde$i#0m*}pFP z&2j@dx&QI{i+Rfor?4Fw0Wg^e21FVBU-#KL=q#B?gB!4H>hQl2PYt44&pkho4zQiikZ~X21o&og`PdmR&bM><|FcAuyQfv zDExgu$dIUh(1lAY`#C0Ok6%*%2_g#J(}*N4%|6TFRNR>bjpqGF$QTvL>;~Qm3YI_8 zv>Huq=juua9`6Cbz_SDDH|R)CI7j-mdvH#%pgFkmZsU#RDUC-5C!darFA2GBFSB>o zP$@+yeyLX1M}IFYBdR@^k+ZQS{^0c*;eF2G(K8U^=tC@cY68h?n6Rt&Vh$K%DaG4f zJ=?t*2FR{2t($A!Q=HjE@BTu@_j(l^i!dtLVT4rqG%WpvUZrDJCgz1@Iwf()eucMlu8oL?k4mI9~tpfvesu1V2DF3-S)p3=iKTaXoje z)yKo}kz($)n~ofrhJMG6F>EYR%8v(Egl6{R4mpI1%7;3z@LV~b#=xZ$mC;jO(d~)e zSJs;2&;E(UO?EMV*{#M~d!%tn|1+0mhQJwAC}x)uXRC!$G8?K0O%u=O%o++&Z%?mo zSH+@R3BW+^zU;XD9f0?FU*bCb8AK(+hUQqH-T|-jXhIEw$;@exAD}rKF+pPwoT04x zAQ}|pMvYX2FU^vJ36Yjk11jXCSb-TRR{7pHXFGxRF=8Bno&bo|J3;!jk9Mj4Mq?_n zQ=lrZO8OSs=fdU*FzcK%)b3ZrET3B@bRcdIlUzeqgSvw#Hi60PIiS9Ppx+bO zLT4cQKcc0QEHW%=%s8XBi-F`FU~bT$Jl!8B&jl(6xrjdc^91hj%rj6K(0WW5&Wpdc z{jjgZ5yT}4?W}TKYMUXgFMLuOEMbaNi654i&tyD1F#4rG;$Wfq1*dvMu?)v&FG9HA zC^QIZ`EI*dPzT*U5T@D#At;h#qqe zK9dW(^FGdmJrNfYf98!8Xo$k}HYv#Xv9fjIRh^wO$HC{%h5fHJJ;7{fb86pzSY(ZO zStfIcyb$qf7$U=!G$ah)w^$&|g|au%VVwRRm~NhhouaciO{&Xz?J#p?jbD$CJw;iE zO5jElL^Di(n29(ss9=_u>QA6mJ}`poY8wRHeL4sU4j0mO!|G~d_!|{e6R?dI0|Ra$ zX6zVzB27G=OMaK1kS5mkgCd~xRnr@G@CiAB=xY#K#7o$iOtpH%D1QC^{BVhKG{kV_ zL4!e$N)&%eNfxjzfF{|fjt*Li1u z*5qp7$PRGSf!o8Gfk{xc!N5l;=dd>H?I`|4{Oxc?l z8$5Zj$6esS4siHb=G+yauYvUoeBA2*XmmThy{!${7=Q}`nWM@_LuQyJg8%%@N%5Aa S=6{f900K`}KbLh*2~7aSqXA(6 literal 0 HcmV?d00001 diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index 0c5e1974bd..cf80d1a55e 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -41,7 +41,7 @@ module "service-accounts-tf-environments" { prefix = var.prefix names = var.environments grant_billing_role = true - grant_xpn_roles = true + grant_xpn_roles = var.grant_xpn_roles generate_keys = var.generate_service_account_keys } diff --git a/organization-bootstrap/environments/providers.tf b/organization-bootstrap/environments/providers.tf new file mode 100644 index 0000000000..33091732eb --- /dev/null +++ b/organization-bootstrap/environments/providers.tf @@ -0,0 +1 @@ +provider "google" {} diff --git a/organization-bootstrap/environments/variables.tf b/organization-bootstrap/environments/variables.tf index a53b07113b..2d06520cb3 100644 --- a/organization-bootstrap/environments/variables.tf +++ b/organization-bootstrap/environments/variables.tf @@ -18,6 +18,12 @@ variable "gcs_location" { default = "EU" } +variable "grant_xpn_roles" { + description = "Grant roles needed for Shared VPC creation to service accounts." + default = true +} + + variable "organization_id" { description = "Organization id." type = string @@ -61,4 +67,3 @@ variable "project_services" { "storage-api.googleapis.com", ] } - From 23e86e439a059d3f26c0d3367acc4cdf8e6b483e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 20 Aug 2019 20:34:40 +0200 Subject: [PATCH 18/24] org env: update roles in README, backend file --- .gitignore | 1 + organization-bootstrap/environments/README.md | 6 +++--- organization-bootstrap/environments/backend.tf | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e2654ddeb6..30f62292eb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ **/terraform.tfvars .idea .vscode +backend-config.hcl credentials.json key.json diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index 0c43748a3d..92e078e2e6 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -30,11 +30,11 @@ As mentioned above this root module is meant to be run infrequently, only when t Regardless of how it's run, the credentials used need very specific roles on the root node, and some roles at the organization level if Shared VPC usage is anticipated in environments: -- Billing Account Administrator on the billing account +- Billing Account Administrator on the billing account or organization - Folder Administrator -- Logging Administrator on the root node (folder or organization) +- Logging Administrator on the root folder or organization - Project Creator -- Organization Administrator (if Shared VPC roles need to be granted) +- Organization Administrator, if Shared VPC roles need to be granted ### Prerequisites diff --git a/organization-bootstrap/environments/backend.tf b/organization-bootstrap/environments/backend.tf index deda6810e3..1745379009 100644 --- a/organization-bootstrap/environments/backend.tf +++ b/organization-bootstrap/environments/backend.tf @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# uncomment after initial run once bootstrap bucket has been created +# comment before initial run -# terraform { -# backend "gcs" { -# bucket = "" -# } -# } +terraform { + backend "gcs" { + bucket = "" + } +} From 70adb1e2c68b6909a49f04dfaa9a576d2de3b231 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 21 Aug 2019 08:55:27 +0200 Subject: [PATCH 19/24] org env: README changes --- organization-bootstrap/environments/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index 92e078e2e6..f547ea7ef4 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -13,7 +13,7 @@ This set of Terraform files is usually applied manually by an org-level administ - anticipating the requirement of organizational-level roles for specific resources (eg Shared VPC), by granting them to the service accounts used for environment automation - enforcing separation of duties by using separate sets of automation resources (GCS, service accounts) for each environment, and only granting roles scoped to the environment's folder -## Managed resources and GCP services +## Managed resources and services This sample creates several distinct groups of resources: @@ -26,9 +26,11 @@ The number of resources in this sample is kept to a minimum so as to make it gen ## Operational considerations -As mentioned above this root module is meant to be run infrequently, only when the number of environments change or a new service needs to be added to the shared project, so the advantages of automating it in a CI pipeline are very limited. +As mentioned above this root module is meant to be run infrequently, only when an environment or a shared service needs to be added or changed, so the advantages of automating it in a CI pipeline are very limited. -Regardless of how it's run, the credentials used need very specific roles on the root node, and some roles at the organization level if Shared VPC usage is anticipated in environments: +### IAM roles + +Regardless of how it's run, the credentials used need very specific roles on the root node, plus additional roles at the organization level if Shared VPC usage is anticipated in environments: - Billing Account Administrator on the billing account or organization - Folder Administrator @@ -36,12 +38,9 @@ Regardless of how it's run, the credentials used need very specific roles on the - Project Creator - Organization Administrator, if Shared VPC roles need to be granted -### Prerequisites - - ### State -### Gotchas +### Things to be aware of From 956a86810305a430bbeedfa1e89f2ea0f235db21 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 21 Aug 2019 08:58:05 +0200 Subject: [PATCH 20/24] org env: README changes --- organization-bootstrap/environments/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index f547ea7ef4..a1eca13f9d 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -40,8 +40,11 @@ Regardless of how it's run, the credentials used need very specific roles on the ### State +TODO: describe the state switch that needs to be done after first apply + ### Things to be aware of +TODO: describe potential issues with multiple resources, and the upcoming `foreach` fix ## Inputs From ee81ce4d05d850b314c9b2b11805f1d298b4c052 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 21 Aug 2019 09:02:01 +0200 Subject: [PATCH 21/24] org env: README changes --- organization-bootstrap/environments/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index a1eca13f9d..72fc1f617d 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -1,8 +1,8 @@ # Environment-based organizational sample -This sample creates an organizational layout with a single level, where each folder is usually mapped to one infrastructure environment (test, dev, etc.). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and sets up the correct roles on those to enforce separation of duties at the environment level. +This sample creates an organizational layout with a single level, where each folder is usually mapped to one infrastructure environment (test, dev, etc.). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and the correct roles on those to enforce separation of duties at the environment level. -This layout is well suited for small and medium-sized infrastructures, where a limited set of teams manages the underlying infrastructure, and the complexity in application resource ownership and access roles is mostly dealt with at the project level, and/or in the individual infrastructure services (GKE, Cloud SQL, etc.). Its simplicity also makes it a good starting point for more complex or specialized layouts. +This layout is well suited for small and medium-sized infrastructures managed by a smal set of teams, where the complexity in application resource ownership and access roles is mostly dealt with at the project level, and/or in the individual services (GKE, Cloud SQL, etc.). Its simplicity also makes it a good starting point for more complex or specialized layouts. ![High-level diagram](diagram.png "High-level diagram") From e3b4cb97036f6603b695b031678e371485d6806b Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 21 Aug 2019 09:05:24 +0200 Subject: [PATCH 22/24] org env: README changes --- organization-bootstrap/environments/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index 72fc1f617d..0e6148b7b2 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -55,10 +55,11 @@ TODO: describe potential issues with multiple resources, and the upcoming `forea | environments | Environment short names. | list(string) | n/a | yes | | gcs\_location | GCS bucket location. | string | `"EU"` | no | | generate\_service\_account\_keys | Generate and store service account keys in the state file. | string | `"false"` | no | +| grant\_xpn\_roles | Grant roles needed for Shared VPC creation to service accounts. | string | `"true"` | no | | organization\_id | Organization id. | string | n/a | yes | | prefix | Prefix used for resources that need unique names. | string | n/a | yes | | project\_services | Service APIs enabled by default in new projects. | list | `` | no | -| root\_node | Root node for the new hierarchy, either 'organizations/org\_id' or 'folders/folder\_id'. | string | n/a | yes | +| root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | | terraform\_owners | Terraform project owners, in IAM format. | list | `` | no | ## Outputs From 95408f5c991d9335a799e69e18efedaa37aae0a7 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 21 Aug 2019 11:05:19 +0200 Subject: [PATCH 23/24] org env: add IAM variables for audit and shared projects --- organization-bootstrap/environments/README.md | 5 +++++ organization-bootstrap/environments/main.tf | 19 +++++++++++-------- .../environments/variables.tf | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/organization-bootstrap/environments/README.md b/organization-bootstrap/environments/README.md index 0e6148b7b2..1920b4ce27 100644 --- a/organization-bootstrap/environments/README.md +++ b/organization-bootstrap/environments/README.md @@ -45,12 +45,15 @@ TODO: describe the state switch that needs to be done after first apply ### Things to be aware of TODO: describe potential issues with multiple resources, and the upcoming `foreach` fix +TODO: describe how `prefix` can be used to enforce naming +TODO: describe xpn roles ## Inputs | Name | Description | Type | Default | Required | |------|-------------|:----:|:-----:|:-----:| +| audit\_viewers | Audit project viewers, in IAM format. | list | `` | no | | billing\_account\_id | Billing account id used as default for new projects. | string | n/a | yes | | environments | Environment short names. | list(string) | n/a | yes | | gcs\_location | GCS bucket location. | string | `"EU"` | no | @@ -60,6 +63,8 @@ TODO: describe potential issues with multiple resources, and the upcoming `forea | prefix | Prefix used for resources that need unique names. | string | n/a | yes | | project\_services | Service APIs enabled by default in new projects. | list | `` | no | | root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | +| shared\_bindings\_members | List of comma-delimited IAM-format members for the additional shared project bindings. | list | `` | no | +| shared\_bindings\_roles | List of roles for additional shared project bindings. | list | `` | no | | terraform\_owners | Terraform project owners, in IAM format. | list | `` | no | ## Outputs diff --git a/organization-bootstrap/environments/main.tf b/organization-bootstrap/environments/main.tf index cf80d1a55e..fef3a34047 100644 --- a/organization-bootstrap/environments/main.tf +++ b/organization-bootstrap/environments/main.tf @@ -108,6 +108,7 @@ module "project-audit" { name = "audit" lien_reason = "audit" activate_apis = var.project_services + viewers = var.audit_viewers } # audit logs destination on BigQuery @@ -142,14 +143,16 @@ module "log-sink-audit" { # shared resources project module "project-shared-resources" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "3.2.0" - parent = var.root_node - billing_account = var.billing_account_id - prefix = var.prefix - name = "shared" - lien_reason = "shared" - activate_apis = var.project_services + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + version = "3.2.0" + parent = var.root_node + billing_account = var.billing_account_id + prefix = var.prefix + name = "shared" + lien_reason = "shared" + activate_apis = var.project_services + extra_bindings_roles = var.shared_bindings_roles + extra_bindings_members = var.shared_bindings_members } # Add further modules here for resources that are common to all environments diff --git a/organization-bootstrap/environments/variables.tf b/organization-bootstrap/environments/variables.tf index 2d06520cb3..42d2ca2501 100644 --- a/organization-bootstrap/environments/variables.tf +++ b/organization-bootstrap/environments/variables.tf @@ -1,3 +1,8 @@ +variable "audit_viewers" { + description = "Audit project viewers, in IAM format." + default = [] +} + variable "billing_account_id" { description = "Billing account id used as default for new projects." type = string @@ -39,6 +44,17 @@ variable "root_node" { type = string } +variable "shared_bindings_members" { + description = "List of comma-delimited IAM-format members for the additional shared project bindings." + # example: ["user:a@example.com,b@example.com", "user:c@example.com"] + default = [] +} +variable "shared_bindings_roles" { + description = "List of roles for additional shared project bindings." + # example: ["roles/storage.objectViewer", "roles/storage.admin"] + default = [] +} + variable "terraform_owners" { description = "Terraform project owners, in IAM format." default = [] From 4e7ffe420174a5d39b7cdfcede7378e0aa9e8d66 Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Fri, 30 Aug 2019 15:50:26 +0200 Subject: [PATCH 24/24] Business-units organization bootstrap example, initial commit --- .../business-units/README.md | 102 +++++++++++ .../business-units/backend.tf | 21 +++ .../business-units/diagram.png | Bin 0 -> 47643 bytes organization-bootstrap/business-units/main.tf | 162 ++++++++++++++++++ .../business-unit-folders-tree/README.md | 43 +++++ .../business-unit-folders-tree/main.tf | 83 +++++++++ .../business-unit-folders-tree/outputs.tf | 42 +++++ .../business-unit-folders-tree/variables.tf | 47 +++++ .../business-unit-folders-tree/versions.tf | 4 + .../business-units/outputs.tf | 119 +++++++++++++ .../business-units/providers.tf | 3 + .../business-units/variables.tf | 107 ++++++++++++ .../business-units/versions.tf | 4 + 13 files changed, 737 insertions(+) create mode 100644 organization-bootstrap/business-units/README.md create mode 100644 organization-bootstrap/business-units/backend.tf create mode 100644 organization-bootstrap/business-units/diagram.png create mode 100644 organization-bootstrap/business-units/main.tf create mode 100644 organization-bootstrap/business-units/modules/business-unit-folders-tree/README.md create mode 100644 organization-bootstrap/business-units/modules/business-unit-folders-tree/main.tf create mode 100644 organization-bootstrap/business-units/modules/business-unit-folders-tree/outputs.tf create mode 100644 organization-bootstrap/business-units/modules/business-unit-folders-tree/variables.tf create mode 100644 organization-bootstrap/business-units/modules/business-unit-folders-tree/versions.tf create mode 100644 organization-bootstrap/business-units/outputs.tf create mode 100644 organization-bootstrap/business-units/providers.tf create mode 100644 organization-bootstrap/business-units/variables.tf create mode 100644 organization-bootstrap/business-units/versions.tf diff --git a/organization-bootstrap/business-units/README.md b/organization-bootstrap/business-units/README.md new file mode 100644 index 0000000000..e8b537aa2a --- /dev/null +++ b/organization-bootstrap/business-units/README.md @@ -0,0 +1,102 @@ +# Business-units based organizational sample + +This sample creates an organizational layout with two folder levels, where the first level is usually mapped to one business unit (infra, data, analythics) and the second level represents enviroments (prod, test). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and the correct roles on those to enforce separation of duties at the environment level. + +This layout is well suited for small and medium-sized infrastructures managed by a smal set of teams, where the complexity in application resource ownership and access roles is mostly dealt with at the project level, and/or in the individual services (GKE, Cloud SQL, etc.). Its simplicity also makes it a good starting point for more complex or specialized layouts. + +This layout is well suited for medium-sized infrastructures managed by different sets of teams grouped to different business units, where the complexity in application resource ownership and access roles is mostly dealt with at the project level, and/or in the individual services (GKE, Cloud SQL, etc.). + +![High-level diagram](diagram.png "High-level diagram") + +This set of Terraform files is usually applied manually by an org-level administrator as a first step, and then reapplied only when a new business-unit or environment needs to be created or an existing one removed, and serves different purposes: + +- automating and parameterizing creation of the organizational layout +- automating creation of the base resources needed for Terraform automation, obviating the need for external scripts or hand-coded commands +- anticipating the requirement of organizational-level roles for specific resources (eg Shared VPC), by granting them to the service accounts used for environment automation +- enforcing separation of duties by using separate sets of automation resources (GCS, service accounts) for each environment, and only granting roles scoped to the environment's folder + +## Managed resources and services + +This sample creates several distinct groups of resources: + +- one folder per business unit +- two second-level folders (test, prod) for every business unit +- one top-level project to hold Terraform-related resources +- one top-level project to set up and host centralized audit log exports (optional). +- one top-level project to hold services used across environments like GCS, GCR, KMS, Cloud Build, etc. (optional) + +The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging the full array of [Cloud Foundation Toolkit modules](https://github.com/terraform-google-modules), especially in the shared services project. + +## Operational considerations + +As mentioned above this root module is meant to be run infrequently, only when an environment or a shared service needs to be added or changed, so the advantages of automating it in a CI pipeline are very limited. + +### IAM roles + +Regardless of how it's run, the credentials used need very specific roles on the root node, plus additional roles at the organization level if Shared VPC usage is anticipated in environments: + +- Billing Account Administrator on the billing account or organization +- Folder Administrator +- Logging Administrator on the root folder or organization +- Project Creator +- Organization Administrator, if Shared VPC roles need to be granted + +### State + +TODO: describe the state switch that needs to be done after first apply + +### Things to be aware of + +TODO: describe potential issues with multiple resources, and the upcoming `foreach` fix +TODO: describe how `prefix` can be used to enforce naming +TODO: describe xpn roles + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| audit\_viewers | Audit project viewers, in IAM format. | list | `` | no | +| billing\_account\_id | Billing account id used as default for new projects. | string | n/a | yes | +| business\_unit\_1\_envs | Business unit 1 environments short names. | list(string) | `` | no | +| business\_unit\_1\_name | Business unit 1 short name. | string | n/a | yes | +| business\_unit\_2\_envs | Business unit 2 environments short names. | list(string) | `` | no | +| business\_unit\_2\_name | Business unit 2 short name. | string | n/a | yes | +| business\_unit\_3\_envs | Business unit 3 environments short names. | list(string) | `` | no | +| business\_unit\_3\_name | Business unit 3 short name. | string | n/a | yes | +| gcs\_location | GCS bucket location. | string | `"EU"` | no | +| generate\_service\_account\_keys | Generate and store service account keys in the state file. | string | `"false"` | no | +| organization\_id | Organization id. | string | n/a | yes | +| prefix | Prefix used for resources that need unique names. | string | n/a | yes | +| project\_services | Service APIs enabled by default in new projects. | list | `` | no | +| root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | +| shared\_bindings\_members | List of comma-delimited IAM-format members for the additional shared project bindings. | list | `` | no | +| shared\_bindings\_roles | List of roles for additional shared project bindings. | list | `` | no | +| terraform\_owners | Terraform project owners, in IAM format. | list | `` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| audit\_logs\_bq\_dataset | Bigquery dataset for the audit logs export. | +| audit\_logs\_project | Project that holds the audit logs export resources. | +| bootstrap\_tf\_gcs\_bucket | GCS bucket used for the bootstrap Terraform state. | +| business\_unit\_1\_envs\_folders\_ids | Business unit 1 environment folders. | +| business\_unit\_1\_envs\_service\_account\_keys | Service account keys used to run each environment Terraform modules for business unit 1. | +| business\_unit\_1\_envs\_service\_accounts | Service accounts used to run each environment Terraform modules for business unit 1. | +| business\_unit\_1\_envs\_tf\_gcs\_buckets | GCS buckets used for each environment Terraform state for business unit 1. | +| business\_unit\_1\_top\_level\_folder\_id | Business unit 1 top-level folder. | +| business\_unit\_2\_envs\_folders\_ids | Business unit 2 environment folders. | +| business\_unit\_2\_envs\_service\_account\_keys | Service account keys used to run each environment Terraform modules for business unit 2. | +| business\_unit\_2\_envs\_service\_accounts | Service accounts used to run each environment Terraform modules for business unit 2. | +| business\_unit\_2\_envs\_tf\_gcs\_buckets | GCS buckets used for each environment Terraform state for business unit 2. | +| business\_unit\_2\_top\_level\_folder\_id | Business unit 2 top-level folder. | +| business\_unit\_3\_envs\_folders\_ids | Business unit 3 environment folders. | +| business\_unit\_3\_envs\_service\_account\_keys | Service account keys used to run each environment Terraform modules for business unit 3. | +| business\_unit\_3\_envs\_service\_accounts | Service accounts used to run each environment Terraform modules for business unit 3. | +| business\_unit\_3\_envs\_tf\_gcs\_buckets | GCS buckets used for each environment Terraform state for business unit 3. | +| business\_unit\_3\_top\_level\_folder\_id | Business unit 3 top-level folder. | +| shared\_resources\_project | Project that holdes resources shared across business units. | +| terraform\_project | Project that holds the base Terraform resources. | + + diff --git a/organization-bootstrap/business-units/backend.tf b/organization-bootstrap/business-units/backend.tf new file mode 100644 index 0000000000..1745379009 --- /dev/null +++ b/organization-bootstrap/business-units/backend.tf @@ -0,0 +1,21 @@ +# Copyright 2019 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. + +# comment before initial run + +terraform { + backend "gcs" { + bucket = "" + } +} diff --git a/organization-bootstrap/business-units/diagram.png b/organization-bootstrap/business-units/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..6aaf471b6ab31db12858d69d94b9da01c793ed62 GIT binary patch literal 47643 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

sD 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 +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| billing\_account\_id | Billing account id used as default for new projects. | string | n/a | yes | +| gcs\_location | GCS bucket location. | string | `"EU"` | no | +| generate\_service\_account\_keys | Generate and store service account keys in the state file. | string | `"false"` | no | +| organization\_id | Organization id. | string | n/a | yes | +| prefix | Prefix used for resources that need unique names. | string | n/a | yes | +| root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | +| second\_level\_folders\_names | Second level folders names. | list(string) | n/a | yes | +| tf\_project\_id | Id of a project where service accounts and terraform state buckets should be created | string | n/a | yes | +| top\_level\_folder\_name | Top level folder name. | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| second\_level\_folders\_ids | Second-level folders IDs. | +| second\_level\_folders\_service\_account\_emails | Service accounts used to run each econd-level folder Terraform modules. | +| second\_level\_folders\_service\_account\_keys | Service account keys used to run each second-level folder Terraform modules. | +| second\_level\_folders\_tf\_gcs\_bucket\_names | GCS buckets used for each second-level folder Terraform state. | +| top\_level\_folder\_id | Top-level folder ID. | + + diff --git a/organization-bootstrap/business-units/modules/business-unit-folders-tree/main.tf b/organization-bootstrap/business-units/modules/business-unit-folders-tree/main.tf new file mode 100644 index 0000000000..e6ff672b87 --- /dev/null +++ b/organization-bootstrap/business-units/modules/business-unit-folders-tree/main.tf @@ -0,0 +1,83 @@ +# Copyright 2019 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 { + second_level_unique_names = formatlist("${var.top_level_folder_name}-%s", var.second_level_folders_names) +} + +############################################################################### +# Terraform resources # +############################################################################### + +# Per second-level folder service accounts + +module "service-accounts-tf-second-level-folders" { + source = "terraform-google-modules/service-accounts/google" + version = "2.0.0" + project_id = var.tf_project_id + org_id = var.organization_id + billing_account_id = var.billing_account_id + prefix = var.prefix + names = local.second_level_unique_names + grant_billing_role = true + grant_xpn_roles = false + generate_keys = var.generate_service_account_keys +} + +# Per second-level folders Terraform state GCS buckets + +module "gcs-tf-second-level-folders" { + source = "terraform-google-modules/cloud-storage/google" + version = "1.0.0" + project_id = var.tf_project_id + prefix = "${var.prefix}-tf" + names = local.second_level_unique_names + location = var.gcs_location + set_admin_roles = true + bucket_admins = zipmap( + local.second_level_unique_names, + module.service-accounts-tf-second-level-folders.iam_emails_list + ) +} + +############################################################################### +# Top-level folder # +############################################################################### + +module "top-level-folder" { + source = "terraform-google-modules/folders/google" + version = "2.0.0" + parent = var.root_node + names = [var.top_level_folder_name] +} + +############################################################################### +# Second-level folders # +############################################################################### + +module "second-level-folders" { + source = "terraform-google-modules/folders/google" + version = "2.0.0" + parent = module.top-level-folder.id + names = var.second_level_folders_names + set_roles = true + per_folder_admins = module.service-accounts-tf-second-level-folders.iam_emails_list + folder_admin_roles = [ + "roles/resourcemanager.folderViewer", + "roles/resourcemanager.projectCreator", + "roles/owner", + "roles/compute.networkAdmin", + "roles/compute.xpnAdmin" + ] +} \ No newline at end of file diff --git a/organization-bootstrap/business-units/modules/business-unit-folders-tree/outputs.tf b/organization-bootstrap/business-units/modules/business-unit-folders-tree/outputs.tf new file mode 100644 index 0000000000..f9fed32998 --- /dev/null +++ b/organization-bootstrap/business-units/modules/business-unit-folders-tree/outputs.tf @@ -0,0 +1,42 @@ +# Copyright 2019 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 "top_level_folder_id" { + description = "Top-level folder ID." + value = module.top-level-folder.id +} + +output "second_level_folders_ids" { + description = "Second-level folders IDs." + value = module.second-level-folders.ids +} + +output "second_level_folders_tf_gcs_bucket_names" { + description = "GCS buckets used for each second-level folder Terraform state." + value = module.gcs-tf-second-level-folders.names +} + +output "second_level_folders_service_account_keys" { + description = "Service account keys used to run each second-level folder Terraform modules." + sensitive = true + value = module.service-accounts-tf-second-level-folders.keys +} + +output "second_level_folders_service_account_emails" { + description = "Service accounts used to run each econd-level folder Terraform modules." + value = module.service-accounts-tf-second-level-folders.emails +} + +# Add further outputs here for the additional modules that manage shared +# resources, like GCR, GCS buckets, KMS, etc. diff --git a/organization-bootstrap/business-units/modules/business-unit-folders-tree/variables.tf b/organization-bootstrap/business-units/modules/business-unit-folders-tree/variables.tf new file mode 100644 index 0000000000..a3905c9dea --- /dev/null +++ b/organization-bootstrap/business-units/modules/business-unit-folders-tree/variables.tf @@ -0,0 +1,47 @@ +variable "billing_account_id" { + description = "Billing account id used as default for new projects." + type = string +} + +variable "tf_project_id" { + description = "Id of a project where service accounts and terraform state buckets should be created" + type = string +} + +variable "top_level_folder_name" { + description = "Top level folder name." + type = string +} + +variable "second_level_folders_names" { + description = "Second level folders names." + type = list(string) +} + +variable "generate_service_account_keys" { + description = "Generate and store service account keys in the state file." + default = false +} + +variable "gcs_location" { + description = "GCS bucket location." + default = "EU" +} + +variable "organization_id" { + description = "Organization id." + type = string +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "root_node" { + description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." + type = string +} + + + diff --git a/organization-bootstrap/business-units/modules/business-unit-folders-tree/versions.tf b/organization-bootstrap/business-units/modules/business-unit-folders-tree/versions.tf new file mode 100644 index 0000000000..ac97c6ac8e --- /dev/null +++ b/organization-bootstrap/business-units/modules/business-unit-folders-tree/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/organization-bootstrap/business-units/outputs.tf b/organization-bootstrap/business-units/outputs.tf new file mode 100644 index 0000000000..c9eca8c682 --- /dev/null +++ b/organization-bootstrap/business-units/outputs.tf @@ -0,0 +1,119 @@ +# Copyright 2019 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 "terraform_project" { + description = "Project that holds the base Terraform resources." + value = module.project-tf.project_id +} + +output "bootstrap_tf_gcs_bucket" { + description = "GCS bucket used for the bootstrap Terraform state." + value = module.gcs-tf-bootstrap.name +} + +output "business_unit_1_top_level_folder_id" { + description = "Business unit 1 top-level folder." + value = module.business-unit-1-folders-tree.top_level_folder_id +} + +output "business_unit_1_envs_folders_ids" { + description = "Business unit 1 environment folders." + value = module.business-unit-1-folders-tree.second_level_folders_ids +} + +output "business_unit_1_envs_tf_gcs_buckets" { + description = "GCS buckets used for each environment Terraform state for business unit 1." + value = module.business-unit-1-folders-tree.second_level_folders_tf_gcs_bucket_names +} + +output "business_unit_1_envs_service_account_keys" { + description = "Service account keys used to run each environment Terraform modules for business unit 1." + sensitive = true + value = module.business-unit-1-folders-tree.second_level_folders_service_account_keys +} + +output "business_unit_1_envs_service_accounts" { + description = "Service accounts used to run each environment Terraform modules for business unit 1." + value = module.business-unit-1-folders-tree.second_level_folders_service_account_emails +} + +output "business_unit_2_top_level_folder_id" { + description = "Business unit 2 top-level folder." + value = module.business-unit-2-folders-tree.top_level_folder_id +} + +output "business_unit_2_envs_folders_ids" { + description = "Business unit 2 environment folders." + value = module.business-unit-2-folders-tree.second_level_folders_ids +} + +output "business_unit_2_envs_tf_gcs_buckets" { + description = "GCS buckets used for each environment Terraform state for business unit 2." + value = module.business-unit-2-folders-tree.second_level_folders_tf_gcs_bucket_names +} + +output "business_unit_2_envs_service_account_keys" { + description = "Service account keys used to run each environment Terraform modules for business unit 2." + sensitive = true + value = module.business-unit-2-folders-tree.second_level_folders_service_account_keys +} + +output "business_unit_2_envs_service_accounts" { + description = "Service accounts used to run each environment Terraform modules for business unit 2." + value = module.business-unit-2-folders-tree.second_level_folders_service_account_emails +} + +output "business_unit_3_top_level_folder_id" { + description = "Business unit 3 top-level folder." + value = module.business-unit-3-folders-tree.top_level_folder_id +} + +output "business_unit_3_envs_folders_ids" { + description = "Business unit 3 environment folders." + value = module.business-unit-3-folders-tree.second_level_folders_ids +} + +output "business_unit_3_envs_tf_gcs_buckets" { + description = "GCS buckets used for each environment Terraform state for business unit 3." + value = module.business-unit-3-folders-tree.second_level_folders_tf_gcs_bucket_names +} + +output "business_unit_3_envs_service_account_keys" { + description = "Service account keys used to run each environment Terraform modules for business unit 3." + sensitive = true + value = module.business-unit-3-folders-tree.second_level_folders_service_account_keys +} + +output "business_unit_3_envs_service_accounts" { + description = "Service accounts used to run each environment Terraform modules for business unit 3." + value = module.business-unit-3-folders-tree.second_level_folders_service_account_emails +} + +output "audit_logs_bq_dataset" { + description = "Bigquery dataset for the audit logs export." + value = module.bq-audit-export.resource_name +} + +output "audit_logs_project" { + description = "Project that holds the audit logs export resources." + value = module.project-audit.project_id +} + +output "shared_resources_project" { + description = "Project that holdes resources shared across business units." + value = module.project-shared-resources.project_id +} + +# Add further outputs here for the additional modules that manage shared +# resources, like GCR, GCS buckets, KMS, etc. \ No newline at end of file diff --git a/organization-bootstrap/business-units/providers.tf b/organization-bootstrap/business-units/providers.tf new file mode 100644 index 0000000000..b4eb74cddc --- /dev/null +++ b/organization-bootstrap/business-units/providers.tf @@ -0,0 +1,3 @@ +provider "google" { + credentials = "${file("credentials.json")}" +} diff --git a/organization-bootstrap/business-units/variables.tf b/organization-bootstrap/business-units/variables.tf new file mode 100644 index 0000000000..4b4a68f65f --- /dev/null +++ b/organization-bootstrap/business-units/variables.tf @@ -0,0 +1,107 @@ +variable "audit_viewers" { + description = "Audit project viewers, in IAM format." + default = [] +} + +variable "billing_account_id" { + description = "Billing account id used as default for new projects." + type = string +} + +variable "business_unit_1_name" { + description = "Business unit 1 short name." + type = string +} + +variable "business_unit_1_envs" { + description = "Business unit 1 environments short names." + type = list(string) + default = ["prod", "test"] +} + +variable "business_unit_2_name" { + description = "Business unit 2 short name." + type = string +} + +variable "business_unit_2_envs" { + description = "Business unit 2 environments short names." + type = list(string) + default = ["prod", "test"] +} + +variable "business_unit_3_name" { + description = "Business unit 3 short name." + type = string +} + +variable "business_unit_3_envs" { + description = "Business unit 3 environments short names." + type = list(string) + default = ["prod", "test"] +} + +variable "generate_service_account_keys" { + description = "Generate and store service account keys in the state file." + default = false +} + +variable "gcs_location" { + description = "GCS bucket location." + default = "EU" +} + +variable "organization_id" { + description = "Organization id." + type = string +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "root_node" { + description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." + type = string +} + +variable "shared_bindings_members" { + description = "List of comma-delimited IAM-format members for the additional shared project bindings." + # example: ["user:a@example.com,b@example.com", "user:c@example.com"] + default = [] +} +variable "shared_bindings_roles" { + description = "List of roles for additional shared project bindings." + # example: ["roles/storage.objectViewer", "roles/storage.admin"] + default = [] +} + +variable "terraform_owners" { + description = "Terraform project owners, in IAM format." + default = [] +} + +variable "project_services" { + description = "Service APIs enabled by default in new projects." + default = [ + "bigquery-json.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudbilling.googleapis.com", + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "container.googleapis.com", + "containerregistry.googleapis.com", + "deploymentmanager.googleapis.com", + "iam.googleapis.com", + "iamcredentials.googleapis.com", + "logging.googleapis.com", + "oslogin.googleapis.com", + "pubsub.googleapis.com", + "replicapool.googleapis.com", + "replicapoolupdater.googleapis.com", + "resourceviews.googleapis.com", + "serviceusage.googleapis.com", + "storage-api.googleapis.com", + ] +} diff --git a/organization-bootstrap/business-units/versions.tf b/organization-bootstrap/business-units/versions.tf new file mode 100644 index 0000000000..ac97c6ac8e --- /dev/null +++ b/organization-bootstrap/business-units/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +}