From 27f1cc2b79d7c76ebae683dd23c271ca8b9ec550 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 9 Jan 2025 19:14:11 +0100 Subject: [PATCH] Implement FAST stage add-ons, refactor netsec as add-on (#2800) * security fixes * change netsec to be a virtual stage in resman * remove netsec bits from security stage, leave CAs in place * netsec - security profile groups * export regions to networking tfvars * netsec - trust stores * netsec refactor, untested * netsec plan working * netsec apply * netsec apply errors * netsec diagram * update diagram * move addon stages to addons folder * remove top-level assets folder * deprecate and remove fast plugins * addon tests * dynamic addon providers and cicd, untested * stage 1 addons in stage 0, refactor stage 0 cicd * addons and cicd refactor in stage 0 with tests * refactor stage 0 cicd * readd removed block * small bootstrap cicd fixes * refactor stage 1 cicd * resman tests * remove plugins from networking tests * fix fast tests * ngfw addon outputs * try to fix unrelated tflint error in bootstrap * remove common tfvars from bootstrap tests to fix linter errors * tfdoc * minimal readmes and links fixes * tfdoc * trim down test inventories * fix plan test * tfdoc * allow configuring output files names * fix tls inspection after adding count to project module * comment fixes * tfdoc --- fast/README.md | 13 +- .../1-resman-tenants}/.fast-stage.env | 0 .../1-resman-tenants}/README.md | 36 +- .../1-resman-tenants}/diagram-flow.png | Bin .../1-resman-tenants}/diagram.png | Bin .../identity-providers-defs.tf | 0 .../1-resman-tenants}/main.tf | 3 + .../1-resman-tenants}/outputs-files.tf | 14 +- .../1-resman-tenants}/outputs-gcs.tf | 4 +- .../1-resman-tenants}/outputs.tf | 0 .../templates/providers.tf.tpl | 0 .../templates/providers_terraform.tf.tpl | 0 .../templates/workflow-github.yaml | 0 .../templates/workflow-gitlab.yaml | 0 .../1-resman-tenants}/tenant-billing-iam.tf | 0 .../1-resman-tenants}/tenant-core.tf | 4 +- .../tenant-fast-automation.tf | 2 +- .../1-resman-tenants}/tenant-fast-cicd.tf | 2 +- .../tenant-fast-identity-providers.tf | 0 .../1-resman-tenants}/tenant-fast-logging.tf | 0 .../1-resman-tenants}/tenant-fast-vpcsc.tf | 0 .../1-resman-tenants}/tenant.tf | 4 +- .../1-resman-tenants}/variables-fast.tf | 18 + .../1-resman-tenants}/variables.tf | 11 + fast/addons/2-networking-ngfw/.fast-stage.env | 5 + fast/addons/2-networking-ngfw/README.md | 225 +++ .../2-networking-ngfw/diagram.excalidraw.gz | Bin 0 -> 44928 bytes fast/addons/2-networking-ngfw/diagram.png | Bin 0 -> 248457 bytes .../2-networking-ngfw/main.tf} | 28 +- fast/addons/2-networking-ngfw/ngfw.tf | 56 + fast/addons/2-networking-ngfw/outputs.tf | 47 + .../2-networking-ngfw/security-profiles.tf | 66 + .../2-networking-ngfw/tls-inspection.tf} | 64 +- .../2-networking-ngfw/variables-fast.tf | 65 + fast/addons/2-networking-ngfw/variables.tf | 245 +++ fast/addons/README.md | 42 + fast/extras/0-cicd-github/main.tf | 34 +- .../README.md | 40 - .../local-serverless-connector-outputs.tf | 50 - .../local-serverless-connector-variables.tf | 52 - .../local-serverless-connector.tf | 88 - fast/plugins/README.md | 25 - fast/stages/0-bootstrap/README.md | 72 +- fast/stages/0-bootstrap/automation.tf | 101 +- fast/stages/0-bootstrap/cicd.tf | 84 +- fast/stages/0-bootstrap/outputs-providers.tf | 99 + fast/stages/0-bootstrap/outputs.tf | 69 +- fast/stages/0-bootstrap/variables-addons.tf | 48 + fast/stages/0-bootstrap/variables.tf | 59 +- fast/stages/1-resman/README.md | 35 +- fast/stages/1-resman/iam.tf | 27 +- fast/stages/1-resman/main.tf | 17 +- fast/stages/1-resman/outputs-cicd.tf | 60 + fast/stages/1-resman/outputs-files.tf | 146 +- fast/stages/1-resman/outputs-providers.tf | 133 ++ .../1-resman/stage-2-network-security.tf | 79 - fast/stages/1-resman/stage-2-networking.tf | 29 +- .../1-resman/stage-2-project-factory.tf | 14 +- fast/stages/1-resman/stage-2-security.tf | 14 +- fast/stages/1-resman/stage-cicd.tf | 68 +- fast/stages/1-resman/variables-addons.tf | 51 + fast/stages/1-resman/variables-fast.tf | 2 + fast/stages/1-resman/variables-stages.tf | 12 - .../templates/workflow-github.yaml | 229 --- .../templates/workflow-gitlab.yaml | 106 -- .../stages/2-network-security/.fast-stage.env | 5 - fast/stages/2-network-security/README.md | 199 -- .../stages/2-network-security/data/cidrs.yaml | 18 - .../firewall-policy-rules/dev/egress.yaml | 24 - .../firewall-policy-rules/dev/ingress.yaml | 26 - .../firewall-policy-rules/prod/egress.yaml | 20 - .../firewall-policy-rules/prod/ingress.yaml | 25 - fast/stages/2-network-security/diagram.png | Bin 24228 -> 0 bytes fast/stages/2-network-security/diagram.svg | 1 - fast/stages/2-network-security/main.tf | 68 - fast/stages/2-network-security/net-dev.tf | 92 - fast/stages/2-network-security/net-prod.tf | 92 - fast/stages/2-network-security/outputs.tf | 77 - .../schemas/firewall-policy-rules.schema.json | 1 - .../2-network-security/variables-fast.tf | 110 -- fast/stages/2-network-security/variables.tf | 55 - fast/stages/2-networking-a-simple/README.md | 14 +- fast/stages/2-networking-a-simple/outputs.tf | 1 + fast/stages/2-networking-b-nva/README.md | 12 +- fast/stages/2-networking-b-nva/outputs.tf | 1 + .../2-networking-c-separate-envs/README.md | 14 +- .../2-networking-c-separate-envs/outputs.tf | 1 + fast/stages/2-security/.fast-stage.env | 1 - fast/stages/2-security/README.md | 34 +- fast/stages/2-security/cas.tf | 42 + fast/stages/2-security/main.tf | 4 +- fast/stages/2-security/outputs.tf | 23 +- fast/stages/2-security/variables.tf | 86 +- fast/stages/README.md | 5 +- tests/fast/__init__.py | 2 +- .../s1_tenant_factory => addons}/__init__.py | 2 +- .../a1_resman_tenants/__init__.py} | 8 +- .../a1_resman_tenants}/simple.tfvars | 14 + .../a1_resman_tenants}/simple.yaml | 0 .../a1_resman_tenants}/tftest.yaml | 2 +- .../a2_networking_ngfw}/__init__.py | 0 .../a2_networking_ngfw/data/ca.cert.pem | 36 + .../data/example.com.cert.pem | 38 + .../data/intermediate.cert.pem | 35 + .../addons/a2_networking_ngfw/simple.tfvars | 87 + .../a2_networking_ngfw}/simple.yaml | 22 +- .../addons/a2_networking_ngfw/tftest.yaml | 22 + tests/fast/stages/__init__.py | 2 +- tests/fast/stages/s0_bootstrap/__init__.py | 2 +- tests/fast/stages/s0_bootstrap/cicd.tfvars | 58 + tests/fast/stages/s0_bootstrap/cicd.yaml | 359 ++++ .../s0_bootstrap/iam_by_principals.tfvars | 24 +- tests/fast/stages/s0_bootstrap/simple.tfvars | 22 +- tests/fast/stages/s0_bootstrap/simple.yaml | 47 +- .../fast/stages/s0_bootstrap/simple_sas.yaml | 2 +- tests/fast/stages/s0_bootstrap/tftest.yaml | 3 +- tests/fast/stages/s1_resman/__init__.py | 2 +- tests/fast/stages/s1_resman/simple.tfvars | 43 +- tests/fast/stages/s1_resman/simple.yaml | 1673 +---------------- .../stages/s2_network_security/simple.tfvars | 25 - .../stages/s2_network_security/tls.tfvars | 43 - .../fast/stages/s2_network_security/tls.yaml | 325 ---- .../stages/s2_networking_a_simple/__init__.py | 2 +- .../stages/s2_networking_a_simple/ncc.yaml | 9 +- .../stages/s2_networking_a_simple/simple.yaml | 9 +- .../stages/s2_networking_a_simple/tftest.yaml | 8 +- .../stages/s2_networking_a_simple/vpn.yaml | 9 +- .../stages/s2_networking_b_nva/ncc-ra.yaml | 9 +- .../stages/s2_networking_b_nva/regional.yaml | 9 +- .../stages/s2_networking_b_nva/simple.yaml | 9 +- .../stages/s2_networking_b_nva/tftest.yaml | 8 +- .../stages/s2_project_factory/tftest.yaml | 2 +- tests/fast/stages/s2_security/simple.tfvars | 19 + tests/fast/stages/s2_security/simple.yaml | 340 +--- tests/fast/stages/s2_security/tftest.yaml | 2 +- tests/fast/stages/s3_gcve_dev/__init__.py | 2 +- tests/fast/stages/s3_gke_dev/tftest.yaml | 2 +- tests/fixtures.py | 2 +- 138 files changed, 2538 insertions(+), 4649 deletions(-) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/.fast-stage.env (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/README.md (88%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/diagram-flow.png (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/diagram.png (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/identity-providers-defs.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/main.tf (94%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/outputs-files.tf (83%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/outputs-gcs.tf (94%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/outputs.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/templates/providers.tf.tpl (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/templates/providers_terraform.tf.tpl (100%) rename fast/{assets => addons/1-resman-tenants}/templates/workflow-github.yaml (100%) rename fast/{assets => addons/1-resman-tenants}/templates/workflow-gitlab.yaml (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-billing-iam.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-core.tf (94%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-fast-automation.tf (99%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-fast-cicd.tf (99%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-fast-identity-providers.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-fast-logging.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant-fast-vpcsc.tf (100%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/tenant.tf (96%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/variables-fast.tf (90%) rename fast/{stages/1-tenant-factory => addons/1-resman-tenants}/variables.tf (92%) create mode 100644 fast/addons/2-networking-ngfw/.fast-stage.env create mode 100644 fast/addons/2-networking-ngfw/README.md create mode 100644 fast/addons/2-networking-ngfw/diagram.excalidraw.gz create mode 100644 fast/addons/2-networking-ngfw/diagram.png rename fast/{assets/templates/providers.tf.tpl => addons/2-networking-ngfw/main.tf} (55%) create mode 100644 fast/addons/2-networking-ngfw/ngfw.tf create mode 100644 fast/addons/2-networking-ngfw/outputs.tf create mode 100644 fast/addons/2-networking-ngfw/security-profiles.tf rename fast/{stages/2-security/certs.tf => addons/2-networking-ngfw/tls-inspection.tf} (57%) create mode 100644 fast/addons/2-networking-ngfw/variables-fast.tf create mode 100644 fast/addons/2-networking-ngfw/variables.tf create mode 100644 fast/addons/README.md delete mode 100644 fast/plugins/2-networking-serverless-connector/README.md delete mode 100644 fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf delete mode 100644 fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf delete mode 100644 fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf delete mode 100644 fast/plugins/README.md create mode 100644 fast/stages/0-bootstrap/outputs-providers.tf create mode 100644 fast/stages/0-bootstrap/variables-addons.tf create mode 100644 fast/stages/1-resman/outputs-cicd.tf create mode 100644 fast/stages/1-resman/outputs-providers.tf delete mode 100644 fast/stages/1-resman/stage-2-network-security.tf create mode 100644 fast/stages/1-resman/variables-addons.tf delete mode 100644 fast/stages/1-tenant-factory/templates/workflow-github.yaml delete mode 100644 fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml delete mode 100644 fast/stages/2-network-security/.fast-stage.env delete mode 100644 fast/stages/2-network-security/README.md delete mode 100644 fast/stages/2-network-security/data/cidrs.yaml delete mode 100644 fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml delete mode 100644 fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml delete mode 100644 fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml delete mode 100644 fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml delete mode 100644 fast/stages/2-network-security/diagram.png delete mode 100644 fast/stages/2-network-security/diagram.svg delete mode 100644 fast/stages/2-network-security/main.tf delete mode 100644 fast/stages/2-network-security/net-dev.tf delete mode 100644 fast/stages/2-network-security/net-prod.tf delete mode 100644 fast/stages/2-network-security/outputs.tf delete mode 120000 fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json delete mode 100644 fast/stages/2-network-security/variables-fast.tf delete mode 100644 fast/stages/2-network-security/variables.tf create mode 100644 fast/stages/2-security/cas.tf rename tests/fast/{stages/s1_tenant_factory => addons}/__init__.py (95%) rename tests/fast/{stages/s2_network_security/tftest.yaml => addons/a1_resman_tenants/__init__.py} (85%) rename tests/fast/{stages/s1_tenant_factory => addons/a1_resman_tenants}/simple.tfvars (93%) rename tests/fast/{stages/s1_tenant_factory => addons/a1_resman_tenants}/simple.yaml (100%) rename tests/fast/{stages/s1_tenant_factory => addons/a1_resman_tenants}/tftest.yaml (94%) rename tests/fast/{stages/s2_network_security => addons/a2_networking_ngfw}/__init__.py (100%) create mode 100644 tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem create mode 100644 tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem create mode 100644 tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem create mode 100644 tests/fast/addons/a2_networking_ngfw/simple.tfvars rename tests/fast/{stages/s2_network_security => addons/a2_networking_ngfw}/simple.yaml (56%) create mode 100644 tests/fast/addons/a2_networking_ngfw/tftest.yaml create mode 100644 tests/fast/stages/s0_bootstrap/cicd.tfvars create mode 100644 tests/fast/stages/s0_bootstrap/cicd.yaml delete mode 100644 tests/fast/stages/s2_network_security/simple.tfvars delete mode 100644 tests/fast/stages/s2_network_security/tls.tfvars delete mode 100644 tests/fast/stages/s2_network_security/tls.yaml diff --git a/fast/README.md b/fast/README.md index 9da748db2..57bb02f20 100644 --- a/fast/README.md +++ b/fast/README.md @@ -38,9 +38,11 @@ FAST uses YAML-based factories to deploy subnets and firewall rules and, as its One of our objectives with FAST is to provide a lightweight reference design for the IaC repositories, and a built-in implementation for running our code in automated pipelines. Our CI/CD approach leverages [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation), and provides sample workflow configurations for several major providers. Refer to the [CI/CD section in the bootstrap stage](./stages/0-bootstrap/README.md#cicd) for more details. We also provide separate [optional small stages](./extras/) to help you configure your CI/CD provider. + + ### Multitenant organizations -FAST has built-in support for multitenancy implemented in [an optional stage 1](./stages/1-tenant-factory/). Tenants can optionally be created with FAST compatibility, allowing them independent use of stages 1+ in their own context. +FAST has built-in support for multitenancy implemented in [an add-on stage](./addons/1-resman-tenants/). Tenants can optionally be created with FAST compatibility, allowing them independent use of stages 1+ in their own context. The following diagram is a high-level overview of stages used with multitenancy. @@ -64,12 +66,3 @@ Since we expect users to customize FAST to their specific needs, we strive to ma We also recognize that FAST users don't need all of its features. Therefore, you don't need to use our project factory or our GKE implementation if you don't want to. Instead, remove those stages or pieces of code and keep what suits you. Those familiar with Python will note that FAST follows many of the maxims in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/#id2). - -## Roadmap - -Besides the features already described, FAST also includes: - -- Stage to deploy environment-specific multitenant GKE clusters following Google's best practices -- Stage to deploy a fully featured data platform -- Reference implementation to use FAST in CI/CD pipelines -- Static policy enforcement (planned) diff --git a/fast/stages/1-tenant-factory/.fast-stage.env b/fast/addons/1-resman-tenants/.fast-stage.env similarity index 100% rename from fast/stages/1-tenant-factory/.fast-stage.env rename to fast/addons/1-resman-tenants/.fast-stage.env diff --git a/fast/stages/1-tenant-factory/README.md b/fast/addons/1-resman-tenants/README.md similarity index 88% rename from fast/stages/1-tenant-factory/README.md rename to fast/addons/1-resman-tenants/README.md index adb91c678..d40422072 100644 --- a/fast/stages/1-tenant-factory/README.md +++ b/fast/addons/1-resman-tenants/README.md @@ -1,6 +1,6 @@ -# Tenant Factory +# Tenant Factory Resource Manager Add-on -This optional stage implements multitenancy, where a limited number of tenants need a high degree of autonomy over their slice of the shared organization, while still being subject to a measure of central control. +This add-on implements multitenancy on top of the resource management stage, where a limited number of tenants need a high degree of autonomy over their slice of the shared organization, while still being subject to a measure of central control. Typical use cases include large organizations managing a single Cloud subscription for multiple semi-independent entities (governments, state-wide associations), multinational groups with different local subsidiaries, or even business units who own their cloud presence while still consuming centralized resources or services. @@ -83,13 +83,13 @@ Once a FAST-enabled tenant is created, the admin principal for the tenant has ac ## How to run this stage -This stage uses a similar configuration to the [resource management](../1-resman/README.md) stage, with the only differences being the backend used, and the configuration of the specific variables that drive tenant creation. +This stage is designed as an add-on to the [resource management](../../stages/1-resman/README.md) stage, and reuses its IaC resources and IAM configurations. -The only real prerequisite is having fully deployed the [bootstrap](../0-bootstrap) stage, but there's no need to run [resource management](../1-resman/) before creating tenants unless a top-level "Tenants" folder is needed (and even that can be created by hand removing the dependency on stage 1). +Once the bootstrap and resource management stages are applied, configure the bootstrap stage `fast_addon` variable to enable this stage, as explained in the [add-ons documentation](../README.md). ### Provider and Terraform variables -As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. @@ -145,7 +145,7 @@ Note that the `outputs_location` variable is disabled by default, if you want ou outputs_location = "~/fast-config" ``` -For additional details on output files and how they are used, refer to the [bootstrap stage documentation](../0-bootstrap/README.md#output-files-and-cross-stage-variables). +For additional details on output files and how they are used, refer to the [bootstrap stage documentation](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables). ### Running the stage @@ -218,7 +218,7 @@ tenant_configs = { FAST compatibility is enabled for a tenant by defining the `fast_config` attribute in their configuration, in addition to the attributes outlined above. -The `fast_config` attributes control the FAST bootstrap emulation for a tenant, and behave in a similar way to the corresponding variables that control the [bootstrap stage](../0-bootstrap/README.md#variables). They are all optional, and their behaviour is explained in the bootstrap stage documentation. +The `fast_config` attributes control the FAST bootstrap emulation for a tenant, and behave in a similar way to the corresponding variables that control the [bootstrap stage](../../stages/0-bootstrap/README.md#variables). They are all optional, and their behaviour is explained in the bootstrap stage documentation. This is an example of two FAST-enabled tenants: @@ -328,17 +328,19 @@ gcloud storage cp gs://{prefix}-{tenant-shortname}-prod-iac-core-0/tfvars/0-boot |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [logging](variables-fast.tf#L97) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [org_policy_tags](variables-fast.tf#L116) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap | -| [organization](variables-fast.tf#L106) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L133) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L69) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L115) | Logging resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [org_policy_tags](variables-fast.tf#L134) | Organization policy tags. | object({…}) | ✓ | | 0-bootstrap | +| [organization](variables-fast.tf#L124) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L151) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [groups](variables-fast.tf#L69) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L84) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [outputs_location](variables.tf#L17) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [root_node](variables.tf#L23) | Root folder under which tenants are created, in folders/nnnn format. Defaults to the organization if null. | string | | null | | -| [tag_names](variables.tf#L36) | Customized names for resource management tags. | object({…}) | | {} | | -| [tenant_configs](variables.tf#L49) | Tenant configurations. Keys are the short names used for naming resources and should not be changed once defined. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L87) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L102) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [names](variables.tf#L18) | Configuration for names used for resources and output files. | object({…}) | | {} | | +| [outputs_location](variables.tf#L28) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [root_node](variables.tf#L34) | Root folder under which tenants are created, in folders/nnnn format. Defaults to the organization if null. | string | | null | | +| [tag_names](variables.tf#L47) | Customized names for resource management tags. | object({…}) | | {} | | +| [tenant_configs](variables.tf#L60) | Tenant configurations. Keys are the short names used for naming resources and should not be changed once defined. | map(object({…})) | | {} | | ## Outputs diff --git a/fast/stages/1-tenant-factory/diagram-flow.png b/fast/addons/1-resman-tenants/diagram-flow.png similarity index 100% rename from fast/stages/1-tenant-factory/diagram-flow.png rename to fast/addons/1-resman-tenants/diagram-flow.png diff --git a/fast/stages/1-tenant-factory/diagram.png b/fast/addons/1-resman-tenants/diagram.png similarity index 100% rename from fast/stages/1-tenant-factory/diagram.png rename to fast/addons/1-resman-tenants/diagram.png diff --git a/fast/stages/1-tenant-factory/identity-providers-defs.tf b/fast/addons/1-resman-tenants/identity-providers-defs.tf similarity index 100% rename from fast/stages/1-tenant-factory/identity-providers-defs.tf rename to fast/addons/1-resman-tenants/identity-providers-defs.tf diff --git a/fast/stages/1-tenant-factory/main.tf b/fast/addons/1-resman-tenants/main.tf similarity index 94% rename from fast/stages/1-tenant-factory/main.tf rename to fast/addons/1-resman-tenants/main.tf index 4d07c34af..89ecb32fe 100644 --- a/fast/stages/1-tenant-factory/main.tf +++ b/fast/addons/1-resman-tenants/main.tf @@ -15,6 +15,9 @@ */ locals { + default_environment = [ + for k, v in var.environments : v if v.is_default == true + ][0] tenants = { for k, v in var.tenant_configs : k => merge(v, { billing_account = merge(v.billing_account, { diff --git a/fast/stages/1-tenant-factory/outputs-files.tf b/fast/addons/1-resman-tenants/outputs-files.tf similarity index 83% rename from fast/stages/1-tenant-factory/outputs-files.tf rename to fast/addons/1-resman-tenants/outputs-files.tf index 6734c934a..047831acd 100644 --- a/fast/stages/1-tenant-factory/outputs-files.tf +++ b/fast/addons/1-resman-tenants/outputs-files.tf @@ -21,7 +21,7 @@ resource "local_file" "providers-simple" { for k, v in local.tenants : k => local.tenant_data[k] } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/providers/tenant-${each.key}.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/providers/${var.names.output_files_prefix}-${each.key}.tf" content = templatefile(local._tpl_providers, { backend_extra = null bucket = each.value.gcs_bucket @@ -35,21 +35,21 @@ resource "local_file" "tfvars-simple" { for k, v in local.tenants : k => local.tenant_data[k] } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/tenant-${each.key}.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/${var.names.output_files_prefix}-${each.key}.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "providers" { for_each = var.outputs_location == null ? {} : local.tenant_providers file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/providers/1-resman-providers.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/providers/1-resman-providers.tf" content = try(each.value, null) } resource "local_file" "providers-r" { for_each = var.outputs_location == null ? {} : local.tenant_providers_r file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/providers/1-resman-r-providers.tf" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/providers/1-resman-r-providers.tf" content = try(each.value, null) } @@ -59,20 +59,20 @@ resource "local_file" "tfvars" { for k, v in local.tenant_tfvars : k => v if var.outputs_location != null } file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/tfvars/0-bootstrap.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/tfvars/0-bootstrap.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "tfvars_globals" { for_each = var.outputs_location == null ? {} : local.tenant_globals file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/tfvars/0-globals.auto.tfvars.json" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/tfvars/0-globals.auto.tfvars.json" content = jsonencode(each.value) } resource "local_file" "workflows" { for_each = var.outputs_location == null ? {} : local.tenant_cicd_workflows file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tenants/${each.key}/workflows/1-resman-workflow.yaml" + filename = "${try(pathexpand(var.outputs_location), "")}/${var.names.output_files_prefix}/${each.key}/workflows/1-resman-workflow.yaml" content = try(each.value, null) } diff --git a/fast/stages/1-tenant-factory/outputs-gcs.tf b/fast/addons/1-resman-tenants/outputs-gcs.tf similarity index 94% rename from fast/stages/1-tenant-factory/outputs-gcs.tf rename to fast/addons/1-resman-tenants/outputs-gcs.tf index 7c20876d2..23dc34292 100644 --- a/fast/stages/1-tenant-factory/outputs-gcs.tf +++ b/fast/addons/1-resman-tenants/outputs-gcs.tf @@ -21,7 +21,7 @@ resource "google_storage_bucket_object" "providers-simple" { for k, v in local.tenants : k => local.tenant_data[k] } bucket = var.automation.outputs_bucket - name = "providers/tenant-${each.key}.tf" + name = "providers/${var.names.output_files_prefix}-${each.key}.tf" content = templatefile(local._tpl_providers, { backend_extra = null bucket = each.value.gcs_bucket @@ -35,7 +35,7 @@ resource "google_storage_bucket_object" "tfvars-simple" { for k, v in local.tenants : k => local.tenant_data[k] } bucket = var.automation.outputs_bucket - name = "tfvars/tenant-${each.key}.auto.tfvars.json" + name = "tfvars/${var.names.output_files_prefix}-${each.key}.auto.tfvars.json" content = jsonencode(each.value) } diff --git a/fast/stages/1-tenant-factory/outputs.tf b/fast/addons/1-resman-tenants/outputs.tf similarity index 100% rename from fast/stages/1-tenant-factory/outputs.tf rename to fast/addons/1-resman-tenants/outputs.tf diff --git a/fast/stages/1-tenant-factory/templates/providers.tf.tpl b/fast/addons/1-resman-tenants/templates/providers.tf.tpl similarity index 100% rename from fast/stages/1-tenant-factory/templates/providers.tf.tpl rename to fast/addons/1-resman-tenants/templates/providers.tf.tpl diff --git a/fast/stages/1-tenant-factory/templates/providers_terraform.tf.tpl b/fast/addons/1-resman-tenants/templates/providers_terraform.tf.tpl similarity index 100% rename from fast/stages/1-tenant-factory/templates/providers_terraform.tf.tpl rename to fast/addons/1-resman-tenants/templates/providers_terraform.tf.tpl diff --git a/fast/assets/templates/workflow-github.yaml b/fast/addons/1-resman-tenants/templates/workflow-github.yaml similarity index 100% rename from fast/assets/templates/workflow-github.yaml rename to fast/addons/1-resman-tenants/templates/workflow-github.yaml diff --git a/fast/assets/templates/workflow-gitlab.yaml b/fast/addons/1-resman-tenants/templates/workflow-gitlab.yaml similarity index 100% rename from fast/assets/templates/workflow-gitlab.yaml rename to fast/addons/1-resman-tenants/templates/workflow-gitlab.yaml diff --git a/fast/stages/1-tenant-factory/tenant-billing-iam.tf b/fast/addons/1-resman-tenants/tenant-billing-iam.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-billing-iam.tf rename to fast/addons/1-resman-tenants/tenant-billing-iam.tf diff --git a/fast/stages/1-tenant-factory/tenant-core.tf b/fast/addons/1-resman-tenants/tenant-core.tf similarity index 94% rename from fast/stages/1-tenant-factory/tenant-core.tf rename to fast/addons/1-resman-tenants/tenant-core.tf index c4d3086c9..994cdf059 100644 --- a/fast/stages/1-tenant-factory/tenant-core.tf +++ b/fast/addons/1-resman-tenants/tenant-core.tf @@ -25,7 +25,7 @@ module "tenant-core-logbucket" { for_each = local.tenants parent_type = "project" parent = var.logging.project_id - id = "tn-${each.key}-audit" + id = "${var.names.resource_short_name}-${each.key}-audit" location = var.locations.logging log_analytics = { enable = true } } @@ -36,7 +36,7 @@ module "tenant-core-folder" { parent = local.root_node name = "${each.value.descriptive_name} Core" logging_sinks = { - "tn-${each.key}-audit" = { + "${var.names.resource_short_name}-${each.key}-audit" = { destination = module.tenant-core-logbucket[each.key].id filter = <<-FILTER log_id("cloudaudit.googleapis.com/activity") OR diff --git a/fast/stages/1-tenant-factory/tenant-fast-automation.tf b/fast/addons/1-resman-tenants/tenant-fast-automation.tf similarity index 99% rename from fast/stages/1-tenant-factory/tenant-fast-automation.tf rename to fast/addons/1-resman-tenants/tenant-fast-automation.tf index 8873f9cff..bf37dbdc2 100644 --- a/fast/stages/1-tenant-factory/tenant-fast-automation.tf +++ b/fast/addons/1-resman-tenants/tenant-fast-automation.tf @@ -26,7 +26,7 @@ locals { } fast_tenants = { for k, v in local._fast_tenants : k => merge(v, { - stage_0_prefix = "${v.prefix}-prod" + stage_0_prefix = "${v.prefix}-${local.default_environment.short_name}" principals = { for gk, gv in v.groups : gk => ( can(regex("^[a-zA-Z]+:", gv)) diff --git a/fast/stages/1-tenant-factory/tenant-fast-cicd.tf b/fast/addons/1-resman-tenants/tenant-fast-cicd.tf similarity index 99% rename from fast/stages/1-tenant-factory/tenant-fast-cicd.tf rename to fast/addons/1-resman-tenants/tenant-fast-cicd.tf index 099083177..8a1d68905 100644 --- a/fast/stages/1-tenant-factory/tenant-fast-cicd.tf +++ b/fast/addons/1-resman-tenants/tenant-fast-cicd.tf @@ -131,7 +131,7 @@ module "automation-tf-cicd-r-sa" { source = "../../../modules/iam-service-account" for_each = local.cicd_repositories project_id = var.automation.project_id - name = "tenant-${each.key}-1r" + name = "${each.key}-1r" display_name = "Terraform CI/CD ${each.key} service account (read-only)." prefix = var.prefix iam = { diff --git a/fast/stages/1-tenant-factory/tenant-fast-identity-providers.tf b/fast/addons/1-resman-tenants/tenant-fast-identity-providers.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-identity-providers.tf rename to fast/addons/1-resman-tenants/tenant-fast-identity-providers.tf diff --git a/fast/stages/1-tenant-factory/tenant-fast-logging.tf b/fast/addons/1-resman-tenants/tenant-fast-logging.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-logging.tf rename to fast/addons/1-resman-tenants/tenant-fast-logging.tf diff --git a/fast/stages/1-tenant-factory/tenant-fast-vpcsc.tf b/fast/addons/1-resman-tenants/tenant-fast-vpcsc.tf similarity index 100% rename from fast/stages/1-tenant-factory/tenant-fast-vpcsc.tf rename to fast/addons/1-resman-tenants/tenant-fast-vpcsc.tf diff --git a/fast/stages/1-tenant-factory/tenant.tf b/fast/addons/1-resman-tenants/tenant.tf similarity index 96% rename from fast/stages/1-tenant-factory/tenant.tf rename to fast/addons/1-resman-tenants/tenant.tf index b143639f2..f53fb0338 100644 --- a/fast/stages/1-tenant-factory/tenant.tf +++ b/fast/addons/1-resman-tenants/tenant.tf @@ -97,7 +97,7 @@ module "tenant-sa" { source = "../../../modules/iam-service-account" for_each = local.tenants project_id = var.automation.project_id - name = "tn-${each.key}-0" + name = "${var.names.resource_short_name}-${each.key}-0" display_name = "Terraform tenant ${each.key} service account." prefix = var.prefix iam = { @@ -114,7 +114,7 @@ module "tenant-gcs" { source = "../../../modules/gcs" for_each = local.tenants project_id = var.automation.project_id - name = "tn-${each.key}-0" + name = "${var.names.resource_short_name}-${each.key}-0" prefix = var.prefix location = each.value.locations.gcs versioning = true diff --git a/fast/stages/1-tenant-factory/variables-fast.tf b/fast/addons/1-resman-tenants/variables-fast.tf similarity index 90% rename from fast/stages/1-tenant-factory/variables-fast.tf rename to fast/addons/1-resman-tenants/variables-fast.tf index e8179a2bb..ed246957f 100644 --- a/fast/stages/1-tenant-factory/variables-fast.tf +++ b/fast/addons/1-resman-tenants/variables-fast.tf @@ -66,6 +66,24 @@ variable "custom_roles" { default = null } +variable "environments" { + # tfdoc:variable:source 0-globals + description = "Environment names." + type = map(object({ + name = string + short_name = string + tag_name = string + is_default = optional(bool, false) + })) + nullable = false + validation { + condition = anytrue([ + for k, v in var.environments : v.is_default == true + ]) + error_message = "At least one environment should be marked as default." + } +} + variable "groups" { # tfdoc:variable:source 0-bootstrap # https://cloud.google.com/docs/enterprise/setup-checklist diff --git a/fast/stages/1-tenant-factory/variables.tf b/fast/addons/1-resman-tenants/variables.tf similarity index 92% rename from fast/stages/1-tenant-factory/variables.tf rename to fast/addons/1-resman-tenants/variables.tf index 210635f67..d9af1de91 100644 --- a/fast/stages/1-tenant-factory/variables.tf +++ b/fast/addons/1-resman-tenants/variables.tf @@ -14,6 +14,17 @@ * limitations under the License. */ +# TODO: backport names variable from resman stage +variable "names" { + description = "Configuration for names used for resources and output files." + type = object({ + output_files_prefix = optional(string, "2-resman-tenants") + resource_short_name = optional(string, "tn") + }) + nullable = false + default = {} +} + variable "outputs_location" { description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." type = string diff --git a/fast/addons/2-networking-ngfw/.fast-stage.env b/fast/addons/2-networking-ngfw/.fast-stage.env new file mode 100644 index 000000000..ae26e9fcb --- /dev/null +++ b/fast/addons/2-networking-ngfw/.fast-stage.env @@ -0,0 +1,5 @@ +FAST_STAGE_DESCRIPTION="NGFW Enterprise networking add-on" +FAST_STAGE_LEVEL=2 +FAST_STAGE_NAME=networking-ngfw +FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman 2-networking" +FAST_STAGE_OPTIONAL="2-security" \ No newline at end of file diff --git a/fast/addons/2-networking-ngfw/README.md b/fast/addons/2-networking-ngfw/README.md new file mode 100644 index 000000000..0cbcf7ead --- /dev/null +++ b/fast/addons/2-networking-ngfw/README.md @@ -0,0 +1,225 @@ +# NGFW Enterprise Networking Add-on + +This add-on includes all configurations and resources required to activate [Cloud Next Generation Firewall](https://cloud.google.com/firewall/docs/about-firewalls), and associate its endpoints to an arbitrary number of VPC networks. + +This diagram shows the resources used by this add-on, and their relationships with its networking parent stage. + +

+ Network security NGFW diagram +

+ + +- [Design overview and choices](#design-overview-and-choices) +- [How to run this stage](#how-to-run-this-stage) + - [Provider and Terraform variables](#provider-and-terraform-variables) + - [Impersonating the automation service account](#impersonating-the-automation-service-account) + - [Variable configuration](#variable-configuration) + - [Running the stage](#running-the-stage) + - [Using add-on resources from the networking stage](#using-add-on-resources-from-the-networking-stage) +- [Files](#files) +- [Variables](#variables) + + +## Design overview and choices + +This add-on is intentionally self-contained to allow directly using it to implement different designs, via a single instance or multiple instances. + +All project-level resources in this stage with the exception of VPC associations are created in the same project, so that dependencies and IAM configurations are kept as simple as possible, and everything is within the same span of control. + +The controlling project is usually one of those already created and managed by the networking stage: the landing host project, or a shared environment project if that exists. Alternatively, a dedicated project can be created and used here provided the necessary IAM and organization policies configurations are also defined. + +## How to run this stage + +Once the main networking stage has been configured and applied, the following configuration is added the the resource management `fast_addon` variable to create the add-on provider files, and its optional CI/CD resources if those are also required. The add-on name (`networking-ngfw`) is customizable, in case the add-on needs to be run multiple times for example to create different sets of endpoints and NGFW configurations per environment. + +```hcl +fast_addon = { + networking-ngfw = { + parent_stage = "2-networking" + # cicd_config = { + # identity_provider = "github-test" + # repository = { + # name = "test/ngfw" + # type = "github" + # branch = "main" + # } + # } + } +} +``` + +### Provider and Terraform variables + +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. + +The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following example uses local files but GCS behaves identically. + +```bash +../../stages/fast-links.sh ~/fast-config +# File linking commands for NGFW Enterprise networking add-on stage + +# provider file +ln -s ~/fast-config/providers/2-networking-ngfw-providers.tf ./ + +# input files from other stages +ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json ./ + +# conventional place for stage tfvars (manually created) +ln -s ~/fast-config/2-networking-ngfw.auto.tfvars ./ + +# optional files +ln -s ~/fast-config/tfvars/2-security.auto.tfvars.json ./ +``` + +### Impersonating the automation service account + +The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. + +### Variable configuration + +Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: + +- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above +- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-bootstrap.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above +- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file + +The first two sets are defined in the `variables-fast.tf` file, the latter set in the `variables.tf` file. The full list of variables can be found in the [Variables](#variables) table at the bottom of this document. + +Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../../stages/0-bootstrap/README.md#output-files-and-cross-stage-variables) for more details: + +```tfvars +outputs_location = "~/fast-config" +``` + +Once output files are in place, define your addon configuration in a tfvars file. This is an example of configuring this addon, with optional variable attributes filled in for illustration purposes. + +```hcl +certificate_authorities = { + # if CA pools defined in the security stage are used this is optional + ngfw-0 = { + location = "europe-west8" + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "example.org" + organization = "Test Organization" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + } +} +ngfw_config = { + name = "ngfw-0" + endpoint_zones = ["europe-west8-b"] + network_associations = { + prod = { + # VPC ids defined in the network stage can be referred to via short name + # vpc_id = "prod-spoke-0" + vpc_id = "projects/xxx-prod-net-spoke-0/global/networks/prod-spoke-0" + tls_inspection_policy = "ngfw-0" + } + } +} +outputs_location = "~/fast-config" +project_id = "xxx-prod-net-landing-0" +security_profiles = { + ngfw-0 = { + # these are optional and shown here for convenience + threat_prevention_profile = { + severity_overrides = { + informational-allow = { + action = "ALLOW" + severity = "INFORMATIONAL" + } + } + threat_overrides = { + allow-280647 = { + action = "ALLOW" + threat_id = "280647" + } + } + } + } +} +tls_inspection_policies = { + ngfw-0 = { + # reference the pool defined above, or an external one + # CA pools defined in the security stage can be referred to via short name + ca_pool_id = "ngfw-0" + location = "europe-west8" + trust_config = "ngfw-0" + } +} +trust_configs = { + ngfw-0 = { + location = "europe-west8" + allowlisted_certificates = { + server-0 = "~/fast-config/data/2-networking-ngfw/server-0.cert.pem" + } + trust_stores = { + ludo-joonix = { + intermediate_cas = { + issuing-ca-1 = "~/fast-config/data/2-networking-ngfw/intermediate.cert.pem" + } + trust_anchors = { + root-ca-1 = "~/fast-config/data/2-networking-ngfw/ca.cert.pem" + } + } + } + } +} +``` + +### Running the stage + +Once provider and variable values are in place and the correct user is configured, the stage can be run: + +```bash +terraform init +terraform apply +``` + +### Using add-on resources from the networking stage + +Security profiles group defined here are exported via output variable file, and can be consumed in the firewall policies defined in the networking stage. + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [main.tf](./main.tf) | Module-level locals and resources. | project | | +| [ngfw.tf](./ngfw.tf) | NGFW Enteprise resources. | | google_network_security_firewall_endpoint · google_network_security_firewall_endpoint_association | +| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | +| [security-profiles.tf](./security-profiles.tf) | Organization-level network security profiles. | | google_network_security_security_profile · google_network_security_security_profile_group | +| [tls-inspection.tf](./tls-inspection.tf) | TLS inspection policies and supporting resources. | certificate-authority-service | google_certificate_manager_trust_config · google_network_security_tls_inspection_policy | +| [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| [automation](variables-fast.tf#L28) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [ngfw_config](variables.tf#L102) | Configuration for NGFW Enterprise endpoints. Billing project defaults to the automation project. Network and TLS inspection policy ids support interpolation. | object({…}) | ✓ | | | +| [organization](variables-fast.tf#L48) | Organization details. | object({…}) | ✓ | | 0-globals | +| [project_id](variables.tf#L123) | Project where the network security resources will be created. | string | ✓ | | | +| [_fast_debug](variables-fast.tf#L19) | Internal FAST variable used for testing and debugging. Do not use. | object({…}) | | {} | | +| [certificate_authorities](variables.tf#L17) | Certificate Authority Service pool and CAs. If host project ids is null identical pools and CAs are created in every host project. | map(object({…})) | | {} | | +| [certificate_authority_pools](variables-fast.tf#L36) | Certificate authority pools. | map(object({…})) | | {} | 2-security | +| [names](variables.tf#L93) | Configuration for names used for output files. | object({…}) | | {} | | +| [outputs_location](variables.tf#L117) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [security_profiles](variables.tf#L129) | Security profile groups for Layer 7 inspection. Null environment list means all environments. | map(object({…})) | | {…} | | +| [tls_inspection_policies](variables.tf#L171) | TLS inspection policies configuration. CA pools, trust configs and host project ids support interpolation. | map(object({…})) | | {} | | +| [trust_configs](variables.tf#L213) | Certificate Manager trust configurations for TLS inspection policies. Project ids and region can reference keys in the relevant FAST variables. | map(object({…})) | | {…} | | +| [vpc_self_links](variables-fast.tf#L58) | VPC network self links. | map(string) | | {} | 2-networking | + diff --git a/fast/addons/2-networking-ngfw/diagram.excalidraw.gz b/fast/addons/2-networking-ngfw/diagram.excalidraw.gz new file mode 100644 index 0000000000000000000000000000000000000000..77a1dbaea98ab7e7dc95b2354047fc6be6cfcf31 GIT binary patch literal 44928 zcmV)EK)}BriwFomQ+j6r17vAoXL4a}E@gOQVQgt+a$$D>>|I%N;!3i9pI_nV`*J7Z zSf?s$KX0}%-q&ug_e>BN1jt~7#b$eA{`<|MZ6uHc!pI(XG=Au|L5Ny1zx=+etkS># zv9|WD_uO&6{k8VZeX5jOUNtBWzx`ofAGkr+^V|3qjQ;NW{h%_sveD~xx_|xoXZQ)H z;y?WSl-qJ2+;*>v*Zm*h&rw6y?SDIlL{t+3pDMw0fP0W!dI`h+q1kCawO1W?p z5g-l2D+I)R2tYYpDW)h;p@Fct3jsJvb0C6Hsi|gq)yPtU{1HGccf@AY2e*XV)@-m>hq z-C!?E$npcP?zPLU9~rwtGD~l zm2-DP9z*6hstk3Q6wGL?I8`R}2s?^6&JIY4F_Lge4DfK8$8nS)iU^}5=L{`D@B|QG z76mvHoFL9f#mx!?l0l%$myV;98iaIVXe?sdH#x-O>o|~N@D)On@PZGIqYrd=w^1z> zXmS6#QQt4LuH2qjB#t6o;98rg-@HGLCbuR-992fn@FAQFge;*sa#C#~wmh>FjY7CyQ%FWisWAJJ8ruB!Zy=q(u2f(vdX-EMUzkk@V|E z&^f^>If8yvUaV(_fXuFI_CY4;dW{PW8vxj7A zFdzwI5@WIglj?*Bv4SAjn)f(ip7tCI$?bY=Sn9R9B@Bf%uipK;?Nr^tn$rpV>PH0V z3iiqkW`yVmuUd@;v#Ah0Cn`>d=xp7~Ct#oMUJfdorA=?JrT1mw=>9+jjqCxNae_*f zDvt0#sF}=mal#xf09iKC0!h3`kh-gQJsT9lSbKB(^mtJn@M6#`SKPDbr-R{e>n)Hh zf*tqE3u3}F1)wY`T_$8I%hyaOCDdnzb#KcQ>YLRI?v)RAE?VoydU&a_hV_SLt9RS< zp;#OS=7m4kb}n`vzhYQV?q!y+&a`p}NUb@ij73rzBL}9&!FD1YBZg>5Sn(mkA(oMI zE;KFxlU$!)>9R{^vYj_8H zJ45)2EFyXIvakqL^hgvax;90(CuRy3^gLML2n)bL8HGYYNJvQ6FGC|%xR8Jg3uN`R z)`D?KlhSyUM#_Aq5C8}jip+-FDM*}z8;&%D5e1+r z1vQaW>G*RXsqUEd z0F)9OMYKR3CuJn?kAfRgG-k6%ni13tk$2y4mkZ|DTPSXjB{Gvh^OSLrA_7$u0CmSA zERPyREAT60?jm5i{NgO9b`_n1P+$9!gXWsYp5O#L%4aoN6b9DqV1biu21K!LVquO zYGs#~0&E2ay9~C+~ILuU`njzmYU<9Q$V?DKWW{hJC zWq={vfD&;LiZdt~=Z2#R6GD^75T_1`9Ro;@sX>a&f3LN(1>mN?w6vs(*!lxP6Sshf zc<$>Bg>a^EYQ%>q%XfiLuF5jiO6^bFKEvC^C_`?#a&|S%glwZy2wPQb>{^o`VeLpn zwJ9w*7uY?iQFrL*ee#i+v*9w*-gVTOk_xQ=6(&j`twdaAa=QRWaXtV;qvQ;}q|EeH zvHRF3_uJA`%C%NST-<3Mht(9BiJc0 z@9OlC9Nu2vRhkXjkp~@dCM;6pTX@=kIO4QdN9D>uxkV^^zVyd`k#0d~m5Dh4!B3_I*DrMA^8Ag~ul9T`%YLRWSzka?Z zf0P(39p<$nS`(UlCkpiwIMt0qtYit`I-0q zWwN-*qnCj_Xas}X@3vQ4 zo8I!{OeRMiw_Uz#x&Iw9GAk84C*r51fr=9W&LEvD8c)3a=pkI;fLZ5U%R zo}ZNtv4=Map^%+m|308@X*TGtP1G%MO(a7@L~~>kc(@KVZZJ3;gCEiMfE#WLuor6Z zC*_mN``(Xo@9z6dw%kd#YXpple*G9)$oL>Xc- zM>GFK4Y_^a+Xd4o?Ukyx7mfDq4&_@fS+!oLP^k>6TZ6~LPEczSUiEjk?{m_6OerOF zm?K&4Sc$}5X3%<+P~jMRBm^-)DDcUxBT6U-T}p9#S{heLo@qVXU~e?~991S^t!J5o zo_;US++0A!NkRyh5Z~%NMNub2Ry z6Alv(l$g=6d{okfyl6FswJmyK>g%U%qX)cM&m5x-C`T}gc7Y67MvoS-u1%em+lLMfWO zSA(kbD6Q=bvClmpZk12sjF)-3ZPU#LIP=1_yPbJz-2! z#NwQniGMGbFDwxJaWIi{k@$uy`=&7I2fGO&F!*zwbAE#fX#~7$AJ)8*-I^D;!*Z*I z@v}P0g}ha$zW6vo|NM&VU``;;TA!P_=lRfw*)TdY$x2Z~2r9rPS#4$i6pj#lmxDlt zNHju4JjaUCU4xRg=pV1MrGN=Q!W_gHVA2pHZ6X6Q^a|mx41x7D%5{}pJ%$E+Mk$-hPR6mg51)lv!%$|#HEh(h%!NIV(& z0ol^oXt*|&-_?lPT1$o!0a-&rx^%G?eo}h$qu z28kr7&0cEc+IDRrTB!b0B2h;I5=?u}GAAlQBH3aXq6wiR^1~Bcb0v{p+ceiTQ;OV^ zBoYeR5fx2Lk7|)xLRcmMGgvzYqC{wTV z5k_r)|L%LIbl`U7#ZGO|3{J}z*^g{MHe=7$l6KkCXuDT!0zqu4K4@uA^hVtjJ(6L( zCz^wUbR6j#25x(%PJ%d+2qBtj!ti!>C1;T(mfs6Pc9)Ls+%P4yR*A@>W@sZ6R7L?O zpLiah@|#!3+qD~U-g??OyeK^Cz+_(iTq3y7yziNJ+$?xM?l*_S?TzDte|^sV=iEqx zLJpi$1#R-^Wgrb`A`VzBW9-RoicaQ3kVH6OxLpyoODQ7$6b;UpW7B&~F@ngE^z@t+ zX?ShZT#w&dAg`(b9h4`nm;jA$wh8MRP*h1o_?)w& z1T?i9K*O(C0m&qg>@S^HMTEd1ORT$>k!O*{9|U4>0!`jYRc=X)e3vH4wC5~4X)dZQ~i-uGo^C3>sP$dnQoJHBwygLyE(dVPUL@gJT0O3Zfgk&;tm=I|1%AQYa zG>um5tk7o?eU7pcne{pzO2D z1Sf(LY512^g*OAZ_m7WmuWOe(T)4&7)!EBU97|KrQ&-BtX+n}^_b2J8=iDQNI!ag$ zhU!=XjjO`d#>5=K^mu7isO@7WbV92BC(x$udElJbRnyNrQeAJ}-|Qcsl!Rwmr*}fG z_u8`c2_kFeeQ$2u1HaebxV+m7`~q_yc8{;Fa=5P~y^ErM(={8leeYpF+J$zbp6$Ld zFpr5s20U6*wUj7Ko|P=3P$4rzj0Bs)q@X9$OG+?@*up#v*%U1;;mk~XgMat&-`jtdYP)+A*(fo@c@Gl1VGmXfJIlANx$KM(Km^3Uv_TF6 z4}_Sx(^^~Zq9#-r5Sq~@vOvbedjKxc4upjiMvhyxkyW!mPAx#D1=uQzC9)-CgcGb^ z9KsTSklkR-+Ny$9zv>dgcqY%^?}3=j-l+UZZPDKKQm(u*UY2i2X8O`$1jVfbBb^pb zTbE=tAH)YD55fq?jdDQVA=%_HcA`c%tQ}zsvL}|4iwHKIzeD06z(akxNkBTBNT$}> zy;?c4t?k8!>(x&EM+L<61_Xx#h773NYOVB~eGxCo*Wa(YrUmwU9!h4GcEW!bc_^G@}#(PsdJM z2!aO@?MMZhumY{3>21zw_N(7S?2b&bJe?8?E|`(`J~Q}2-{RDSoSZoZ)lw`4IczbM3Qnv_kZ6LSAB~ zSHQ?+wZdCV#8mK%b=p+6p$-hlQUTprBi86*AoO`EivV=;?gNPQ`#jc;a?lim$^^*lis1@u&jj*75Nch z&IRC2Jjyj4S<(^Fb}39u!a+jvC&pj1ptX$)Kxrd-oxWM%_&Nc9+8zoeI#up&$5pYA zn`6zhs!9>Lpr zzzS4K80$6tkRN)ztbNa)^o}2gi9WvHSzf^FZzrn;mlrl}_aB!1L59uaL2uUZtUm6} zkx6;@53@(4S919hSV!mH;UB=hh4U+f>%2hJ#Ld7vVXaEvFK3S&7teE<1TFAHW-De_ zFYd24w;t=)M_?8<&Q6-kbGCT4g780&x<`6GMO4-Fn`%!Kjt-j=L}Gc`eL7n~lsEz- zIj6%*Zv)jJ)p zZH3P@4*b5oQn`!gtJ^tI7qjjAb@$`Fw${B@bvFiShTQA^*VKQ7-(Kq22XYrEfitxp z*sEJRAmT; zDaK(VRe;@e&bsZ^<5rW;$TymK zgwAXB@<{$cc`h&SPiN3Mkdff%APX#el?aFTItf)oTbIZ=ia2;3Y9)*a9S8!B#u;Sw z{wbx)FdFrdDIJ@3r<8UfoB)tLL*0Xrf@po!VY1IF$W%({viE8H-N7B&yIjiqS4P1~ zDJUpsoF|{oNM7-jj1;LM9?1gOp1i+S+(T783Cp*N{dcT5JfP@i4G~`_(?xDbq|tli zX>efCfGc&sV8v=9B60H2$d1+)e&u9!3ijI9{crCY3oM7^5U}GZ963&t*pAr%&W2w)DYYOpIEcHoIOPw7oGk~;Xb-1J5h-?%rX@&VP zjB)bR>0$Ty>GH>_F!xREH&(i9H#@FW_(`*dOGR@W!*`Q9O6l;EV!V=lilii_>5bK_ zZs~YBMF8-`V;T)ujkFNb1zMR$ndJDjPSI)c_>(2hgE~V`*on+EAbEXG`uM6F5KHgp z-uC<%kKsS4*`N9=>??aOuCn-2yLFz}B&q1qY3J#wbCVfh8sf4B15DWuR80gaKOH#K zgA&7G;N$4f665O^8@8G-1a~CT!4^I_ntqyU`dI5q&wX=r@5z%xoRcyXk4~=6fH@$S z>kN6_Per3Y=R1F#U0$voFYlb28_}$PzhBy25nE%B8YtkCrSPtzlu-C7i}+*!8v!eW zk{R!lgZwy{e#pV3vlhu%B%F)ggbqX?rU{{+_cZiio)ve<7+3P?jLFLBFaM4-pb?T5 zvg&*g4|N#LAtw_g&v(uLiX6}opO}{e8v2OUchTZLh7)zaQS81!TRm zyf#9FUI^iUhvp&l`)!HbuS%yLEGiTr0wGE-Wki+%rO_NA2qAU?i4mC)&l1XBaRt;A zvS(O`*66NVvgntCbj~jBCeV}cC@Qe^AyDC?1{>MQlKpV-d%w0HKG?C9i?-ywc0m;44Es~TrI zulG88xTZHYn*^Ef?ylF|X03R=B>}dG&c7lDXC0~m=^ZYQjf65>;cJZbp@yRZ@(?V& zYK0}Ita*;ZbOLdf9q4AD?NUKxp0X>``dhJ|%~tXwBQChj(TtG?FQ4yTF7EHGZXeyw z&K>OT9=J1?oHcQ#DiJn#@mWw)8VHq;G-pQ4##Ws z96mCMDwvP6sV(PkjN!<%LlFo~vo@Z#e6lqw_lHU&M{&|$>~OYAcxhTS1xD#$aU|Hh zR1U2xR4wdKenY3cG{G zo%yAs=Mr97$%t5|WgHIH4UE2&mItOYzm2EUNE?tVcQz7Ef+rJ#2r6*!ONlmpt$N1i zk-e}IMj@gOpMPhwRhw8hT7mRqOyxe{dUN~k?Ay`B4O0;!ZT-eW)r}A3w{yz5{~Z0%WS4tRPWP$R zxH-8!xoAB!l78|dIy`Y9?;+kcW6nw0hph$~q878d3Mo6E~jn_&Kj8BqQm`ET& z28%|TL`<|-@D+JWIG+rTcLKo$%mpA1h%k;7_XQ+}D^BS^zqxoR3KB9abGh=)gAY=5 zl~?X%$>z&sBb%Rra-!Zfb1l4 z7dH_yE@Dj<3RhJ~n{N;MZdY;akH@lm!A1LlaV9F#iXX?Im&74mx%(YcDJ^X6uKo}U zn`@8IeKs$r7q9!9bA|bqL&aSk!U-#hx7&e?A{~qfVyQ-!Pk}%S>U5Y%b$0c2Wb!4I zwX|GbggYKoCi!By2<@C3W7qlh`;%z3J8Sp5XPeJ>Z`UrjH|C0Rq^0J@%X|G$%pG^` zJ8q}8yo?9Cm2o7mj4h*@o^w3IPg#H?@k~f!KRm!v7~)E5;ACn`kcZ2Jc>1mkoJ`?j znG)+_IXNF{Mw$jdKkjMh$$Tr$k7zlF4P;341WI;)LmQ`2a6r4_835&XbBZFximx$C zxqG;JZ2cq&lNbmc;tYdQwmA=04exocsfGQzKHplf^XsoI+`PQMgTfw~HM)dBwg7~D zh3=Pyp*409y=73sY6r@uYkq+Hy#$W|Fj8BdD3rDY#zzFl1vktYtqe{Bq*<2Wy@G08 z!_|TWs6rOP$V?w?yhN`**YEu~7k&C|^YU&*-~M=9+rGG7uAiFD!s{4ZbnbOu-}kQW z;BoG5tESqG)BA_(hOkw+e$F*kRRtV=dj(v-buuEvOw5CecpVJpKu|=)TDX@3G&viU zKJt!WyHNBfpV`+mY~0#!zVl7P&HV@V9m6OhBIeLpvC~(Lb`y3A)bUu`N(Nd=v$1n5Z;_-1< zweDlAG@%^Ze<8FE3{_74UNuqDd`sAW zf6kCcBP_v%R;(eMzQ0H$K2m}$b;Dx{lQveG7e{Lchuxh``F8EjUQnzTR~!Je5yX-T zLeerNhk*P{SYsrKHfc^)(@_hwOio$Y5M$cgG>4lzV&`;iaj8>V+Wu~@AJ3kyI=^6i zDVw`II$L7^F35fat4h&jHfSg^*1`xeM!L3f<5u@p-!Ja;&c*yb9KhkyeBq+fc{zV5 z7FOr$cw4usbF#cN=c}taab%e*qE+FiEI_NcW`qm538fp)YA=Jdiq1GnrM)(S{s}1T zS```LVjIA!A%P?OwQ1F#IKm}LQ|QEb@}w%LBEs{XviX-o0QjVeNR|7(F#r1fKX(ru z+5oM8>6JbI@64pm7#re#i*m0*PhAb~+FQC^TwXXnZ5*A~Ryx}Y-MNEKvp8S?7imG> zUJ=g%iv!%tX%-g($)JymThbvM9kMQl859p~@s|rq(53UDU|s-7Hx9mJ|4!VARgjUl8N3c~2B;Sv`b@;-$f zA78D`FqD8DmRMgbnHWMH%4DiB85XIt)tAJM4RbW&MVN5pF~pC57J1}6dqv%?xdt~c z?u*(JOt9pc99Izz^tC!#j}HY&1j#kEDSM+Lj&tzf8?LZNu%QNb#xI1nK{9VrMwSWu zPAyKr;y_U-k{KUXQYelrg|Udp(UMTWV~oI59v~)Dl-OW@Z#HV?j^uUDpW$WZ_<8=LETb{Y=C0&>Vs#qbwc@F@>o55a2 z6)Vg-dOwzhh0~QghFGFRmS~pRaqC0c4qFEiYdX@lKw+gSBeVCVghu3g1%*4@ji!9K z^~u{zpl6Bb(^~p5a=opqy07g#w^Z#3{fXG!YIks}lpdZ(ot!N(5_byUO#$viTU%wH zAo?a*5v{G|L?n+yNgbi7L~WI4Xrn|dmWLzKI}KL3DVZ&aCxs&XR02v|1lZvgh2JN&6KI`;vkL*De&z(RM9ChTfQ>l`m zkFY|CF)9NzOlMx>?Qu)q9KE&u#a8R7J6|-Yxx9E$S9bgMo4kL=`fcs)@(J7hf6J$X zn#g4=x{T^r&H)WSWdX-5J$NEjJa^CwE=wBd;5S-F%>RT2LoPMon7xY}qCrXj&4_AF z`%`}2)6k>2Q^GNW_dM7If(fZqizh#3FI&YOwehT(DXpguCou+w5?a)( z>BFKC+6k-Dl@(h1NC+*3cFGW}4@4XdM3UBv0|o+w#IKe6EMp9INA}b4L=Z4owu{uT zh+gt6|9Llk|ImH5@BX>lZGG#!b~m95w_|YAxz~MtJgzU+&gz?Q2V0Gu?d6O2llSix zTz3Rvpxo=4?+;hC^_!Em4V-_wdlswPPuSyiKUU9I3v{I~*AB?!!JS{;@ypMjb@`A3a^oCLi z#*nA~HH-Kky5N~u-hx4Z&hXLit%;{yuMR8vS_e?=@lJC#JFpER7@uel@`PwP-iqb}o zbE@=~Xv-!TOR^b6?zF`L+C9;Yyei z4P%_O^+V&nZ=5`}x0l4u_npm!xA&XERkW?m!-vb};`io${cTUb?QL&ZR{?|DxP4qX zfJ0G#YwtfCt(`SQH^Yc9k9p7%0fN>~7mJS~u^Rb&qWh5Cf$oHc^S@hB&!)Np15Q*szKCE;l> ztnTequ-8Xs>(G)(=U=TpjLqZE6ZQUoQfr}c^7zVh%lDQ25p^F106h;7z;3EA=@CNH-Ry)%0(>L;<#@>sf5yY zqPWHrv+*9al*lo#-r7w`Nxxw@E(!)RqsKg^jEA)bh!gTiksoH(^F6GZ` zYJ>K!m-6tH#g0paVm)|p+OX`oqWcTU$4kleFx=q6kVBUfMk*9K&bNX6_p1%-ylbI^ z@I8r)-?wX`|FCx@U5z8n_OI0PZOE4yIWL*#$pvM@hQnaMg~4D3gMa-O;iAg2GbKw{ zHtqxJMV&5abxO*-5jTb#qBkiIw81A*1v`kQ6gX~PAJk5t*WUKL-EQ3N9c(RBn@{;! z7H%X)<%qo3?gkLBEXBN#ph-CqVk}EpqG3vbqOqSMQm14!1A5W;|59L*)2v40YMo0V z#aF@A)A30bH^zWe;cU+y1Cv>EM9_gFRGA*lTEE!P3$J|1Y;~5KPu+Iq82`Upe!$;F+A5HvwCXhsdr}1H2MfC2+;X+^QXr2{_ z!*XGW(?IkLDv?fx@MAgBv?x0eiwhF62)hr+&~5ADxmkbw|GWCr+{^v>dnobq!>U=8 zHzO?LFdHiLh$Sf+WQKGy8Ig33k?0z}Q|go*sxKz1j@aO+1sV|~fCi_~=#y}gv`C(m zk0~oTMhRn%h8@XtGJWtJCK-f=^YPgbl~7vmW0@Np{AJ%{ONV}&G$qGQ(E1s$pD$97 zx=HB@wrBrtfq5^D3l-oNQd1wh6W$2Qql!?VKrV4OGqc(*{ur(B%Qofm>~i91zz27h;kUND{_)!w1S2KLJz$ z{>WPs0TasVaJ`ConvbP{Ajfl2Y{63k)#HoW;oarR@6&_ogS=mR+N&*A=L%%# zy#X{V!?Al$xXP*aNjl^GchCBSfwi6&STj^#oV748x;hzSGO5jQ_*tA3;$yRF|E;f3 zh^gCH*ls=iTA5qfJ=$w*tU2Z8ucn~HnR#Ecytcpc3UIWvf3>u6(y*7mHz41A0~8Kx z2!Upz6|gfI=RW@Sf^#3OA=HfHfy7hmxEda`;+1CLElBiwX~0zIN-4(>43va-iO+~g z&37LEeN7{e{AK>w6mshsHq{s==m z9J0#sY~=*1vXL;0Raxg5m!z?Yew){7C{TrTk_q%gtwsMp`LN5BHXS3rNz$Drjs!H@ zO`@M~IE}pl{hhc^LxQXESrxiq+zJzpZ6%i>40-p5e+U}nK7szz4~zSjo4QZkr*s87 zw12z6qL;?2il|c}+8OOVSCj-)2_No9KN*g%8k{lcsawx&u`a4y%qkoGek8+dT%dO$ zttagTiVpHf+ZRzQBnyf-qq0zFy;CAqSL#mRHtw2hhd1YIuZOLrjrE(|0?B)7+VZ_l zLnM(lF*M;1xJq=h$Sey%)eAc8`T~#43>3Z|i!K!Bu{;2Nczn}N;VJ7?S{DGV7DBVC~mBnT;Za%tio-dc_D3 zgt6pATRX{el-<>?d2gJ`I0Pt}*Z$idG&i6hG23bU5jHX_jbG?-%VA2{*p``pLZo0o zK~6vqk*nc)9}kY914cz$vNu#Y01a!H2EZ>QIr^d2N2roE%u}|R#6i)IgCf@aYXwby zk4=@3@&GvJqY4=+JxZ!TQaELHLMf#ZrPNJI*Icka`?m{Be`!Ri0Pi5Z3&$n;T2@gw zJER>F?LG}gRmys$jVZ=Ea`B{WL>yc%S620^C#Z+g@sQV{pMeYtJc0`4%Rm%b@056w zeSzPejO)DTRp1MJ%sRy~EC- z>!TdS(aMKa4dUh1pQv^51oyx9-2AVT^%OGz3XK=_nmMowkX|MSsgI)b(q3W&gGQ20Lp9t(&c*i^khW zhHBlpya=40{|6HC1IMErIazKzJmFJOnPal)Z{l&Pjpgg({X-5LM{V9Xn?JPvZU4DY zKAnWzp#P*}#gpQz3C>$YtyOxRanv;D483Kez4hpqMp{7>4ThjrYfht;WZByC-$yNW z)K&}hjA&_)`txY9BJgy-h73AYSd={+rue?^Z{vYkDt~j-lZY#gHyUPy^^1>&2_L7N zxKX`*TD=yx%SVUx<=?9d)tznIS

X4Hpb8r12z&s9`7(1W39)=LVf%3OdK)&0v^B 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⪕xz5EdL76exns}t)V;l%Yy zGIl6o_n0{5PY>_`YRutgNVe-IaL$*1GRpeWkpzgQYsq5X9feCnZf{p7P?f5A*>!wU z1_Y3V3X}F00w?|Iak`pIr-FjH0mmn(mxA*+iY~@we~LHdP8h%|O;JkVh4KD$W}i(04yI`(Wb*fm}5{bULiXyJBiuX}tni zdSi^wx~{g6_t{6)Y8~a*%3kw*CPahIYnR|>YT)BRBDBWPzOzBO7EcejL`UUlIRtv^ zX}UPi{`^^%3ig-ba$g7wJ_U<4;I2A(m3okkBH-h5-V4A}cLZAo(>?1Yqri|!X7@F| zW2iM%?0s~Gt*D`C?P=m*EAx4`pNtdWduQ~b_1j(75c>eV(^S(?*NK7)bkGSF#iI&7 zVo)TLdhPYCAdykga*C|Itsc54Sk4Qcdl8J?+O2-fz|Kxb2H!uRcD9@S7KLwjz5c}? zmpAs#C1-Kujg2He>MMx`aWlN=n>T{0rrtnLP3VQ=uM~~b7RT90ERG*Y!IiF?>SCPn z!Czm`&(t>Gyd+hM4k_SdV3s-j;?2#bR90I@z@0Y#1t8_-UGD+8-3b7-pL6}V2zXbj z;2c+3ysfhgb&5bz3tq9$S@=F654(U2A0ZJLnVS%a~Y-z#{F{8v<3u@)+aI0yoMwo>~%3?<`M#PDamx3%z z|Kxi~tcC`W-GFt|?(73a_GjOaY|Fqwr6;Ti%#H-t;wGm(R{%pWspsk7d_NmS1=pam zzNvx>bI-fM(2l8R`8uRK%^DT-k2tS)h3HOB}L|Gb@8kt9w&2HbRNGRWE%^vB`pB?*Gow^;OB5tURK7>(;~E`Lj~<*5WX}1UEh_Qt1SUO@gAr4ZFjYl`oSXWoxtA&tN5S zG2PCHnpC<&H4!%hUc|Td`aiG@itv*_4MWVh?>paHfIU!ga*K>27n%V<^ zNKxHl31K1iCaO?~MZ%>^@b)_`vCc@tp>Wz>MZ4?4!zQ&BI;9}k`=j}A4yc!1A1~2~ zA=F&2>SZ*I0zGoY0`+rkh~(EZiB}n_I@VfZ%*V&yHbLg%iO8AEMQ|8_r@B(=M`5y| zXQ#Ab;==^QtVY8G>QV6(dP<`!D9&tssLwzULrcGFh@(rEL$_Xgn6}@nP5fRlT~>^% z&lXckv{)k@GRi_HGFE z)F6y=EaE(7|o= zfw7mXVtU-wuIG3svu%qy>-bxIULgC8Zr|GDGc`=aAIyO?VWM+(xifjt<-j1VJI>n( z^-13dtIr#(RVNs};fzs?hN6Z_N?_v=U)@clcPFkplkZ&JRAu%95GvOyHThmx3J=Z8W0zncLjNI=h)x1;-2>n)i)g;@{Z5 z`yHx*Ylda)&n@e!im&AI4iCTMp(opqn4?jzV98hu0~k_ zWh3d(3548(M&{e?FJfn2E~p!4(|jo&sg)}f==dv;7l&qBKeoGLJxg z-p>X~G3Jk+ioCw{*~g}WYKN^h;)EF~&hf>H{Sz0`AdhE=;HX6NTstoC2_2cj9HMP4 z_B8HW$VPqdXqb=lM(^1K-f&4tOc?D%rZysUe8}70iFo^zON12)q->5cG;egTfXu3v z_Rdtn$~ngAqGcLAl$mDEZSU*x1x@xes;hty_G|0-Iv}$BXr?_dn^*sOnt$gCaPRmDuJ^!!!{l0?cCyST7KFoS8y*aG^=`w+-r3mi$Ewn>btiV1odN40E3Ra?}`4(JWDV6=R;^P!4iE97bjFMkM2h+lLE z$iV@G1EgC12UQ*5k(<9y|{T5Qs#gCtJJE1@JtgMEF0avm-NbzM^1z5x#1Jw_*XE6%2OdU;dE5 zGi#R_bS$23r`kVUl;jaHVO}m-ujV}yl=sj?FH*2QutgcCBQA5Imi@qkENOGa#uA5$qmZ1 zS&n!QXhjfEzT&=|Ck&sQ+PyqxEJm1B7q|2b;j~Kk{^_A<>(|0%A3&x|y0!Vbhzc+` zOQ?fm`h{64cXUM<*6n4j{7Bq(f#wJjoKRf=Omw7&`U-piLc{Se6+G_=j&Z^t(B7^C zpQ>ZCDZ{&Z%{Hr*N?h9N^G^3l3pNE2226gA>s4mf@WFoc?}8U6sU);FPVQpiuHX1v zS}4+38prn-EiRWYLTn6hLf2OrOAvP5cE{Kv0Ht=`=y7!<`cz-^&FM0rrs*3d9|GluYUR$2?qQfcAFLe?3L>a(z0p~ zUUpxYOAE8xHY%zERsoL1E6cs#QwqO~__^VW))KJpKl`b#e#+sXi^01ykOI-J zQpjH7T}uSO5`P0;_#Ocz7BwhX-(|=5xu5 zb3EtDyz-&bim|*JH<7KE(QPE1&$L&)^`fsn?7UfB+ESQdKGoKpNPI95deqDI^SLqX z*GmCnid$P%$my?dkeZYe%SRI_bBcN2No-)W)K9X^pvhxz#Ry zPQ>jIjjg@dN5=DQYWy|Xn}bVBmV~l32w#jP1h+c0 z50T9Y);0d}y79!gNr;ff78$NHv4KVRIgk%Vsl>Xa{diK;+Gn}>o0E+d+~M?x2U3(5 zva_GUB5!VKFqt0O&vZb)leKaFBGh$(o)pSe6k=hVX4$r}A#U0aFd-#?GlB~}{%ZMr z5sOzt=vH0?zZu$Z2!QjWm~1Lr#>t}dGrNIfyBhb=m2)5+0cuLeQh}ym<;d=>fzldV z(K$v!VLri*gs2lW#HE?d0f6k|nL@FLy?nYVf=SkW5XF~3!IbKb-XMD^LWH-HGhn$M zoY|5EG9{GPOgamGN1(+tE`29GWM0jphCdg*c-zik9e?;*DeW>) zFrTh;O5&IrSLge)B((FQ%vv2z0mj&X!8z<5 zuN2RT>|aL&a>A(6~!#Y^HRMLfpK8)HpkG*2gyq1!t~=;O()@$t#z0HwNhy4_Cyt9~ox8_+Z`=xa1t zSB1wj4AWjHJF9uH|IomOY+OKXsC)1}ewY-dHb7v$wLA)m#%^(yQmqJDE~uJoYrz7* zPYuvp2h@`8=HoVR*~4g6V)|zjPI)ad5$mbsNK$-U%si} ztme;!qX)B}j9fkK6aQ?gcU=e*T4zZ(qID? z>Kbx5k~$7>;lX6%05nNUrQmn|)g5&Fc-X@#*t+zj@FfKoqaTiN^Fap%k*6=~be;XK zwM{31tF&$Y#@2&4V~iqP!r`$ToV=*3^Y0_IXDnPr*amEF%yhI-FqAC@exTK0PJ-_X zW6lBurs4((P?KeZ5a@l)gvvbop_nP9sFPaiM+7bUB*k;iG~vmC{M1=-8wTK(|9}@B zyg#ul%=K3}bvy z;Aw27ZQ=FFkdLVyYRHWO2vnV$zgIr|gg@^?YQ=sHs;x3;(3WZ(@t0P7zlKOtn7?swY~7Z zIaYwZZYXPitL64Bb z;jwTF)&aI)7C_B2(=n4r&o-~qm_H@76Q~FD{YH`(TEpwZJ zjU{{~fOgohcfBS#0Xm4sgSTU7pe)uR{K61`(R8hJ2UG=-fIF>aa%ZUaLrAt+%eLsv-)AF=hLw;jzx9VyM_0U7 z8(um|g7utiAs_2bgT1R)>4O=2yB8bg!+B!IW*Bf!<+g0yj1PMw`|v$hbGB${%NLU@X{GU=|}3LG|qNS74#<<E_axh%%K5r-k!u*0YKF2`^w5B}L&6#lSF!pNygqH;Io0;FC~_;)1z4TaNZlJ_A$%@yaJ$O*x|V$0q}R;cv0n)qw|Wm z;itYm*HKEqK?DASF34Iw3E!YGm;bmP33OPuKu;Es@Nm+~nf(~5gBfy(Qg=$Bi zTL-w;0-0))$nGh<+;W>Z%NJ5;!7GN}=T87yddcZBlPTd=xv$zym(I zpg>-_9KCx}uFw0kb(HdGvi?C0rCo`&OYZya^5-O;y|JGGhHX^Q(Hkd0ht1d6H|FN) z4mGevZ`Q}ELbZmf$0>_S*y=#nz-%7XRJIfPlQ7OcGGyD?dvg&iR8YlEh|>+j@WEN* zV+Ww5etcqevv(rSyOxCBwS^?q5aHX(d|#h@DA(9zXBVFR)orLLXk_rcxfco#b&P%iCf%RN5PgYY9lSu*nZy;3db z0^6B`JiyN+x5hPH=t!&kI}Gd>08S=5TF3FUrPZalw7Lh)=Up_*PN4v_immpyGB=mk zkbfJ(lMj@zvTLuqI*;4v_-^SN_xt3*5Hhj^PFl1v7Z{=YCl^z8+DL(xuUVJWTa!91{zCCK4=eZ&efO_E0MnMm!gb z7nk%#*Oqs)A99R|@BfY7_h$_uF+lTa_JIPIl6ej}n4r8&llSH(n5jb@dlAK7iWicA zGDB`I%V^OBnGqL~{MF^-i zsd^!BF3M*(i38Pg&%TgHfUz5)4woGGHb78^8NA{j3U)c}=ATePo-c#Y+xZ67Lzi`& zQ)Z<*uusVnr2OPHVXN{52}m7&56{-FsnSfQ?JXgWCrrdGia}Nl$5Zd&!=6+zi_@+8 z42W5-R;S^)90Z1@FRA;1Wn*s?@uVF(e}K`4{AW)3^$z}I*5r*&lKFC9U;;@dj@23BH~()h2)B- z&()fHLV_$Oo-ua7;+a0+vJCBKIsdWu8_i&pIYhBDR6r<(kNc-W#woks7SiSGc#c$}>p(YnELgrQy-}D_&%FF$Gu~7O1P%>63&n2X8%_>BBIIw`8A88x@?q_ci zpw|%cF_DeaG{yu~;}|eHo=csZUXt8@JQP=a(jm>mG{;Xx?Bb%TA;W*4mH9cv*x~&+ z{b4lMS*}h@9&!FB5!zT|ikWUrSF-Q2ITik{Si?5FMyiPU6r#Y;K$x{zTDF1T8tq7v z{IO@Z@UMBW0Il@t13x8|=pg~?b+l3=c#}WSm&W&o$~v0T^}Q0?JS^av#&u@L1XT+1NL>%KAzhVKr-e$oMuk9|jhO z{4A@RkW+-{?W|w6IX=x18aTUt4_;41XFYm8qwjUIyF3rKaY^2iQd}D@FCpv8XMeSkTlnm-tlxMuG0puvq&%fv+vzzvml$D{YgbGs zKqZq5Na}4|ITWOEXN#r}fd$z??l8~1=tDbt+tWGOq;)a-apkm*8K{r-Jglv2Akd|k z1#t|LZ3jbao}$ua{YTyzt8(Lhj48?`b_SE7kiX}2y!>?dqfY{Q%XF!oE%3gCD=ZhK z^}YnJ@i-bf(%bKE&|jR+M|lVCC`m|2z>34_n8&If2Q|8)wt8e_xlCNf{>lvdSzk{k z^Y>;RHRqR$qT)tK0`=uh`y|C(yZ0j_WW3Ry+UFjd>RrP%#3JoVB;SFseVFYrUM1tz zZ=~pEUI(HsIL>A>1lhGV3b6@?Ym`}q29?K9|DFZ~^uQO3#$$n`Lz`=RVC5cBZZZ7U zRFOe(d{T%p?-N$<;e%${^nY_xk)qP7lUXnuC)27Ia`I7*ql-v*aw#;q<^ycw_x9_$OF7jJ1+2>wJ$h;E}WlrP6n2d5>s1Xz*uYRB=csz*MkC4WG zu?Tmriq#i)`?6BLi!9hpBjaiEkC=v z^ScA+4)9x%sKi}S8)7QR%y0C<*uw=giJU}6aZAzZ_d=O9m z;K~%dDpP`d;{8zGF{g|O%7=u<+!CBn+#$Q(ToPJ63Y@nwPxuc((*v68(a8%&**B-- z;$*drO*3HBT9}`X&pJZG-yNo3^g*Oexm!KHlU0h%F8gu&kSCG?Kb>h^PIjM!RE#FP zIoe5@`tD3_EBU9~U${%tla1WolIMu&P^t(EZo#mTpspa^7eWU8 zOw4{0IOn&2Z_R&C!%gH_;-?;@AYTI~o*g`>l~q=VRg2infm0yQ)3vjdBGAIl$A9aR z4!OtlCyCb6nl6(t-%Yg_49YV+f4RL>XcRHMBLrj;e;u~n#5ql;4H zlu}{$)vYQ1zCPHZAWKcuxu;X@cY9Ms!qJ?NNb2>nwn&R1?{$trRFHF-I>W~-Q`|+Y zCXPm(*%X}29yVFT063PJ@<59i4%6Q{6tkVLTG`}x}0#s!QQ!z`{!c&*3g6~F}=mEP{h-!$XzFzaw^ycWQ*(*EUm zD3*V5aZ!KCZ2Jy@H04JAewX*B9c15mO=R{T+i+?ZFc+Caz6fESY4mY_BVeU!wOLZ= zv4`r~KOZk?d0Z_7uW`mEW?Rp39WLPf!!gq?s;$tKI@NQdtC2q=(}WiOZZlB!g$uR~ zu9Ne8Ah$EvO%lAga8-O};2dx3bZZOpq_Y}Vld%D9Iop^!AJdX84@l{MUK+JaB8wCr zl@^ne7{18nj+Aa9|i8Q6Iu15k8Z0i$qRMr;6HtPe?LlX z2+}GFNiNCM*;_E;qL6`eV<{Y03g%Z3sSMw+R z?4y(ng=WHt~O^%TPQjukF2KBOF19-gf8tPh=nx`$Q~!F8q^~nwm~W=iOnBp8uN; zF2QM|ZJN)8{>V@pon zT@#?scS7fg>V>X~LL_KBL)#OwAcbY>`8$bWJ5 zKu?E@qV>BG5VYYde&v8imVdG8I7&A=wgUy6SAc#pR*d;NHh(=*oUbzh#`j7jqL^Nl zyU>5L#O_$xU`=BvqxMd9#yS?GHmgWj@KDRTt$OG5iMiaByb42 zw|4r~fnP}BK+08Ch(~jOYZcdjbDlK!q7s7bJn#D9?cdBKmkP?fB7P?2Fj~kVlg9}Mlqb=lebiweTM`+@g$b?^VN*M*@yy6=xz`TSq$m-?3$*4kX&WRovAf zMmfvX--1)-OiL!WKDxCWbK=a}Fm<|LzlKiNB@d1fk#Mv=;5ex>tI3C*qmJF~nNXLz zr6SpILlV*Ue}{fe?CScuFFP=j)LPGR*AonwXLM3hSbO`R$EKangTYfKZ*AyD^Vy=i z1J&eNJ@A_x9%e2fK0}wI&fR=XWET^MI@fVObgkGldx%&4u8&>tNVRQZ)qVn7f8F0x zcJlrhQsdQ@6VBNnZ>Q((M%%jS4nwp*SEl-WldrqcvKVP{$gnuC8mr=-LLLtYZ21}+4@EH!G)#M- zvs-S8vBSIQ_pm9w(;4loLV@r>Z^WJ*7&6eXShNL*P!fnT3FJNX3h~g9XbD9qP~Qiv zh#r(FC4%B8o57-yTV7}Ljd&9&_1VmJR@14;yPkjHcJl<@>^CM_zi8bl<1JS9Cq4r9 zYS;4b6vt}wHM7n}>#b8SoI#KCmfFGU*oTv)!(3As(*iBBzomc#8Bmz zSVA-AJr*%^fVb4zpag!i_*=})=5`ejnc+Zw=@Lo=ozknlb}lyv`(D)(eLDyah}*O7Le# zw;1Ul$YKANiA6K>g@b}cecWU2IlYsCLPf!(xvL%L+&XZu$9VV&6Cu7@)`Db`U8o>GnbMKo&yxdQ)CQWX7i%JU7VcAoQq)$wwIRV8N-Qg9GtF@$x$KyyZ*4bKVx4|-@E}cwS-_6LR|-i)Gj%-)JY*u7aa%3!71o!A z_7yOolKV@o^G&6v$7V)7>I_afESzuqACh~*h;bUECHAtvNZIi@Kb;FjjpEmXs@?6i zW2Xf}_?p!#-~)0+e(LA!joDs5KhlXuY>&7MFcyDIAM%(#JMqH6p%J>;Nlo$iY|pQy zre92k0W0HBjKQcy>BN9 zF1JDSiZ+{8tm{gMv@<@R0Beqf6{Is3>;l6JHzb|DHOoljCXF^lb(qSlKtZoCE21xw z_mwM1z56siFV5kQHafb5c`@Q|*Exp7W~8QfaONw*tj$@cd=L2~g3)#`$LX{&bbGca1!vgDj-v@eXXxDU4Dg^+upbD-NJj z5J$mTK$``CdOxPiN_Bx7Pl14-0QI}MrM;xo)qbzO$8ehe=02-T9@X(i4**r^A#Ds*XqCuM);(cS zZYiB@)pkZI*SiRBoc;Xwe+&GzC&G9?lh}3x2o_e0iwB!&={KwUE0>uIV8&9wE9Ws+ z_PL`rke!UjNz`!%)0B8gE7hlZ@9{Wtj!xrz@RpHawuurxS{sYgM)I*>V$|MK-yxt( z@${Jc|FLFZndHKK0)b>kaF`OA^(|?bwJz7o1YMg03pMdtuQ7d=pSafU zC8+yEhn9qkEd#IUK3*X6z)lOsPTC>Ts0u@OiKHc5AVDEP!UD+Q3cjajQ_>`$eRyok zuz;)m0}FVf}iWCx)r!8CQ9NmlxA0qd@>+bz&Xc#rki( zFmpk?Di0>d_ykk2&c7H4jyP;PUXpz_QR#)y0$A-*pa(uig&tdaPjs2lNlpt{x0&8y zv*?zjb9^OP-^+Ry;sM$3WPH}7keiH1ZKd(j4Bv;#bbL?-c`}>-3c!*_%~)y)axMR6 z2Ba=z-FK1d>jG4bb$Z2i2eGwlsWth}Hi7{v*lY?cDLW7$cBpKXtHV5shmq=IKTpJilQ5|>sxGclAXDa96W8^)@ z{hhudbrG8&b@}atue>LrBqx$LW$b?5By8eo6Lc_|j)xf-8quRyEn@>NL;kzBnba2c zUTU`|vz8*n`%Afyzttu0ESL*?V<8P+{5S)q&{6U#(85BRh5iM+z@S=AX74ZXu`n{O z%&FU1(_Q7f592T=k7v?mu?<`^C8Okd3prqrP`>-!u@J-(w?x;(j{rG6j~CX&sAiSY z*XErjDXFEe;xL0j+Z!Ll_X)ZUV_nra!1^zE?3}0SbJOc)H(>)9`${~WvEgJX)AI0q%RY_8Q zEV(7?6>hj!dMR1>s@Qq69VwVXKF9n^<1UZ#w@my_o~qR90blkYD93(Fua+Hi7mbw= zM1a7oi_h=Sx6JG}wsAGFJh_o|bqT;;sZqr)m-|^a%x3NJm z=8V_?s#6gDAc`Hx0BW)}QS6_)`;}ve&+lP#KS9Fuc5ib*OjHqd^YiKQ*8gI7^+l?u zr?2o!voVaves9rCu3#{KblhkHW#w7sKp|?8dUg&DFSv>9J*uAZtE>Q(8DW>^?s{F0 zkoO}e2zMJt3EfP`X29}&qjS5in+;hk9*et&JTC;8KfA8mL!$vHOh}k!ne`2Rluv;n zKo{Jw7BGpyd7!j8yMgJ08>TTKo|uMU=EO30TKC?_i? zPn}tciXDEcKgo6gxu6~=sNYladAJ=w*-v%<{soobCHwPB4wi79tchP^rl@5L-j0C# z9A1ZhtkNb#-XeP2tjIn5Y__}GI24p!7oc+02bLwXQL>HH;zrWvU=${p|J-cnLG;M) zMzLxrxyL(1;Dt$Idc+o$`yS>w^y%uSuk#oTg6z+8MtMPX=V+7VtnV-G2j?F(U;)6u zMd35U5{1ekTiQ)#p2&jt`HsY*R~JAH_x%V2ndatVUlM9esZ58ws`M5cZBo281;>F6 z$P}8+*OnVyidpDnxx5=~2a%_@ej0fg6tp3UBJyGjF1p>_8auSa(MLIqjCXy7CxN9T zL(}IC-lT0XGR|HoX3&J^d8W+^6t5_5$|_s_rQ&g`>fJPK(qpfmO14zDdiMU@am_BMmt*pi` z)wFEYv?5k()C$Oe+)j<#@wZKLJAQ5F_L7$Jl9uQU*$*D5hG<|+kSMlD#zEK3!Er2p zgTm<7g9?22x*r%!u~`p&1DnlniJ-VGZjLgp;!)UsB#{U#nNWH$1`kDK;v0Z%JWqLp zoX;3uFz%EVa6$y;iNqW%*-Je1pIv~=PH!ip76~LTD(riP9@swf= z&Y3hp!kJpp>ZIsyoY(s`H+S`KLs9(qScH4`K=QxId0V8>XTKwgNSHu+ATAG9ch028 z-Pk6dzStJAe}l~CeCFEDC^u3}%;});3zetwb%K}zi|o9R!hwj9pOe)yTAcK+0|TFd zlVBOs7kNC$YGjpofwIk%%f_Yg8$V5 zd<#XLWfxwfYrQXL;BEp6Y z_>rDB;|AreX_hUdhs=oB8BicW~}ub?n>m^`-{i44oWUls!-Y{D--F0 z(L&!}L<9D47yJjax%0o}4(e$Kl5mtxFOLC*NP?KAe3}7A(d6<;?)CNe__4QiJ_3g^ zle3+x$#pN<%Xp)&07Z2;1cnCnLqz>->u5nXkt)x5KN9cLUt<7lq>SY*Gp-Z=x z9k$!|?#5DWEbbr5M;0_PZi=Uka>U$|yaC^MlEtp;{o>DcDO8o?^FGOG+B*qut9!DuRwB5l1Pb3M+v@DHFG*fU(llBG%`# z8-yK5HNH>9xX-7Xqnehin$}3R@8VFph}I(n3c0;AJjNyqIomx*(Nw?Nwx^UqgiVf* z_@we7nGz}Tgj}{~o#XPGOhT-+tB2IvNKm)%J`5^uWTdN=)$^EqvI)l*MN7rZRMI4W z>JP&zVZLY`gr+DZ4oQMly5b)ar@6!>sWuLI(BOb! ze;1wNogINM6|Qw36C#$?PBL`KEpG@i(NT)z1e(ZjziF$eWX?Bq~#zNzU}gQu6^ zHHZ{K>h%+DGFaIs`ePpQb@cr&Z|@&TvNfu7adEKQ^fwkd_Z^7}@Pm%4apwA0^qp@> z`vHts*u8D}Xf3qj_L_=laX}NguWKRZubBx z2wxwDA6Nf_?GgIN@@&>4JeY1yt2jk=`ItYIpWZ%JU+C(ScjYNGAQJm9}Iu)=nmO+Q&OdGMbn#e`S5B(-0Fd4}woJiR)run%Xcr?gHyP z^)~u|v;|il_6t=YewCbwFri*D02Tt|_ZMwyNx#0Xr%=0|RfQ>g)JwKKXoxY%e>1!P z&m)v<{?Zvz3T%xiT^&ML6BzWjbllvWZ+S~plpsd~kTv*|q#o~=LH0Zs6`P?Ct~X77 zy&JuIn!c-cV#!xG5|ko7U5}=r!!e^PXEXgW{EbTpcTdu|KFF00k!G=i@c_nw0>1_0-;^lU%41W`dV) zjvIE+h-ZwKT!eSycf<9OVSEO8RGav#c4MCv$7pr8L7Y&yiMFQQoO->cM(K(AXD5DW zL{yh=7A;wCfdnxVPx9Lt1LvE zIo&zSR~$Fl_b4|+NXh0_>74k1MLOy5_8BJf+%b|1Uxj*Dz?rrz)%Yk!cMJ+JB98l} zctK+-VM_f*A4_oPGF+MMN55%n-zac(xm+k;qB@ZDH-yy&k6kyqtpa;$O&~!zY+{Z2 z`tAAo&3mnu>s`i9Y7SI9@=!E1?+f{3F%xbF=QPK;K?JHMfSRBy_On_774TL(V@u)Z z>1EnrfeT!csjA14?>xHsm`N(Fov#UP1fb6nleKGj$LJQXhRIgx8yJ&o&k&~KuW=Pe zG1`)U?*Nxn{8i;(QnAMoAZ&;LkAx|zIt=GIxKbz~o?+Nf{BH`#erS`dAQPaL%ekqm zUt)ubNJI~Evg5xFnVhT|0*rjoLDVI>;Yfc<3osX1?P zfhda7s8cEWG_gL0OjdsmJcEK{M|hd+#*eOr_yx4lu5t4?a#E?%vkpj56SYBJ%#}?Gbo0W`bD# z19Ftk57XznF!m%kXeaY_hlgbYO=aSXb3kY=U&%!GyFy>*MmtwY_5Qa#gV}=el&sMf zZ+AscP%T#f5vW`#hUTaptTq_gX!oMZJsPMv-)x~o*SH9w5D=?}OUR$~s2#MbeR1YyBa{d~1}qsz$UEOnoo z@VMS;hE}cM*Oy|O0)Ic_NF~#9sJr3eYN4Dmr~pKVzdheD3&|789(-rNq&n+e7j`A9$GV3&<<3ZL|9= zkUqizV_4klAofGO%u@J zGmm8HzvWlaoHn$Jb$;7Ia<1>=jftV_%ff`d$iWx`nA36`gXSpp$~(PI+R^lkdZ@7K|vqp_Ab9=KgT?fVj70?YtUR+pm4B_I> zOSYueZpTdt0}tQv-vGdjvdR7=mXk`~9h(39H$7kKxRuYZ*m8+wxm>4~RxLzDg=pgN zFbbhx6lKq`D;t>pq+Muu1pS&?_iaRA|JH!p>qZ-Jz>4PUcIF@=iIorc?8Dvt^FZ#x z?9ffRGu=E2T_12b0A?OPllbS4!&FF}M#LTMU+Rd1QHDRvK)9kwCZ+=hzuBFhX<)7n zF#brV#gvBs?hdi^(7+;ZDo$7^P%lEF&75yP&}PWr00Uh-ZYlEc2Ws*Y=0 zqg~wGawff8x;LYCSXTF#wHu7lQiyCgV=t$2^;ka7Re|1Z;l?`+3I(1SSo^~XON6$@ znFsvATvQUiw^v>Dt=TyB48m@3=&y+&5+mSEK#P1$|xjv!RXaXH#HKG#iw&ngfpjPf5rJ-+1@;r$M`g-u?0-z12xl}^>Ax@Vi}_!fuB zkrm!o@l)5Y>ziN9+N2qR1_Cp>fg(TK)JY2vVaB0BLYtAwDXNjW99|Y1EDsZMUbvst z`BwBcRzDzrxz6YmG<;f!nFhRHQGSZivd%EHPmT5PPCVG>PpK6rC_w80RXjs1Kq#|P zx2vvNK?=d^N4{^NWY~V*3O@K|t7#eF^6^90WY7fOVM>s}UWet_x4# zUc4yyv`Yn&j#fU3IYS}&R&N<28ks7z#xJORL5o8j;?_~(DqcbWGD~W*x{cnaBM)J? zq90xdToOFn*otI{oM@*Y_8Wl{5g=kIZH|z~G=#BJo51x(-R6Q*Tcyhewh}1 zLH|zM3w;^Y84Ux*emPb3vKk0cpmGY6Mrn)eW)`Y>#>QkD&s^bcZ6g@ZjXU-V6KVL4 z|6_~8Ci5kOK1c76ms2fZ&!D+h`0|qU}2X3oN=8R~aQ$D0~4zAH@ct9bAJn~d!z;;3BLq!{1&|+^vKW~hbv^4X&l%@3F zZ2nr{^J382c9l4pXzwB>_Vp+sAD%YF1Bp|1)k3u(BLl<$joL>k`n7 zH_D%&aUj66X4GW-jR+bpRn_DD_PkmOV&M_KzhJD1K$jgA1J>q)`Fw1Qx}VFtGMU6t zbabEvQeMY*8K*3G5V`Ma{#Mtk!LQ6&L=ZD`3^`)6zwFe;SnIXq`fEmAPBw}AL%*=! zvSkXn59P7B9cRUaDmEG~*$m`>9&=mEm?MDo;4f!{@Xk>UGIH0!+tpwxDqeDp3?34N zsqI38mmJuRwVoKF;&?RKEB1xC*w(O_Y=xrtQ!LMK6Fe%deqiN(BgaV#LSZv$il{6} z`OowH!^-H~X}slMGRE}bwWkQ~f!6|DTk7w~XNK-|EZb^-JStNSY!U#MXEWpo8F6`Y zkzw2&rbA|PSylqduiY@Ag$>|=13nYCx)4C`@hISZCxfTtac4wd*Ecz zThrQ2ZSeC-)3W+z*eC;MHhuj`!cl^J+K2<{KJ%>lTSE*&yI|z~cXp)Zf&J>*_`FXP zn)AiT&(&fWKtuiOA%xAL%Gx|oIz3gx>OkVb5jTB+zmK86JoT|_@62y^4gpk6YCQ;O zK3WBeRaSl4A!jXrZW9i!z3fag46#WcUVw^Y*fa8lBlX8KfYJgu?A1nF(*h~fsA>&( zx0qHzeLPX!_dg3A0J}^=Be+s-<4g85ULhxxU(FJs;Zq$5f)t$d0q&ID2{Zkmacy8E zdqTnz2?&zdq;XuMa#)abX{*Sr(nFmi;UUpZQjp07bm(e;nJO#lO=uHlc zcu`FCt+9RtV%?u+VO|j7h*f>&5df^)?h*nmNV4(H$WG5a@>;oIn)uFZbaFpRJ%YNI zq^Usa9CuTiuIi~lqygq8TDs>#MerUxBcbHLVrACXT)+Q*P}COwPiO4X#ZyN5Jz1$I;ie}PrQ5N zU7zk^1M}kJ`TOi|i%A(Ztb2A786x!PF6n9W`V6 z$8%cfi=YWFg29IF_ewf&#k1)DXl$&)^&cheZcuUjy-iO(5;3JNYA0@LBZI8-i`o$8 zXT!t_hnfyAc(sxys?Q3x3X~2ymz%C){}EkJMEn0&p*A#~z6gQ=*q6I=J|VSA>rc7C3c#4ndD2tCO1lEVgJ9?k1Zg3HQ1j1@?3{(>O%D+Lt5( z`FvM`%nBLs0rjlitgaj*G(q}}G6_?{&u0teT+ji7!a`j1x4^t|^tpekj`^t!NmPB^ zFe=P7On7)mA)d7PtlFQ7NQGm$U2<11FAn$phmkJU)zt0Q?3PF<+ zQeY!WbrvYrLj-G|5h`_mSf}u5z<2vd@}Y^0*$&^%~G zeloOOy7D-`p6*PJhld%rMy^*SZ)Tl#=Y#E(Y$SXZ%DJ*YmuS5+VH^b|D!cR1`U4aaHiQA)b@v3NtdsW~+* z1GX4zsyoa)(d&3`-zBYq7s*d-QORq>SwT!k4i3i((fOu};w=nrn!SUPsI1+c>36@I z%_HxjhrJ%HebpTxYLPr2092U8n&yt)gYeYl`0h7*aW%;vi?dGus(Ve?7(FbG0Q%rbP^nNGp_C_1sI*C z=@J|FTWz%3z`O&t1`!yL;SBLe&nkkA;@{6EV5$@#)(`?jgzs)uyI&BB5ANgz5zSP0 z9Y+s$lk9&cZdF*ZmH~<8nM7$qsnyTYRIQpm!UHLvrd{0oc)n~A9okS2>enPbwZLu1 z+W*x!wX(<4@qm9CZN}{$%uhK)xgjR8YyBZEE%_u1vp%42lM{Q1E8vj&K+uJg#AT_I zjIA6ZE)DDscbo3uLi{9nSQT|Qi{LFQp>AJza#UDUKeHfhbR2((X-;l^8r+xj#H&3yzy5_XxDysY)7jy8<` z6=!bJul{MN{a`OTyO_z>csV>+1 zE`1PlpEh^oH=u0FigiB4sQk0(9MgIMq&3Y^Nqo9epq>==KkD;<#)vs$sEWSxtXBEy z?GLIsJ$$lk&p7Bj5l~#0`PFu zC3%7$l_Q~q{S9CV=J;mMgfk|%czKRmxtT_M_0^kR=l8yz?DE6wZO&#Ha(#-A9FD!f z_30Zr));@@Cn@|Qlv0xTp>?c~6e*SMD00&Hmnm4Wo##MZ#Ko#(VYphQb7p$`gN&BU zzEE~4+titY3=uzWsAnYiI(tFoxMO8W;U5X+0z^RcZW*^$yI^FHv7PJS0%T^8q`X23 zUw#9ETzCELkua{{e1st7F6~>}Zka=S18W*HZR!#5*a$ED%F@fnPQUA5u8xzKQs(xQ zS`~B4%R!uTbU{F71&FRuR*E>ibG$uo!D?bd9!KRYz@K}MbXbYe>337)82RiFX~{s% zQT)`Hu0Zwq(|zPb<)mDx{H1ah($!i>(KQI*a~i6VZ`(vbimS_EO>>E_SsE$3M>V8o zFjAuvjs$|L%>Uu7-wHO*h)XZdkfZ1S6Y(X<3PHac@4Qk8c3{Hsf$U)_ARf2U^EK=U zr*U74^YneJyvwcJ|MF4}jfHivLoB)-CM@()Nkb-NFArVNAbV?<`s{*R)TH8IP>Jo2 z-mNP7(|*U5D!K(5xs8AsptPRH$7^i5;tI(`(=rV&s|5Zho+_ZM7|W~@)B4jyC%urF z6DzY!e)7*KqeF%5OhYsh?$b)*H^xZ_m(40LGyw6!6rKIF(GrvV8ZHy$<8bg?vCQ8! z@qDamy8R2`ohT2n-EO!RJNWbPxXY$KrR)oED@I>y-}oPK4-@7#ejIHC8Fk(^d~Heqd8))qj`u^>)S-yPShSL zK=1sQ4+uM$LH#b$xI7A63RodD-OB%jh=S;$fJlk7(_(ZT1h&hO$jF@4UiobxxROHe z-X7l(gN(<(7QSYNti3ko~4l~#rD;0D*C@p>_LH%PS^a{4wj$2ipdTDFM?+nA{ z`r*#lc!VAUU8}Gbw{zYbZ?1@jLgj78tnBcra4aDSl# zZ7md(J^J>y+39zuN}DHEsRvk{l;EgF>B;9zQt^}pz|+0qa6yIS5C)^H=SgFLt}`Ml zat5IREs*33DhUCPwd!!{rI!()=47gm#o*-MLLtycVWhKIsz?vdn2MIjL*b*E z(gVYUTYYh~I{#;-Eqt9Y53yA_CBx9AMrC!Zdd#4WL=R90(dO>*ZPK$Edw%Wy#~ZJ< zNc1`h#8f~=Pp~gkK6?65`U9w5(!e1*UiB;tiG1O^bmAI3?-XX@l!l3&a?a;IHLf~R z=dheH1P~xHGAbE$`_Crx*WnPF@nu0QbiYqGckYWhzQ`MtH%Au6BQ6gS?ZQmF+MMla zuX?Cmehad`zKM`BlJ1v=sa0PvJ|U?@Lw$_KOaN7Kd(*)aph1HIg78uLdgDlWE9HgV zl^06j0=CKQe-a~#`VJuwREH(F0hP^W<1r%%saF1Pwp%4F@w2T_9H8zmrA9f;z{wJu zX0L4ThpU&5uhZ|@Pl;tMSbLvYhUrJTaRD{qm94q%;R_(dZWnMD1Zk()VaAh#ET^09 z4)C0mh7>}`?(M%vC>S}^d<(Xzk-(#ZMAYC#7F^)@eFU*Eyog?Bz(=k4Bcfmdkq?EI zpyeF#VM=>aS}Jzf%&g?t1{t0p&c%5JR)Zllkb#w<0ILBcT#~Z)TvF~Pmp}q!mdB3^ zf`~1(1=9ORjsw>!O9co9E{yoQ$3Ye#T>LXElnt~Kg76$trvKFfP?(Nv-=*yG7jr^6 zZ(5mPf{gVp?7{1I;Y?t=3aX`rvn(SE$grK1R|sQ7+RNAyf>6dwj&v*w$kTF;&cDS0 zI0-|lG-L71_o8B!#C%R4U_Abyq*L3uh^Spn@^)P^@qj=zhAQjekT?uS7yORzlAI@A zAjCKi+vsr54Yt`7Wj$@@x7Pm9(7Y&y6d^8km0n@lvZ=@#p-aGpE@1;pxet zgOWIB%$HpqeUI82RaMeE1cx)}fMZXpIegGdBIcUpKQ{?9wFsGCo;adI{{U2TK*{z` zo76VOKjtQlyMtqWYX;Z=&r6XGo<#|CVZhbfdHs(=1V~CGyVM4ty=kmr$)M%bG#%w}S1E5lC=CbLKi*aOV-)ApRXAOIZhV*wy9~xBx~Ca zMuEb$J(5Y3dOR1?1a&8W>?+bU*Cmd0M1=`pdDvpQY;N1?K0NF~iF@&DaO^|7)EMk8_!U|gghrhsOFPQ%-cR|-vCyj*$G zeF_pV-}fg%P&5+Za@(vyVsSqaNE*7$q7_3AmH=RDVrV7ob0&OGNxc74MKaklwV4m4 z^P;Q0$2$&@PTHqgH1O&%*>38&E;%1Q%P8^M3a@*5N)BJ#@&Ttu3moBA%X~V>aPEF zf8h`Ckl7>;KiZn3P^}(fw4Y|;n;>IgcGv=AucdxKl=H&UE<<2@({j2UW2NVq z3*$@-h%k%_>{@_P;r9?)Ae*V?+n(rrb?+*|=*8O2TZ7t~9D5dGJ0S!UhM?gack=7} zomX!C9_^o4H8krinE!H_=9~Y(4xSY#08WKnvFiC1 z5c$875X+!FAOn4e=w`3=n>P+4s0IERXQkwMNC37Mut%h%F7TmZa~SEOqH?N{TI=hO1;{c8g&^5J7&{&By&PccQA;Dwq0k5E7x_#I~6oQ0ffU&}BW z@Wc`I3L$#R;L|-k`Z70t62hOw1r)IX)zzZI55N0EfsXhVm)X$G99+EJZyChJn!3JU zag;-*go_tuJ^a6m6y`&1t?MDcHAfMG$qD45l?4ST_GbI`LBFTjQT;MpimJ`mcM#J` zE-?`nHqR?a47&V$b4VU^^%2NIo*f)4URNqo(m5qT%^uEEV(g;8O+svcE6#S?&6Qa1 zc9;@@GXuAadZGaEeBS>8We?-PJG{|rmO!fYN#|aD2EY8%97Pg9PAIfmNTI?m$1{%5 zm{f1CIOEVSP_DI5Z%@G6%>tcqV=I}xMF|PX*VhXpZ$_DV?19Sfe?x0ab9YGy-L#r# zmb2jP|vyt|MNB&_!WZDD<3Lc0^A<13h~dTfU5EF+kCm z!C~%PwrtTbeLv~V$n5#{xDofGDB|7X&n_Dwuzt4N zvO4qrHXhl#KF0Bp2$>lH$_ll8ew*8Si5xewE)Ok_6)9KiBhYw4DFT!Ph`@1yfn|yF z1vXB%!f`}Ek_Oc;x5LWqys|cImCO%lrbm7sylD||*uiULY}Ds+p6K4$(FIG|Dc|`2 zLAH)jnJu?*Te}fn%+#mBR3k^H!cZe;rNTfGCs(D(6aO0dqDGk~PA*GRB(54LF8m=R zamu+k4l9y2KJMf`r!kQ0fNReth7Y9t1KW@7($RdFv?_3}+o!K0m@eIwR)SIg`D8gS zIpy;my)U5Ps?-9?9IK;XQ_MvtzBf=9+QM zSbAIt0Idw+T&Z}KPW^dyiHpg1S)yv5-)pM_Dr|?QtxWt1-+^-A^Tz|yeY^RzgiA1{ z4F)Xi+`$qBf@Q{d^9(<^-$Y8TLtnXoQz-t znug+FJA+mzDXN5_U_Qge=AexE#Ct5CZ;aR`!Tf{n6C%ZPzecfG&3r_@OcWqsSIcLz znNYvhWv!f)GDkJp|GNeSl?Md*gKnoh8A87j*4Q?%C z4OOki{IV0SQfT4ezqktCg97X=U!qguP%Tfl1JrqkEz<8YTDZ0k7WXV%^DTpRn2f_e zlVo?U-t{{*+{eUym)SlKL&UqbUuXY;@UDUsIa0IeJ87Nj52_{sl-B}4FSH~KANQ5=(dkHDHIw}228m(+FO_iZ#4NVv5x_-4H%X$D(Gvq z3KVqm};4T7w70r%HJ9yQplnxv%7dhl~`C zv+xk4!jYu!UJFFN{#~69PLNm(MXbHapHwW85QdQQJJT^s8vJYV@Ea%2k>)B&eTAR* zpcKMU^fL-MZEls|m!)sjt6Hv*Zvg?Jk}I?S$Ynrb+Gr+Kz4Fq+Q6@WRew{q0rG?*9 zVL+*s5m65G`IB+iPxp?6?<=^5u#*`qd3EOcs-3Vf4#?3XSUSew!7Q^xh(y~AAxVHT zZ&X!8`kkQ1FIHWc0+RZ86j(82;egj5wN6F@8rVL`7w4ScibwP{lD}SZNQqtf6cG10 ztkoX5p*`F0Rf&sxmV5jQVPJg_cCb-DPir8FJMbp3&75~ieqqC}e*h~rF+$`sja&G& zE+2QPL4;~lENxUT2<)ivHN1&Eu3~Htwb7f{H(4`k&ZS=d+SwF54PuV@hYE|0M&e4N zr`p8jH1b>ni1JQf|H;LuwYVv~u#x!^w|(-byYbWbAx*$%1qH4L`ZCYxL-TJiuW-Nvgxrj>{{-$R|V>3(QB~vAXCyJ^{ zgj*1)yT8;7KzD4@2lJJ~gYDzqrlt@)jrx)U9`C6o3d7F$(WHo(c z9x2`6eb*J*d(r$GqeB3uKK4yadXxP0W|e$V>%kySrk1h5C*wfmzGD^8NCp!-w!uUR zzf^v9{_`btcJVQFQvDWOYq9hr!@ zBJCC;P8X%iqO)AD3HiR;69~w45lNno7Qhb{z2k7B9n!bRva`sl08ehSKZG842V+&a z+dsJ7&|rvsM=l5YiDzt@9%w-)c`XkiF>^B)!kakXG#wQ*8Vy821R91&a>=UwCGV1t z&)fWMA{cuwRF0c4Pmn>UL`encLle$lsu_}6Hy%8fPv&hv=*4#4?z(Dk9?+H8sk0$- z@^@maQJuB1)R+ee`B5NIYZ4U5f;$Btj9zdBaGbif1Gt{^ZCKNI?nyuRwxWm}@F>mb z?^hPcL1*~>$$d@zW%4PY8@#gc{^f=jiut~?<2+p|m&ZROvm6Fn4D2p1b2tdWa7xK| zJVAQ#73U)GDU>o`J|L;w))9hbEdRX?v`YqCDYPP5nyDV|!NML~c3fZF_3dP>+C#TI z?Qu$UF}9&+>j2`BQ4+yWZ-fkx801A&cb)XVV2C!p7qK6ddpbPcT`p>|QBn1FK?;Kp zmU||>LGW-w1ZzN>(xDdsDOqZEXxC*Lo2E5h}--HdDG_HBiGnIZ)Xw|-wMIq&V&uB<~9K5$XB z%Ad>6=Xkqd{y9x0XaSd(%eN#ZBWDchGrI=;rmJcDI*7b-Imq>-gDE}l1c%`7y@>xI zR`_|N-bj1-w@hu`@>L5%?AmRy*rZ2YEcd)jQLgYE6TJRc9ZZ@r)P9Dl0H``+4W-%DVh!n@4eG#u!{VFo zq++jNzOS%(6N0*f_6pP~0aOv0I_pbgSk>>VXWikDz$AyM0_#d1v;vqro1y)i$`HWD z5b_@usxfNT3%ksG;S21#oFAcS7+ftfi)A#=rH_9^EBDUDuh*neeW+0sAC?A>1T#)g5XJ1Cs+LiM=` zK&~_a5>U9TBz8CevC?{*7A_i)+{Fz7mU4yg*f9ddOnHrsneiwP?_NV_vf=|P)aUOO zv`Zq90F@B``>+qk2djCzT(3Ip8>=*%!$iiWeU zSjL>A?v=z(SKc?R`W^Z)ex`7=draT{jD-yCbN*^xKs|x{fY7Bc9I#0pHPkOu_k8nK z{y`A+4OW>8JsxJF=IQH3JT_;BeRJsz!3054m4JmNQhZdO?t$P$e8THS@@J!wk6mkh zZ~m2IFYH*7AL02)^bW!Cf>N`e;^6sFE9HF-j2rDK9~c56n$`{0{ZzZ#@WQMAFfR;| zftO|^tH8YkCwSbzxC49N^5(SbLkXwvn6@i(>`oC+QVVbxsBQCys z(%5zjS zdJ@HbNB84 z>iec7Iy#z<;6;+Y6MXasMQ#N<{Qs{ydQE>N`f}ACd!k;M0E^(2p%0V9|7Nt8mxa^K z=#sd+U-et^(V`R`Nz z-dw^wLx@e(*rEHc@cH}7ojZN%OrbS!39s~nvzKldF#g9a8$wbvI5vAIM2#<=!0gUxW6OdPzT+$+)xu!ds4G?`g#Az7(<2- zaDycyg-{Sbo;7e!IeWBak0OKmPOe6{eSNZGVC1wtFz_bIeJj=HUvll-fn`+4dnOz& zr2G#Fi5yaXCm_H$vPgjo{NFEq|I#NN(f@^$|1%YkmoV`;z@?M(ZwGJrf6o9(&<%>` z451~&*Xc5lQUB*};%qAeZ+y8B$^Q>uDu@K;*zBOBBl`Rwj>UBLF==}l6k77^|K!$& zt>k#C+9!eXTF6AXWO*$4lf@0>}2Uc)4@nLXfc!67)<+``f;9L%9jh_uc7;2Vl-??1ropk7;%mu>Y1bQKGiZ` z;3BqZ5j-?KOQndd;1jWMo_(9a|Khqt)PlG8?-JpG`=C&k#lEva_5ePWs1y|y02oK1 za8%3!XBPir(EfK|T0S|QVI(opK_P}AY>c#2nG-HMc9Ki6_bk)~Ltwn=IF5&+7E;3gS^X}aEbp(`E z>JosM$ET^NH>3WNvJ*3A;vH}P-VxmWe`t$qiYiS_RemX}3SO&W=M2%da^k7=xZZ?E zE;!Bjf3{XvND%g006lgOY(pmQYy$kEOk9oUr$2!37T4->;TG{NG5%SXXYrabeXVukiKOi^it{k3nxO*iA=vx-U@SAB-rHwV6J1DSs2~! zR^+UqfBcn-Bj_h__cD7-Zstoz&BFdYiBU18zz7MUt=J7Sb@sn^-}3e_Y+J5&f@r) z+0K5a>uHC;5&3d`SD?m@{e>6%3o77^H{|S{^>y44mx^Q!{am88ZCR;Q=&4iuKLS}R zRgn}A)nk3`JjpxP`xR3$kuuKaVcolbg(+~-&Y(6Mqg;WT5z)fQYNh^6_?P-ij2VT; z0VOT7F;lY6n9rUSZvnnpB}H@FW@nY}wVyuyefZ89H~}?xPt;y82S9ps1{*saaTKHX zO#|I4(1$3Ubycq2ltNHz`SS6k(T7BKMevoHoooj9HB5Tp#HWt-SCc5fh(-cyH%&7h;=dEKwGNxtXNeH#^M223 zeeFaG#A%&59l z+PP%L@fh1$^qZXV_ZL-_GMo4X-9S$>vh|<0&n@~0_6VjArD~bJ%=up(UP90FvZYgI zm(|>W0EF-_pG|yA1Vzu`S&cB&wp?c%Yo`9Jp_h52d5fqtfs4$s)zkNSkd-j@L}*nh_g-oLO%}@JS5L>CG821 z4eqe5+;gs8eLWuiT-5frgO67Rvwn*`Zn2KmDBAN52;HZMqW4-KC8(i-P@d$_z%qvM zBUa!jM*2w(_$YtG+govh81e+vsf;xZgb!KhGO3Cn4yH*NA8i?6%&`w?F+QWf4u41QW-n)g?74#Xx0b$@f3vHWy6E_Qwk)fpv)TIwx&ziR3Kt$4FoIq`{DqxN z!wlJ`G}i3Bc{5;VuHajs6~^>o>Jb6V?x3$__`9>r{7C2qZSSf`co!2AdZS}pxp93< z6q*)2(T%J+M@P@TK~B~#l1s{5Ink=nyvXpqDq6%?T1rD=>+UgIt0xXta=Dh>S-vF% zpH~1-Sc&jN;c)2Y=y5bu*)aZ$vj9Q&Ng1EMl&u~gAEV}`v<<4=qMZ550m=`}({*NM(Av#Dl3`t1yaenu0kiqi{#$FF$Lc7P((zx7-i8 z9wpa83cx&ml9G?8&+B=(Kd#{Os0!mR!Qz^jN7nKv`-vi$H$Fza@ccDDD}{1)W$k8y zII1=iNbqf$OI5Jucf!1W{9S?5VB(wG4(qpRDMO#z&P7;x24)ITRg&XnovG5aB4=xf zQI`y6>)LbrxJHi1ekj3WQa{^k>J<*>>sPUsCW>;c`RQ8Uwo^Bd1fRV}-hI!(!4_uO zWgD%U6k2Bmw_>!YJM1c6;-RT+!nFn3CvH8goQ&_qc{WBycCo>4AGn5+^Anjx&rdo$ zhxwO7AvA++j6mT#juMGrcB{nlbrwRC_Sk?Ue-7VzfvV%DU*K-0CF4^Wi-u#2@2m zft&o2ptrhN7V`CAId&r3BlLzz>S}#jCGb(`^XL0FTD$$+E9+Y{AihqPA(9 zx~SNWD%Nt|0S+^=?<}-XZ`R;OR1j_CpqgAiL#KQMYz=**1k~^4JTx1c=NRcyRIDfzcCdGC z>}R#PdM3a4Ku!i-b(sE)fY}e>T{`yY(Pz`N9jBdJ8yP?-G#@LigYqf59OCR%%HwTX zrGKZnk4MP?&N(>yX;h_K)*9?6N%Hi2;lfe}zfMD8=pLlaSO2;jW(bceI`3Pa5@~5f ze!|=gdkG1sJbZ)|?Ah;(h*;|p+q^mTwBJ?-I|kjXhm|a70%Zie>W^!|ME!B@+--%> ze|1#M3D3_-*J_oL=W9fReJ?&`c~ghIVtT;EejQ3LN}aqR-(QED42J{XB^9$;&RE3Q zY8}5)4SfofrEp-yv*5!`9*hEcUPqNf#izAue;tClCn5CRel1P&XA@&^F>M=qUIYC2 z`8TIxP_v(jGhTU+TBceM65UcKC7^zkFVi;~*4j23mmJ{T?>}u+eKMoW-_UgdP6~(V zXS6gWjaShH3;j&ITya_~z11tOCm~u>^2XFOJ&QTi8iO?|GC$Ogxz!B30w~`81V09- z!oTZMpI+WD9&M}tmrq6v)trvdm=N~nt>9eFrL93*=)y+G-Xp>-og z#$7_r$#QYcc-shu3<%^`OQx8v??P3MMpM9Qlmdr5*tPhXFa-qU+4z* zSG0`zd#EtkGm8D~hj_Jh#)@VBE@P9X8rf*sXRI`vWxwr;5H`Q-`18?;J(A7R$Hd0V z+ZHf$EQ(*nh*QTZA|Q|cThNA>W9B1;rvvqj=-v2wa}SKWQ9ap!%2k?1LwLCb#eDdw z3hlhEXtmM~_4WqV>4%;XIIQ-~AIICsmeN}`f!$XOIW z6Hk$7RE1!S$Tkvme+5nP6LoK9M~Azgjv4*NP$gHe5BeK>4X)WbLgDbRi(SSDgIZw? zIJR76IBS$m27>8veonzfhAs*sRiX`Qr_{;E6t(NMPOQibLc78u?%^pJu`^j1<(jj; zA`Vq$V?&@#*ZkC;uO;{SCiSTu9qY=fFBnySYXQxbWBO$9X-p!MnM$+zi z&E5>5|CFThj_(7@05h|^qIqlQO5hSh3yZSn`Ba2BnE9xs zK7&{dtAKIs224nCPtf=ULqC>WY9?GdM!T4YQD~zN+qs&@9P%c1b#*ZOvu&!3=ZD*+P|(AOWkKT2!e!2RSoP-wYp={zv{d7l;TT-FDUa97|7wm3uEpn9 zQ)=@!w{Egjw_VpYn8|dNLU+9wOZfaO+Wn^|NZ5TXRcyJ=(RZtQdXwbaS~@w~it6s7 z1+_fO)AsQdAB|f!o+ZRVD>YTnNfJzq*|I$C#@zFr|Md=$~(Fv=P~21J1!rNl_Y2%`5ki*|h3h2c*K(`2bIUng2w+%`)C3-)ZwHtvd~^A}E}tcTYGq3tii zia970n{~W0%r?L9iaMTyQRnQ~``FxJJOW;Op}Ml?jAAHZuKSIRk4$fu$9lW87IIf( z`M%`_OZlv`K(StdG6l0sRGMI6pyzDO5(<r&}hhwG`B*yQx= z?PN>1TK6^S#DhSk%^jcld^DQ6QEl%zFlSS+_m8JoG0UD-OHWbQK1b2#F^C6$J=ikF z&+ITs9#^WsL#SN26Y8R8oEdUT>nsvUmKe*d7iY6uMiVkL?b+|4uh_Z5ad`T5-DlY# z{@?)IY`i`Y-hwHQ!MuOe(3SbK4GdJ41+-XzcLyYRu_HDgRbPf{EVsly6gGfXDk zhVvqsxrL8<#eWR#?bs-c9VDVk~$D8Ze?vINah6#4abEw7^Yuk6euEtp;1#4aDUQ>CzN`cRf4hk|qG$?Y_ zQC=+Ew+}Wu#Qt&9%#E6J*}C>BM+D~+6G8_{)qdKZboGbVZWSXe`~K(piFXfAR^xZj z)?8Yw+ucPvp-M2lejaL$NM6oEy*AG9F!-~R*B@)1YNM1SoiU>fIzS1VG8-drgs~L9 zc@xRi=(7sXCPE*fy=EYey$)VjO;t`hYa^NV5x8;F*$sS^nN{z`!hZCd0n+1gWL*bksy+<44R-j`s zpwBCBZepXD(|P*ivv@CZJEN5>C;{0v&Tj-_72FMt%iY(G*;MhD9k={rjxiz>R2@n= z&cMfrp(EEA->?yocVn4Z)qmWCXy}V07elx5CaD`J^a{cL4$>vq_4}-(fPA@SX?w(N zZVd5JnLh;#6+g{p-o+s{Y97x-lmwqeb-$)G@;n}5d$h^3mYMP~)##u0nTzx}KHFO&0Om;{<}NUft~5lcbbdf+g69^|1DH z#ncsDq3b5P_}8z{idD6Ed=?LZZr?>oNz$s=KxO?@;)Za0?e}cLDk0$K532<+^OR{G7D{g54`2KbXRwGQ)e~ zLdTS>oNvq7vd>yuw;$PZ*{MRSIIHhI*6*o zPKKXhPX;QZtC}4bo4eLBA^W3QSIGUR=Tn;;?~m?ho5egPcl{Yu^Ywq_YIv)4F-?4FkpXYh~Rl5$=gS`=w(o>pi2G_>>{Lh6qsHTN@27Qh_#13r&ukS&*IR)wZde#1G%n(?4NkgIw`kpB=0zxUDoFy7it( zf-dcdJ~bSzt!&{Mx=#~+`TpoNaH1kkO1$UNB2S&eK5#J0D+L9Rj+cSN8HQg(&|;w> zkcV_fzMFx%mcoom@mNWM=g;2o?{_5# z)T;e-vOmENFl5T)Ij;DTPwocb>WcdLcfam|Xwx_T!3_oa+2_NN2yqBWS)Xv>+1)p9 z)q~BadIBVTzVs&~;=OOk?GlO%M6UGWu&!bT1x_;$qX-rHq?%T6EqXFTB^HlDj2q4Q zd?2+~jKQ?zcUN2`VxFhmyx8*BxP~4{N#u+eGhhm(*+GVq+@YX8Xu3W{m;}8gj7wo);^N< zAChN(V@%8p#i@b7r1G##WZ*97hZ?A;_9CLc*&Y_90dzOh&p<~dKj4YGa_B%e4G(j2 zr?}_y4DGa3ff@;>=uPhrPH1b(23@Bz&~x7qs~+v5ov{%R`kW?pEb+#xcfuc&LI^J5 z4FEGL*6LEGkp|VR4AyZ)A^;;af{S@?n)@=P#y2*A^k0Mg=Y1RTpoTy98eB{(Cb;&> zwc5qIX}>WUj_#c+R(1gMbSXvQ;gnOeyio{f!qpB8W`iXnko%yjF^<)seEZ~X z9!Jy6r@(la8x69*EW6607Im;YQP_w1QtaS(%t{o)CRH)3d1Z`kZQ$b|KV4PTU7(zU%49a?=(x1l zl-1C!d!j`^hi|1sTX@`tQUwVBaqcyB_vlt!U`#>^zFSrX@uZXi%4zslc=XqGSy7rP z{L-&hnrOE(vWGYu3G|VYJ1W+pyz!a*pv|vgCLSag0dlM;J&v}A7@GK==IG@uv{(g% z{XfBJI@`^yxlmjp&@g6h!jsXkGc?xO1z3)4PNW*0#Wx0wx)YdzkG&cegxAAMv4w@G zqO4EV4Xp6X9xtqmi%v$sB8H25KuufqaO%%XCd}$;ha)mmBeu#gbdWl-%y?ZgI_esn z-4QzD_w}f*BO>uF;CI}L1RfeF)(;>UvTE&;h7Zdsn`Ne}eM21PR`MfA}^+tufdjUMI zmTbB1Qpz!_)ne9X(fV-wCLK0Lb0fhdpM}d>)#X7qH1(@^Dy4H>9SDH$i+Va{aY)7V z-gfQ;JYCA(X(ZDEuifBNP}wL%1&RM;_T=r;Nw*VLyM5irhzD*7b{pcRwjTPUhvSbt zU%YqF9|wISI%Bi0$L0KPVUHXQetH;q8e7~yu{?38XB#XQrg)t0zdn4=W`VT!p zM%{)4jvKzuRwXaW1kOJ)8txCJG%eBxhD`F@=z^ffVTQ2b^%4zyOD(>2<$IOl4ARK( z(pE$e?EwKy{5li)&a1-$@u;~EO>EushXCP1X=uH3NGKwnOD9`a@`rEAg^SBin)67E zT9W^wcp{t07W4dY3%pWP$ntCy*L`=%yFQ;LndIH_Z-ZMUg3Jz^i6Ga(#-Z|@hTz(oQWSI+*UCa9;zGcr>qa{LVTr`nE`9E5 zVoxgaiV%q}YI`KlKHe^9mdM-6CoWtkG1b;%lvauKM&a&cnmzjEDvml~XXq{o##RunBA%Qos2kh4NVVMqqtMBDvo>(ju+T?u zv?q+w-a1^7xcb5HzXyEp`vj}UqF=2~F|)QvoJJKgZKBK_=n}LrHtiXoLPYqsJhINvyq zg70g+YrK#u?>$<>kP1O1=K{_6*`e6?$^U?aF^-%d#!!!%F^yX)MZ;*UqzcAgh0(}x zRr1J%tPJ<7$t}(v$>K)R7MDXTvMI&j<+IR}T=6OUc(uGO)?8eSB|-6=_(5kj4#}bM z@w{qTRyksba`O#^X^!1>#f9wAzC?kpVRYh;_re>EH9|HfPGgC=4BAcD98j9Uzy{ zHR-0&&YjX$Q8mo(s=fF2r0FzH2WYci;xU~rvApeq@0dR2iDsFT-rH?BtlN;L%fjmqI)|p*uyT~nJqxJb+G$AI#jI{wn`4gX}F0NTq=|2_PITL{{|h;O{7jKD7f2RZ}S-*9eX%HPaNTcZP1L znjyi;8unHYer8K8@Vx6D;N&+-{c3-bXVn5WG}L}%bzc13Yc?yKIrs)AGd2+PEGQ_T zA0kTJLH@`nzLod1tM{$pQj1DKkdwR`CU6->AX+A%y+8Um3o7RW`Vtjix7$y=o}9|3 z^WrlR=_I{=u`(EdAnRBTl26q3nk+^|kd25jM z-HJhcX;E2M*D2_4w}Hx|689OjhQV;?QZw^61CyZrgH!>GX*P#6tHIq`atpsr2;CuR z`Bb$MaP^u0Z2eTQx64X2@HQ&u;LmB)QP02qn(#-KOzl>6SQ~k7DPnJ2I0yWAj`A6- zwJ$>YOsC~xUq1DMc3>7=jgZhHKPegj$|x`loJPwc{jhTZDq8wjhN5!3K33I~0}#1Q z8?t%UCPf`r{}Eq6Fw|<{Vi2M!NRypGoYI^(bySGvb*dtsuyneyWAxVeQ4$10YTfI} z|L!#N7-sf3+Mc^>Fj{FDNJT>ew*h-JwPMKz{POUaXQnTEx{RY?kew?|(%%`ma`8Iq zgS+P#8;AJFs4p}w-4#P~spBDBsHn&PNE#Yym5DX~#<&*ly70?U3I$W8K-T0d@1x?{ z11hR1UUxySn{Sb9so0*5kz{0Q&WEo6V<)FvnZL<@a@)}waXO|9MEyffJD_0?!i^}a z6!T6wIa0^Eu6%v6EnsV{x+%{u8SYIpYr4WLWXSUH-8hy%N4xfDOWyiqwyzYiaP?n= zKF+!oYgXAQkJVDcr~D@u07TUy-BEgHvUM$FpCQVzGeaP}L1Uy+MC5C8sY4${KGHH) zEN;4yD_?a_6@z4&hM|Xx>dac>!ep|D^R>IN`4i{|fQ%~O2Zt$jNIc>AJ`L>yVb``n z`Ysp|9f$UR)wU}*9>LRrQ|qvc=Q@fM4dzVn56Rrwh_0sNKDHEbMRLYZ@w%R?*rJQP} z)S8UB)k|V`%4jDR-Uk;nU)pwcW5DV-6lIAUTkw z*&-zL!_*K75iXMoWVq)$hcolm&E0_#cWW3|GY4Nqsb%L+J+;t%V(bBupkYYrN1gew zX^RNvKG=tKR;@WEpNW$OKGe>qa%^Rws4SWG10QSG;)e&2wxH8mrC4ZYf%FU_#+iG; z5(s-4pnVq^$RH7#XOICxp8Vz1_CvA>+{3#f&gY-Sgw-}{%KKx2HFav zz7X6V6{!k&oqlA`R=iluJtNKT#_i!Q#Uey};l;)r+ESlUBOl@p>1LzU*v$<((NmrbJ3Ngn(2!0xc3_5K z(r{7hpM;cBCWRG9Xb@vDCuXKV{JYH?D{UBm2OozJ8tm*J)_UJx1sVb1e$VorIgm&G z;(qtMecc2mZ3TV*Z9501HzEF<440i?kR@lBOKfGYw3=N)8T;@B%(wuZSP}H$uB!`% ziTZKsI0V@bIgQJpXP6$W(9=x?!gdEUHL`3rJj-lIh5tmb!(9CIf`WRYhAQxR)lY-W z-{d;0ETcm~RiBY%t??I+R_QN$;bP=IpGg8I?oYQ)T;DHCTW8MfROFGZs}||)_p;QVLjrm`YXl@JDb#x*cY^? zTOZX65M?j$QfE#G+v<>H#s9FpcVgk5ndjrSZ#iHJ6Pr-eoFrG#hgn$8+WsK@JIDAQ zCUf}3s%w|M8ch>6cozkNN$l5%U)#ULSBS>%l_MiBA2GxP?AY=?8i&AKe^pJ*S&5^F zLNeI5-dj!GKiRURo5CV9Hg>k6_k9coUDM^$A2&*x*;yHc6ZQ>7q? zmFZk}hMD+U`s}lm;@ft_)s_lTfB&$sL(+qJkuaqy)-g!gQ< zzK3AC;9KoapD;hpt;aBw47MDMZ?;bcT*VRG4B4x@}4 zS>_x2WbDNGo?ej#)$Gg!Z4L4qI#Ny4EWxBBd5Kd=InoOoH}`S(R9k||BRkh?wv3E; z?+(KrQ3fmrBNcG7H~R?U zGmIgP>VH>xDyKN=X^ zym0%oGkOLw_#h(?@ADApJ&DmuqYv%~2fEqF#{KJ}L-_6yG*NlHxmrP*DrfY`Ut}ZGMm1 zBmk-2wP_3w+iY#Klc4`uxqfm>-y>!%9uT(6Qpf6565A!aCT-!N+M0p-loE5Q6mTzc zUp2v#nH`EL4d%P*{d_{9ki4>JI0GYj?BwuH6-^@pzo@|2e4N62Fp`WreIh>NmgBem zTZe2HFFW)k1}cvX97dR?)aQ482*RR|dyCvJw^Ucj=nS!86i(CR#7vn=v-4L2 zTl}?gTu6uIvMQNz2{5yE2-Zo^c8S%O+qr)A8h?cGroDhs0iz1A``r%C+ifKm7aFKe zpHtJop66CIm$k+n#$&?CUtLIvsq4f^Ouhx=w0kgIYLVnnNk?@N#leIHz$s)sT>HJv zWWIXa*sh6er;1ql4}vsU@XRQl7`}2SNB^U^$RR8HR3?{_MRBUp*<7T-)gKa|tbZ8W zO#fM^+zX8ueRVLK2klKED&pt*=vGssJlEVf{PR6Af2&_xLBU=uef81oCoqHfYGR^n zvM`Ak!?+x)RVS~#Q&Bn5^R720jsg+sy8c=_uv;{kIl+6If$_QRV&1ws z7}LL4lS*2mL1^a4xl+~lKv43!WE0cA1`=A;M}X0#tS9sH4+>+a8X9)YPJMBQsw(&E zE^A#jcYOK8cuHw0iz*}yElc&-WfBAy3aDndx4fh10*OqWK4OTvc}mrP*6=0XYH1ABO}r^N-M={j{+1PDB!9 zI}5LLYrz18#RV)D#$p}0ebHvvAJOFNmvK(nc=6m=(!Z}T@66EFB;kLD?M=0g{z<3W z!929zjW~K2;$XHsHFSH3zmYE zQk41hPqBF1?Ve8w2kQevCCRM8%t=&%LYziFNg2W8EhUm@M1-(ZN({`+HzA?>pb;-L zt?xE5r+>gbSvNu8s_=TXI9^-LbyFPVrgcUjD2cc>Hs4LESVAL)mz(Y{d(DRtAlFYT8YL1KD*2YB3HNG60(y~zpn6#(&=dFr1K9&FG{Qz@nB@0KCc%9JTQe*4~ zVPPePW}zhvN(pe%&orejS58LYX-&sXU-)eWRY?$$riW6CQ@NCG5cVoWt8%jxTW>N( z1Uo7m%}2TkqD1hLwRLJ8sK9N3?ERZH(pj^K*k@#}%8Ql1OHa4cF8kwxNl1J!7-C6DC+M`y)GNE3 zcEZiZNP!R5OH$e>54YQA0zUNiQ(P&hxoJS#b$%|kWd$CmKDW!r_3d^D|AX#ga}vD4 ze(N3eNs6xaAwfZqI>&RArvPUmmLxK$^W>)VV^T= zN+4yJZF*@oWr7CvMuBB;_OMDY9g9_hQ}6rXi5@7*Y?99xIJfKV4ZP;MMOh7lK)Eua zUd&7O$MYcmn0dI4{q`<$7cKNB{Na}{Q+!w7NHNOtc$UPHRCXkxj5g;bdFe!h+w z_N5Qj3u5#f#ve|cxXo+Now&o1okGuXcT4VfNOaY}yT$eOfwm?+pQAfhFVlO}hTK)azDk6(rcttyScx*QM={O-d#p6es_npWfT;k=|> z&{RKzl4T+$$$YW0J0cao!7PS@^ff@s?XYB7``{CJ=RK*E%43osfmAO&^2cIXItuABnj$gy zjReVSaJj-}0^`Oe5G3NYJWL-iI!!UOylb!}b8$JmgTnp*YxiqRTRN{OiA%-#e!*j5 zO)rD8D~#=p`5->q3y2cmiX@Q$j(-H;`W}qa$MB?fYL{JkFQsQj@<)MR%s0kUF_d}k zk{w3N+%7o88)s&sb+9cTvnK2P`=r+D8CDzF_Xdc8m5Wi~;BI)HnvM=@E$^;i2cm2& z2ZmFpTc6@C4)Lo%Ou#;qrhwYQ2(u*3e-XL&TL{XV6ArOJKw(wirKWtPUhxqu|3rgB zHME%m$ZpUQbIyl;}Rf9fQNU#86TzURG?9lD{^%?Kdsfc zM?!L+<~E#qh#K_MAjoi@b=ZJ}m~Y9f#O1*OsN6laa1RF)AsJ~*eopmx6`EaLkoT9p zKN*FE{jFL)91?T$SsVsubLLC)R>HV}W>ZdIShAk@yhms4c+y;9$X{z($Z^xp?fG)J z3Cjl`pt+sVNTg6s(;>*G%rC!qt8x*pSi6iic46G*%>=14mPL55`1u&N6loUh1EkWt zjD^*NXQf)S0e%1Fn2nBUC9yd(MBGV1zyP4j=^eew5KWmiyf5&T{iDU zTaEKr7PBuP?TQemWQ20JjnV54l%|e0E&`P%RDUGz zuCU7k0+ieabppIM+%nXJ9ODy3TZ3qDa2&%ax)PEeNA&^9I`@cN1@G0pWO!~yl8p(u z6KZF{W}7)GD_v&1zt?;J_Jh}Z{LlHbdpsX;J7VG*qLE-=3{;n`jxrE$(;qaC@zViXzw1dLUy zlz2``TZS8yN4*63X0ON}T>}B-#ShD6-ZWjx@_!zcNsUh9+1Y=)LK52r)UzTxdlb-v zit@z(6g->`vtNNgE~-qAxM8FafPQ`b+!UbW^+8;yuf_?2w)pG%~rkR*LX)1 z1wvQL_XWQdW%aYDRGr=2x4&clELC;P!GgH@>xb{*p>B{8qGoYum~kW-A3M?>2FB#+ z&95Mc{fBPdN2P*WuOHPt*KuE^rd5$3;ovaf5uRw8*du2W-)w&3;q7Hq=>W!$$gtp{)^aB88PG!7S>0qwf+&6aAN4) zqyli8%?Q39>mUa?dg`YMTM7t`iVhJ`98R;mzSMxAiVB;8oPoMVK1AQ!=wR5H8o7iT39?twUNs|}&A@~NgHz@!m)0%YQlu&Yj5-3jy%b8&$)3i zwRhFUw$ncX144-Umy2Ndzyh)oDgr@u+n~n#AVnqC4+D7%zzLrVI;5)7SbI*)F#=eUw_qT0XhF;{anX$S4?VZI1PX*Jqby1 zHdA>S|4_{6+BI72%{rUkeD|mx=O&ZOUI;B`$R$&(oQM$AfS=dAcNQDKk`kFYimDw? z)(UZSF!gExyytTWmZ!Yu4nbyVf|DWFy~g?Y;tuD#R##hOxK~>~ zB8v)g6BsZ3`R%&I4PH1J(=Lx;u2s*VfJ z4v?kfc|p)dpxwVapEx2KBSEuH`6Lf@iS1-$%tnu~+3YtGB(W?V!e?b?W(H_$xAodb zxq#A7z+=B8KCBffG7&eGXBP6@J81s^j*K#M+1snyv*USzE-HfDS3FcwiGTZ&0_I0p z`5EigOCE79wy4w;f=?XvebJ<^s@sbr(JDo>pIBT<@nQAcS-B_X=jEyxqEfs5*UdeI z-i@o_TG;#`D_;Nv=Nl)Ea3gC+j>>tR2jfiVae_UTD6xARw;b-#e3B}WomiG0g@ z^N}pt)MzlD*De=Kp#=42sx!q{pi!h z3+lNABmXc(0Za2~< zXoBP8V}kRpomcby?RcUGk(ElYo^|^W6r4F_2F6#ZA^hcwtWMVJJ<*M1Bk~PF|9{~G zV|416r&B6b5}UXb#-GQ{SVpVdcVt0C1RxCo0)vPxD`7Zve*+50lm(wn`p_llDeh=Q z!ZMYp&$fmxV=#x!Hs8Xl=e$v`5Q<7Lxqft;Z}G4&Bs+wnV0OjC!;ZiTvotux|5Ymi z9YzE^b08-GDHaGwUxoc>*imuuWQ=rcVV7ipHmHv_1yxbQf=Eft~ zg#tk@U}p=dbBn*uRntHMc}k<)|3UyBo^E#AkE1WSJ6EuAW+#h2VZ}Et54^u`_qvlp z$1i%v(qChxa}$aS$pFXa?(29_Sv8f*VT509NweBJXP)dmANV0KQ?$OB7oS%uUBWp( z#OLXlekUb;wzZI|o+y(olRst}7h+g?izk83I6Mk!^U7`iJ)L5d?DI$bgG*$te0I(x zd1@9EUdr&mAIH{DH)opNN_hpD6D2Aq=Ghw@QVPW*+h0zzoIb>6FK_}JA7_GUf1sRc)zEjUEs2gH2?SjL;|Mu3yJZ@AeoAc{16n5 z{B+26N-YlZHOk)DWDWcEuNe2*qv&LB-t!l1YvN|pTX{gIyj zx^m=vAZ|9Cn4&t_Bus?GG|K4Ke!MVAHXLu0+X)nGQN>w%1Kz?!itXGGR2c-h3}6Gd z7#{q}Boz=PCe+rp#|8bU!hU>MYmUR?6D(l`OowIFL`IV*t_Qy!pw#gGwi8CB%9^{a-F`C^~qM#W^e^^gDxcio3b$&Vt z+UV|in9O?rj`AW%gk#|k(8gw+p1MK)f(qV~ojHzGKqzMx!2iz@iD8&Fl($G1ps(~` z-y?_2A^e7|hcYMrehfoPr~7HS0Q8yw!WgtagVxh$LdDE2&Otw6tu@DEo3h*U=unlO zBowZISON>~;>3}ij;TaVa1Q?^t?Ejt$t0sQQE9&5`yR%rslR8LKOiHeO)qK+`#slQ zl0}L&I$*%NzXr}rK;#*8Wkf?ml@nFf>$tV?2@Xzx+?R@GqWL0@hb%|eDmaS#WB&I> zDP|8&mx`97tcZnXPcDZ^xvbs4AS4V&_gN=*5INZ0}PA%+2iTBPnWtX9D(U@!)a}g|EtD=|JsLf&e}99nTze)>xeF#jqa8drIq0x^s7fyH(?; z7;xZTes*0-cjBZfe`k_ZQt~;dlR*Oy0facplwd%H3o-dIYP0w9PQsOazXF; z+^z_Th3hY1?guSKaxGk8qh!$~UNwthH2VBQgTsf>{F4V*l7VZj{sC?hwOhWR; zwOsb)PyQdW0xw>Fh&iw5Gpd~BWX`?ln*4t)0F=10aTZhHy`jM6)f7s)v2j`=55{|o ztw9&*DSxEXot+)2puz3%2`@6aK{MV&jFstd6m1I|99a%q0F`sPfX+JCC5;LSt)H#-rvZ*k;o|75l?Rf-E3I>Q+EtqgP zk4DCe5>P^UCA~~zUz75X>N@qYu0DfP=~O&t9F;f$Z1lC8WbN&%BOlc`n^@jY zyu4wb4U812T{_{2EA=to9MaWJa@zPN`io4wU8_*3iKg(N_-eBL6_y%bbF>l$!WlQ2 zS%oFqGOuG@S43}Q)Qa#ukWk?p^-{FcO6cnSN}%UM`>mqtTkHTOQWZv@B4gMnByFRk zhT`fzzdP)=9CEUj4#$FP_Z<%mmv6;F&|kW`T5ib%1RK&aL$?xeRg6^pXZ5-0uBq-! zzE5RJaab)*Xzf{Ngaz_^cm9YERsw=2AFXe)ZqR`xJl5P;=Re}t>*IshY_V4!?gP0FF;`L`)5UD!tp$n1TPd-(K~MXG-zz)t_AokM4c`sC z!F6SIj(j4d(MZ-56Y9X`kaXIlJ-e<>-C9PU?@w(vcdzGq$`kCO6(n9F_PWaW!|FN{ zciFp>fB$CRc;2p6iav#fm6J7FO2f)qF@*>Jm-+M1Oai%PN2gsI~=pV--E|cB$52k24lvUr}og$}BrXLX!@KJJ$)2V0>V25;M#ucf;gWtB3#Wxb< zs~tHvkMI*2iv9O>dyiM@(@I38B?iA*tjCF!ap2CPo112RBK%mBhF*ie8N2rN&d8Zm z0y3eu#BgQ>40!exp9K#17PzCjFNAxm9W10GVuWy$B*1|G%vvNS$|DE}DP9aP_zZ2i z?UzIu@V=W#c6BN(hx1ev0&mZbSGLvbinMy_lic0Fxe*)pU~?j*(Ki&buei7Of5c=s z_7HTtL^>1~!X7qTEjCM(80LyblHh2Ga0#huD$J@^60*PhF#`1WD#&Ny40D zH)BW3qZ4zkMZSZ6^EX-n62mP#eFG3uFC(Pczp(iXNIh+?eI zi|{#Pi#+SC*3e&?eAg@T%)+7M9OR#Y+hP0DXpxuG8ac23rmn8Ao4znXk$pBDepj{W zVAAEo;YH#@e8v)Jwnl??(Kjq10~^%{OhUebk?;uz+;?LtdC2ccDl03#3{?zI%T%yx z@E~N@tbTiq*6e9qJ9D(Nx5Um%Ww7?^Q<3izv)1w)%s-CR`DQp>&wj8kR$aPPhea+r zuw5b5a9pP&$BGdmR>5sENjS@*fG5r#A0~FSCc{sVJpA|8+E5;acsPGbiSLo}=JX4g z@ZJIa7zyD}A-?a8kJD}rhQK2s);V0i`Ks@alTx>%Nj*ABJ!RjDkGY71?w5Ml>Rwb_}~ONb2j!^aY&zi58JFbaJ3f#oznM9BYkYVQ>fFV)Y$ z@RTyM!*`GD+RAc=buBWZvGOtdFqFa5iPgDLnAIS; zmQ}qH-`GmDNRd+I-bo=xa4$HIyzN`z*cvW)|7qwr8g2$jCi=P|Z=elrt;9Adlpwoh zXE1BKC@^2NPR7@*GPp2k$3WM?wPm+yDN03mh^BT=>M~ZrE$k3?hhxHE^D>2Lr|9r^ zOTRaxCue&1BVB2}V+!Rbq(eN}R)31t_J&hvQ?&3@D(n%lYP!u`%Qry-BMe5HD8?uGQs=IsGBpvWf#$g3@}fS zIV7i5xG1C*mV0vP^JI3pJ<7m90c^Uc<3(IW)+*T(T(HjX!-`K+HUnB0J-^E&$n?f6 zIV}=-MMns%FC`a@EG0W-3~G8REd0wJU0M^dK_VlOA5h|b6PsWm`(bv4@R3*Tix6qU z?jE%8GZ($-ml6iYO=E23R^T~3h!G_m z@{F_yd5X>dKBn{|sN~|(EmXhjevi;|6L3St1y{Idj2GCeLmGiJmO{@+%&FnxAn@(s^=M=u5B>u2 zd)QnVwqkXqhZ&vejsT^HJ4ph9XS-``>$Pco`*)y@tJ>MF;x`$d(0U^UMC^Uz+Tdx2 zc}?0PVdm?G))_*l?&X6baP{i7_`~kkhh0dlPV1i~EeEa6Fg+&|e_chUoCpD7MLz@I zt6thd^33Z%_`)~gn=H!KvdiHZn;OjL3{rs)_U1=hq@bxsuP?4rGVkMO&y2qQdasf! z$zQIrp*qe#%&d!-dyhErDHduE5<2>MI63$Hm%bcz5UaYlFPp-mAq8I$r(E}c!)c0YDa8LYRWi51m!FMwuD%$18iD8Q{f|uu& z`oTCcM*;ol-MdRWvlEQvwEK}30mj*58D+l&Q|HTWdb{LNZITb|+x9^>&MmL|{E=mf z5pBIZ-zM-bMx|ItNz%A)L?#FZd_+P5Qr z7RShr;Z9F`;;1|G&3;1ml6uw{GGvc0%QsbL$1S%z9%9qKip%!TwubK$-U~mfVTDjg zV<+rEgOSE8TpOMW1Kfs)=7ZM#fDI|l*Tq|fxIGLf+5S9p4FN+tZ5VFlp0c|9C;fzn z(`dPrv8muT%dy%P+tU@B(dksqd5v4dd`^CM&Nv3#@zo1UaT<(~j+6E#2Q^lNRz0(_ zMtHySP&-COr`7gD$`(CyE6<5)w$@AyDOPaypUPGLZgMBmQ`oGyXIt z+GTUo2$jJ6Pk3uwZJ6IljYVo2>&1U7FADP>&8alfeC0;KY78BmdkXdx^jA|aQNr-p zvzBAr(7O@x@AnRP3Py$}jcHU}(tlm2wXs~8(IJP->dFl$nkDwjY~CN|{aU?CP(e)= zFvd#Uw^+8%{#can%8%2kEjNcqpdSCPd&Q4f2JV*9)174Z=!>oj#SS%HI7x@_QB4RU z<>6qViNSCibLoHf4#~-2Hq7B9`1S=UMizKGpjZKMW~gCjz{hrNoM^%ld+eb-d^E)rLwV!QiZ69 zXb{Ha8^^=q7KUZ)?W`D&2bs1%!gn~9{ifM@!aqco1c<3KnlTjA(q1E^7nNNme+~+y zemWx-)lU0a{tPk1Mp7+)HK=``uQW8=Ty#vk9E(t)vSbkBWxB6y{l6vjj zwIJ))+jR+6XFEgRAN}xg*1G|0DO?*MB?=go=b z%61%eNQXzG&R5n1%lkj2Fl9?iv4QAW9v(4FPR^}_bflRh#0RsjA$ZbyHFsmdOr6sAV;WY;1TcaRT}HuNffUpfu_ z*qy*)WE4`oKJJJTh6htOiahBqaav;lI}+b~&t&yU(ep~H4HhM)ks1w?B8Td-%j`vQ13Qtp?rT1#E zVyLK?<7C?2MxWm%FDEG}H`l_y{FW+CkROuq1Vj|MuG z15&zIii)PBnFXXIWxKoF5*Bba*G=Uo@4g@6pzr0q&s1u}bB4G(DR*sPnSN34Dzm$d zI4GTulY8vmO6j8+?{4upUK)wmj7jjVvc(-pp{m4mHeg}bR(*6@Vsm@LiBJF~#{MGY z2VbO`dfXTDGI4*}I>LHr>`ZB`^$xz$HxOXhmXv8Ox0Bups|d|2)n(qX>Ni}4mIP=w zg$5`Qp$lhgznsV~UtKEE&PTdU+!erG*i!q!0%jWWaoe?OcAgr4u+eE=I_)D#caJEuSFWE7-Km<)YJ)i5f!0OpI?X5aMyVdP__ z=>{(|M^#$n5Y^fD^o8I|;wI7j^wUROwB-XT=Q}TB`4QY|>e<{5GDqomvs$$qOGHPe z#sjpqp7#GN)}2x)m(T6JNNzc*H_BNkI^E70!Xv6}xCf$!MR}j%RIlVfUbabF$yKD+ zovyt0E-q6Hr0aN@E$VxCC^3!LC$}9OEA<)B+CjH_2vUyQ9!{D` zs^8$A{Z&GX_V;JF-#m|RJk?opcHQh~<}Q;wAWxkvIPx(XJ?VYtABb7ez@ZK@y{%?9 zsn!SOlRk>atEB;xSw?;A}jAofnsHalFPr z45{KvyggVqOmtitqp1@eSs`+`RA(>ptAx+LpR`rV9=?enj*~kj0zoidKp7UilKkM4aYzyUmm3Jh1BmpCvdH zOt%~jl~CoRO-ovE;44WlQW*J3tz0J%>uiH^zTp|da?F7kHPqMFq^-m5;kx1VNGfdq z>^+BmDvOpINbmz`Ji*@=1+=}i20x>s6-~jn-kL2DIw?L~;6x!6w)x@`6E7K1C-U)~ zwm!vjrc4`x^k4Vg-OX9&S#ep}a<+KIsBD3+8AiUvdqq|Ve~;d#MURBE!JbUdZo}KK zJ>oqKMPtBD_yX90aLpNw#N9CGibR}aF?8N}fe?a{S`hkLE<)zb?!j0N|7XhXr_wLF z{$@R3f!5-<5c~;;O5R*447Ya8&a4)Gci{3v9g|#NPeO+s7f#`hS+*Dl^d~RpP&5)~rF3sy74KdzHR3B4Af=$sHU- z#4J|o6umxwb!MYekcy$b98+m#Of)c>m6b)$LR5!N$Z7nMB&%y|MZ5Lxh#9(QzEoXw z5Ub+e6XC=_`fRZ)bedO5&1SfXdwfqHNrwKFUpV!3v|IE<*yZHJM_Q~P!D~3yG*%_6%_{cIj(U)87G(!7*d>`_p9iUV?71?_;I$V~s0zTia*AOKo-ef7? z$s}gt^2|9rG;NN45d8wPzh4v@5iiPC^R4V#RKKB3VtN%O}9fAz6{{+8`BG)SZ`JbWgMT=;|aPii$;U1;hI%hQbz-3dJgh{;~acFEPL04Luys_DNIOR_S-}iaQ zGxGb`?W|Mr<>htw{saK9Up~9bazkFFEmb^=3j^lB2ht~I; z$(Y*F>u`BCxy;ZH1)}9Hdu{jk3oN-SEEP-$dkt_BwZ&Q-AN9kxypE=SF;Wb}-}p-@ zt&;8Oo4A9To|pi1Xr&=7wbtd7xfs@GR< zkrinEy+6cK4}g8$g_ z`c%Ol83+cb66H}5Y!AOi4#+g0xovzI(BZjeO3fmAeac=IhXBW`o6w4hSsk_w`7vR>CWwu@e~oElS4^4 zZl~fGqra#1d)^RxOahXAKZFK!qcod!z^~N-7UqzHL&#vD_mkz)fhXd_TGu5oh{EfG z$|jq22^~Oq)I~*QCNf`0#wj{;zndH!Kylwn%tyk1u|2F5-1oZdrb7qEm9+{LFxnYf zy_3f2lg1#;gBSkTg}`$BB_Qy7Xrt>Con$VB)TP4hZDH#2LuJSik~F-D(py2|#q>W{ zIUg>P={3>T1XvoMSgu!S1Sya%a@$p*qQYDl3Cu6oo%0CVR=SV+$~tcz81I$C&b~2k)sw| z_K26GE~>Sp0kP&Z*(%s*ctb+cSKFP@zAq}ofXlh%;9>?YneIH*scO9&Z6G+}z7kI= z^&f$YOAbSGXRpB*hL-%mu+89L3tU{GY$7J1*`jz-h6uKI3!=dLDhN2U4;JUI!np!_ z0k3ddYa(#@cE3+r9xg{K3D}{hHE|c zNf7u}rGDbse728GK)WUOBHbldn!8w8QmBog`6IGW|IeQpI65CQ3orJ&V%}OY&5op( zX;zBF#$aBa^uSEymV0_Sv?w=?skRsd*)h^&o`RYs5eK;I0aU}% zIEZ4>7lCb)@~j51n5WYkg}fexPDfP-^La$v>}w$m-1{FE%Hh5yZ`mDI3w@kEZP1?x z#T671vDy8Q#^A9_Ci=zu`=y0pI`7)c@|ZT)^ZaXhO%ZVx>x=}4{0oQtQb%JO?$=66 zN8R9*ZX)G8M2~Gkk*p7KN@QdgTh%inL8pE_OPL~U#~ot6u3uTY-u?LZU~ z+UOmzDj0Cmj)_1)du_D6ho&hmzI1&O7Za0vPj3%7qD7>}PjcaY=6lPd%pTGfVDtLz zZv8Lu2*zZ{6j)C9%j??O5L@$Nt8GIe5S5zc@M-t-13cSE16VXT-n-%oHzyM66xNbh z!DmA?VQBhO;r&j8>pKD3?y~DZy~wZkily3?s^va%)LrB+rMay%YfC{Syz(7sYJ0{V zXG_g%lA<<@XpGcKy_v#m%%`27g6r5dL_`$QVrz2paHP_QlfSMV-knD97S8?oO{MY| zKTIe)FzB@hw3*)R(tfs*!hM#>P{3!KpfvkuU<{AzjwX#C_1apXAwZxZ3`#*k;Qu>Y z_EK@bRGZBd-db#&67+xR?2hh8j<1=6vPK*W50wZx(Lg3TUocr}tUVZF!>!k3vm-|f z5`Ha&dEbr*%C*K~iw&DoPu!dZpF)_#lNSPuczB=5wJDHW9`iD8 zbghY^_N#?0-5G#U)^4f}jThmRSfJj>q)Q74I5>1|r`H;pfGLI?`d=xrqDDS#^o2@n zQc_bVFbTIh+dO8AZxVZS_HO>RYAx9TG>;U(Hn&|N1K4y-jh-a*V2RcyWF*sEy6cBg zdeG$lcAyN9k}irCARuqh&{$H)u%iF?RvyEkCJ%@oO&6DAAGp-VYtrMVN12tn3gM8` z?g+N0Gnw_1q5u?%%nG(8R1JBqGXf!@P8H=~zZZJu!GB%O7TnZIlyQlz#HAb@OTN8Z zRFFZ7)SW5>4rln`!w0LTRTpYClYI7}npaS~U7Zwle!RpWqxxNgYoPumgvr zJA(Xoc zsXF^jQqsJ90H=)c(wgPKvv?QdB!&8B{iFS+UZe32h5j_eA82K5!GUBf_S4ih;Ild#nc?R( zpNI@BH7oHwS*X^JJGH-b$&)U@eTMTt&drDvot70YUjX^RsM8YF%}i_En%;VY1F+XH zB<(ucsoJvb&+`QeYy6r0UjBd?TcZfqL*L~r=cWv8}=+=bz^ zRCgsAfNx8`#klXMX{lWl5qd=7E5`f$9JUF;n`JiH+PzLUK=x3Tn}Y?EXUiT}eOZ~# zSz}R2X5!LY0Ol=w&V%^_$?)R&9n@jka2)LFyFPFN4lL_X86jT%9(d3+p;|3LL`ZnK z8lXqzn_OY8;-VNsLf(DD^uc?~q*Hm&;d;rl@mn?C)16LqQ9va()A*A&!acG3Xkz($ zd8Bu!P2arh;v$ocCMlKVqf`s{g+TVF7AE3$HY9}E;9Tw;bM`Zp)6}YYN=|;rEy*KTvVC6$uN9nM=s7Vy zo1txaGYuI%S-87lJVBldJc0q~Z9}$x;v6wCN4Q-c#^vEWoBQ=K`H|&1G!p`e-z$n%lIdYIrTfT*Eq1ujIm)gWj$ zUWYq~TwekpnVLh83axw%Yc)s^zU+wAbe zK=zWQ9D)TawT2tvT=e_3OBr^PI{-1=)#kW4@_;W~-ssaS_lu|0sLBVCR#;PWGJ;Qn zP*S7lzEkH9N2Ml=9qr`C<`DkRXMWp<$6D!Ug2g|({^O~md%zE5hra@XO^zgu>hE4s z+qDIx46An^_n}ZekA}xEwgcl4Jg@6EtF_MQAT(g}8CupVtgpDC!Z}DndLDu+6EbW} zZQFC^T}$oX8`LUIri(+;Z}mc6z8p^7ce`3F?Ci|#R!7NFMqp6QNamSOO6pfq{PXx& z*eNuE+$R-L5^1}{>2$d00FT36n@+sH?R4^v9#bZIz+Cx&sPV!9? z>7RKyP%@I6{Q=;BW*eY?IX}~Tc}bI@HD5{&vo!nY`7+pfG-F=12gcW3O{wTKH#u44 zoIxOUvmCmYdvBL6Y~-knDUe>bW;0)dl!lC>-gRI3LBqJXjO0!lxA@h6TBp@dvDHxw|CeEp?{VrE0^tASeRZmFEM z?H%r7w2XRGv`ch>^W%MBER6=I$a8GD3|P+&2Zh_1$}d&%;C!MVMk=fs%Ty4}+4hH6 zPz4NJKXV-@AtvN?AEtDW1%;q_r@2qAC*BTJEQpjtO`NXdqmne{s8o7-_rxACfyD?Y zNlphQW1pT=usMt?CJD#J$=?wEc>zOT3;+gJE=qTCuP?Hi0h3BnnC}>RcYNGBolafS zo+k)776Eu*@$w{+E1qbGp0$xpd!FWaYQ9nXk^>er9`o2!qG9pf6@RO)kp8R0ejwH_swKkx2nF#O{8Q5B=8>t1u;R@Iyusn&WA zZt%`XDnH|oxM7*V`*+0XNUQ-(A%gBVagH3W*$8zub~qB!AECZfG5nT#W!nrVS0dPZ zCdIRM&fe)DGE!9f`d__hlXjt#=g6-G9gIQ`&j<$Lk~4+oU!B@|ZyEJO8T~)_2kO3? z`4O1nhHLXNTx%jQWx`CGsaj^oMS8n_4_e~Vm=w*fub<#5Czs{c75zzwb^v@SRE8Mh zDiZ{1yz|!$Gz`SrW-gV~H_Hvu8dcr?dQw$-)`*PbgcccUnDWH@F`kjhKXU3`!ZuD< zO{AOY`H!f|&`?7~+t+CulqD5bd~z);TCL2LO__6Y#!x`NdfK>KFfR*S{nPimyTXv0 zZTI_N)iOH>R97Iw7G!DGwPXzvg!#cr{z{A$6;B8jQrG2gQZqt)zFB&Y^2Dv8Zdm*8 z#*oW4{;>CuAe(4FYeCVpoN-42ii!nT}}zwog`Apk-`qJ=&$h0&!%b7 z&e=J`@}YmW7*b_3xiDY=C#PtSH({f zxTNunW*kNGf2e25%;1WY|448YMKP~QUeU*<@Z{ARU?~}6CNLoug;3)^y08~SL+3Hv z&i5ByaHuG?dcs6->w#QvB95-bX%WDKx|{81m7R&oyAoxBUQvtS#oWwBwcZDrd2yR#yUCt~4x$p~mEDh;LHpsUA83pn6wU4jO{WiEi=(Nga zX={n4CcSOTBF}1T_L1HHm==p$9bTyOQ?3?0XIR^~T2yo>UF~38Gd^A2apv!zy8zLT z)h4F#M|~7SZdY?mOup_wlv9LL$4UEBIWCgr_#)8DffDsEg1lXzU*TY)fkO@Cj%=)r&z zyyBKg*|+ValV5baLYL!I+J5hlZepnyW2Hx7sb_CaN1T_;Qsg{Luy%8LwT$9^Ijii7 z17-*!KKT*9urNCu4l)-`${>S^BNM%@%mD%y1n1M|VK0-ErzfrD#K1z!l(F{2V(?9A zo*jWA^oD=$T3uAsLx5mKVDim$sv|lA6C=x}G#5BBYdSGLicwD__??L6N^hc{Ez!WI zQ1gnx#GgeRafaL@YG>iJ8qp7C$o@9;#UJB2;UGSV5 zWKg-sFcIezN4}<_qKdC|>Io-IV*uaWa95F_TTk;Eq+l+Ohed}8oBrDP2ofCobTmg& zaP>5@aUyWnQ*foY^~vq$C-ohj&CKn)?(XFuj1?!OWN~WjmDMV^d3H=JctnBddE}s( z*JxWpSDpO9t+ZRgI7#%az+nPUO%*i9Z2^m#MJq(BIdZ?%05?;ejEZG3Vv&JIQfa|y z-0+ACT8iDnbaKQY$>eY2#1}Ue6Z%WD+j&X1tT{fie1oVrrS{rY3sbuj4NT9nX=_gt6k-8vHIg9<4ooNXDV+ zLj}(Qa9dBlv3ftQY*E08_IDCy4P`SE!0&uQ< z{X|2G27Fi=Jas)j+g|t$JJvA8lhssul|WZsyw;G0{)=rKQTg4>rK>3*%XU>7rl!%t z^SEAMYb-aPC4R-nvJ*L7KdRhzc6fE-0{JnJk@~5v$*IhAAnp`}cQh0)Com+MlLS2N zkGN-5aAdqOAf28cH|!X20R8ZQ#*z;p<#;Yl(Qs)bx^2U64|(wAlu2)TEhR7*;Vjr) zVrA3cZVTFQp%rWvHez)1@qQ~*UM2nRvI?#B7e#7n?vXMNFZ1$xdQ+`=vG_Ab%YkuC zo5nb~_Td6(`m)?R1_rVC3?FM;??syh*MfGcy?Foqx=EJEO&NXY?)>%AD|UG$Fq`!W z*fT#}TUm?QQe!;ZI|-1$*$(0TZ>DjBubt=0eQ9XcgM$rLSD)9rWVphqtd6sin?wLG zMh@)qf+O${6HBzYKf4d)2n7zX?HF8l!b7(YE==QKqLwkYH5p7E`i86yZdBi|#+A-2 zq<(4{SHCd7)G?2Evj20u6P&z!p>m5ZXEw%&01X`V_xx4iW2jcyaXMA*P!^F!k)}JA z7U7sCQ9&op$VMO10a(7podMEmo^`KAkLuPVAxQAh9B@;Fx5)7mYF;pJ+lfKAJ z6j{xBPm$+M*Hz8@GaLq93rFd8j*2kV=y7oDo> zaS=|LYj7ZKN=dKc>HV^m<7J4p=NXsG(*?BI1);M~RFa7`^_G-3fBzpn>G@VLl-Y9? zu0=G%PA)v6Oy{%J9dOkLo8pa53|OKTdPhvbP)r?vMAwTc75rH)gWg{?h;Wb$q#Kzu zi`mAR`w>3FvX(+z*0&Llz#aGzR-{?q&6EgMz+`k(rgIuDHER%CfL@rxSK|^ZFr2ye?ARqs+;`PL3b?@xG-jB_Ie76Go zKU1T3iKjmuP9@wPnU_he$jPH|yNUx2WFqWe){2b)O2MB=MI{KO-9{G-VCMyFApD}r zx{QMhOl5Tq_M1b{B~-4hCdMbPyjl;3gFB?eR-2|XdiVON6_?&C+}0Rn(2>lY5_lf> z#``6SI{RMzD@#f=GQUf7v0i#UK_Ru)ghVL#`V+q?}X!jRmXWVwLsT0l2 zmfhGEn{UD2i&HEg!-W;RQWeamYp)F}=;OzJbWtM2ENwJkm3p5h<>3Z~53rx;x~Ba6tSP$FP-(3@I8Ji9^birE?I8ENRR6qnbFx^9fr#0# zr?-XVVWGJF`^XMxCn)S&%SROjgzVr)#D=-92d7nz_8r`wj(G`2h-v!%B|H5YRctzZ zB&JPqkQ~0$s=ot86wZFO6y1OB`Y;T~ra2TZ(dRUtzz`WQ@5d!?qRG+Zx+v0LHeY9g zSO7jgBNk1W^@J78{qYX$kEfMGA)rJ=jOL_-nCJoyj!;;5jon~Xpq&$&Y@8IBE6DS?8m&=4!l58VpXW_I4C<;7*KK`|GI0Zl1hgtI%J6JADHRh(b{-8 z!FK81#CaGnWy*&d=@U!gcj1{avS_R9sdCTGL8{|me<+jqWJip@i~oO2y>(cW-}ePN zbc2*29a7TWC83l`ceiwRiIkLdOLs|k2}pN$gLHS@!{_(C&%N(oJj1**^Pbpy?X}n5 z=V`LtoH51xqm)!bbIXjDL-@|X;X+-Vo)QSg(z8BbwDrY=Tg(v5lwZH$R}nP$K8g9_ z9yvk*4SgN~n&OP?UIr-U2bOD4MMlKGJO=As-3r&^T`>>3_oXsG;R+uny@6^IQYu?~ zl9bF-LC5YDr=go$8@V zKi+pIys$CL&MA|kH8exuDER@TiEMN4$1}xOh9Cla-!25K?uR!PnU)!4}s5 zeb1Rq+ftf2Vj>r#*^yS^##fscECVhDMT|FDz|&WoD|Vl4?df+a0rBwv_QuWUB#Mb$ zX;92hfyEHt&uIZbi~QwbF$td%NCHqWeF79}fH@du z<}dso|L3W59K)>R8LjRORHT zDJa5)cHc^TDHhZ5A(qP|{07(^a8<*Qqu>Evq#+vXkBRqkJ1@cc!4+^^bA&dt zY;0YOAnjVx6ETLT`wBB#sh{5^e%H~{I5)c>_$8Fm@eKtj!5xvkk2zT{3Bt`7s~Cpn zvzJN$-|Fv@ZI~Eu*mBSep&&oiRu>y`M}%z#hRYtd2?1vp&G#bo_T5&vIwHPZasgmA ze8t;B(0h?Rn{3t+Is->6zU1CVsAggt7M`;+qK1Zu2Tv|qz-ne6ENjm-)qx7Jz0oq* zm+;q&uB2XM&G--w0+IjcahyiT`u?|CzwiUg!}&-q=tKvw4OQQ_Z$_e`9A-Z!{lGv6 zZc`K59>2t)5DkUZRFJHms~f5J1_G^@m%tvF;ApCZ0*D7(W>VhQ%f%!DiNO~yeZGd` z{u`y2e;_kt!$L-}6ciMgY?%Eec%dMBNFL9R4u*eUMd^3{Se?;{`G4PAkMC!*jRdSd zgpA^A^Nzy=|9ddyn5?a>jXfmAHxK8ajz+mN=stpw#!39;TS4H!^YZcFI{e%o_NMwG+?FtKbc^7r={LZSRz<*xau9oeB%o?@p$_a03tpWKd2=fn~|n*&i1! z>#(`~dMH)B!cv_u@V`fbKxz=d!1+3Fm5_7uK7SEamVa+scau@}U&x{m?XtJ&^86@n zq*XWEY}gZ!_mX?#wQX;9FkO&*8BTDKBx}$4{sMJ|(VzCJKF6jZPs8fqa?+9)q)e`H zNSa-71AeZ)Aj9o^(C7KEcVvcH9gEWvKYMI4oig$V1;qj==kNYgG^Owz34E9pv~U2x zrWXJGrtHa*&vXK*hCB(cCK<`=n_l*SNYkrIS5{2kvvZIW=HwBbYHQ>94f@B_0?hPs z`J%W3pK~9jlkC>ap;&zK=GCuY&RB3?jzy zoAcuOuhw#&9P^$WJ-^iHkHI*7^?8ALzE=|L?$tn~1EdlciWV9gX)Q(GkHNv*DIj$v zjYIZ-_g|lun1}%BaWz7Z%;zI|9wlS$ZUpCW4DmWJwPqqWrs)xCciujx%G zvAUwk{9r|2!D0m$n5-@`9%X5Gz+v?|2u;ai!|osn<~4BrXzFGx!*0xXYc6+4?zwM7 zcZ6RpGO{PJcY~gST5_ylyx4N&GvsM&TeX#PngdmnNTUCy!DHa%v|LX2JMCMVn{Ua) zn+0O1I+oG}DqbGjfS;~RPsrfBtmmwx#Q$yxoquLAatt`oErU16CsF$x_Gyvtv%3@% z!Ka8bg4qD)5{(@EF5#~*H=K@7{ow%ijjcIFb!G*W_Fd5R8=Pfsx{^BSJj>y^@d37p z0k&)}N=b2y2&8|L_WIC(1~1&a-D$|m@>Q?mP}NrUvDsABO(SL#*OH~%EUjByn39qb z-=w9r#lh|C?9^oW)oi~Xi4f`|pq8qvwDc_>4p>3*MP1&v|M56ZE#H7t<-eIABY#Wy z_Dpg6S?Hs>ym@gn-z(n%`4^B8_jZT$Ek%tMC#T)ISO|6va{2&|vQ*8JUPlEGUx0Wk z4CDRF5+GYR-H3R2Sf}}gn9APsoEfxt6yFD(?p6EncrDlVTt>n*2;?MPL!cpiS`ew= zySBB!*CCrHyc?Qr;1DZo(&M}~;gjcCI zBHV~%vTJJcvB*27uv0_?R~Sr+2hmP zaxZs%*(&(=d0ZMhA})uNehBeDgNA593oHz#1n5|HGb%P+UHu~AFg1pI+Se^n+2`#5 zNx~v7%irm|iWQE9CO5X~;;@*k1mQ)x!uwH_@tr|l$!V56<#o-s`$_4FGJD5+GjlGW zhBhM-mek+P57y6BbXA+4<_!9D*smQ$TwH*eha2nk8`;TQ^u-?J8VpQTjS}APE@>ZL z!2+6b6>jHU)X}-euTd|{CB>=7Dcnp8GHZ~7seNXH7TK2%5&q87#fCb7HNe6k#ul%S zZvc4F0nUz^G$YiFW!hmp9}29U8NpP2wRod2j{_lb0f!;-b)tqa^V0zw*mtb+D5%ql z-P`QB;FMZIj5tdHB{nzLu7SGl+hV&;U<;ekqS365N;f|*CI$Mfd8Ly+iKh{kkSAxn zlKc6#LY+v36=_ZO6JjbE;t?zJiNMHtm&3ZqDz6`&R!qtSd4pLP0MAC#&(6|A_QX$&`f*tm2i zj5lQ{pUOjg0*BBC*48gTbs4Z>*x#D!yeGjBaR(Aq0gUHlXSW?r<&$|A zP_C8a(TN7yU{=^0pR_$QTI0l^4cRQ5s5W{}bH6ds9P&D$P20PI%H+O4CwR4yA8lxL zRI68Uj`z_V(J7kAbLnl61DWevE?2(-XPximQ-l3eWCQ29#d7h*W3Ligw(~gh)*H`tKbsWO;V9$Pp zi9@rp6x+60F@2zjNGG`H1>}WQC_Bu^Ae^FabGy@OM zYSaf*U2k=ed`@2L>{fsWvd$MV;44cmNw=iO@4uecCX-v>*43y?^TU7qFBit>s6MJx zABhA}g{}EUjI|>S3?tCiW5ea)@}EC7m4DRIGzX_FkLYU(Sa zSFPNV_K?KWn%y&W$T$J9ojdx3|2(jy`+3(Z|E6+W6Eo`mNpN#(Vg08$aQAut-V}QIC_Ma12)sC6r?Ga^e=HQ9xK!u?h;&rt z1t}SIQr*xwgvM22js5I>Dvxmg1ZWVAXfTpCvm2z^&y5}fRSP8@1=nH5N`V0)ph)=Oc^+;(%|KCfcRBMQ6$2!iM``-$KA*$^F7GT-7JnNVrquNp zYs|PLCstmsajj+2D<4gKl3w9{1jQe3WkPW9m~DU(0%^AZbtXP36VK0O#|*8hHGCT< zyxEhY;7h9EM9r*axZV;bOOvfNU7)RJr2&GG8Nkib^}XTFXF-Dqo?4y~o1@42s6N0K z0H?U&--s4UQlNNWryTa;YmpK;>4C0{qSDkDSLou)7WYcTQZ^Ck`r6c&33@yF}xQ)P8jJheUOw$*=;+t)${4ko}SJoIt3)wQCq6MH2s_60-x z*824a))bd^)Kfq0 zjsF`xruUp?Uv^40_n8ZEfR1DKSI6(Kae8&dcNYzlMlj5yB~x#%gqg5Xww@1TCqXEsW>M z=I;pFFt19qUG>}S6ebh#i&fj46%cwL&9=7#E5wV85<>eh@j0Xyh~GeIUWFA55DZpU zCcDmqBIi2c{ODjKhV!XxulJs~rJlqu!g-z6()~nNVoa}-9p2W4I$iLi<^?Y-&Dg=< zcXgZ;_yNo^+Gqg7w=(@mZPyICGdvS3X(Oaj1B-}N~P z%k_47aw$rn-Hw(ABjy{GvdtkA^)Ih5=aN+xzZg_|o%Hb92{;*TX5hD}ppycVk2o;; zAqHD5&|Z3w5Iw6NEE7HfFa**XeNq&A2c)=}Xfb@ReN|jk9~jp&MwJwNj-%Euvb%T3 zS}`fJ5PD!8zn=dC&(fdwhM&uQL()zvcU)D6&f|7#luD*c8;}lkG10-ju`?~+=%a&mZYL-a|wrNF5H;r`0zpcU=74Qqw5sVO6z3V?Qj3@yO4m9?mYM%43jpmyQc=35BBiN z(UZ&AxvCzI0brjR7R=}ole`(Y)6R@WHW)}vwG{f|!$rOzH}?*`Pz;&%X%PQD3(No{EDtZ%T;!^V?eyv zv1>`QI2y@?94197O!Iz}6VLxqoD1@nkPObu%1kRY-V`20B{HT1!|p>&UR#3?0&zy!#WgL$wLr&Ptp?3|3{ zOyx@D*NC=uPV_*y4E|qyr4Lv(`bK9)P`?u&Blb;ipdX6}U2^$kXX?jSkGMF>tYvwP zQ<7I4E1*ap)B$>X7p?r{DIuPhL{BaB+#GOp$%J4Ex+o^>SpSIPCj^CH*@2toaUT=GWmFcF9%5#F?G_Tt9}Gn55V z9Zip(iY^68V2D41;L;2YG#2R;x|hY=XrB10R(tTC-3onYXSK_0rS?Zz&8DiF;4!Dn zQShO4D=Hg1>J7xEzJ|QN$P0#^ksEvwx@N>i37paBjd*doT*X#g? z$dH2my`Ovq1iQD&;<4(JOlPD-%tRw%GHWunmA~h=S92q>piBsl%WUeQT?JS{5Mqn; zJ4|7Lwu}iEL7=8*L=aLc`nNZ@_b>0U7fzCN1WrC)(o=fNc|>H^-rY@F5;&vg!f3*S z9Pa&b3sSHGPOjRQSMtbTi*KNd@N6CO&PjsWj}wii|LDQ z%*<(je2N6@gQye@NkRc+9Nz^fi5l`Ls>%;3(q&nm3?teU5w=7E)Y;^hlJU0cc z>m=b`Eyk{%m!r--I@)7s-AS@6&~o{)rb9s(nlT=A)zwT5lz!^$%%C_a3N?POjA{57 z@<(9pnN5iI?Xi{Ee1~QJ7b6&GKM}Gc$j{kPO4{TJ+A(g1W3pF|aL~Zqp%?86A6gpg z)KYN&5j4uN{9XQPYE7K%6=GK@)k$yE>O{U>VRbg~!O*X+u6A$IIlcUUvJI_urC=E| z5guDqP1+E@=C3(*#Z@#(?GPO|Ayl5Ebn`>kk%xlBho6~FpUl=L1fzwr_*~j3lQJ}0 zI(3B!Yr99-K1qvn zG&ICWrB=5g$AuJGMv)*_h-31e(d(!tP=xGN@N4VY%WxyNgfDDSXpZTLWD_4)=H*O_ z#`nUcnL4B{LPD>@_N+p!sT+#2==%lucTZMjdf?HpE8$wKH(J`col&Mj2%WpA_B=httAcR3TBkb5Rg~3nw*1hmX(d(Xn~ACb z)#Mfh3;7t}m0L+HHC97F4-xs(l=NxsoNC_Go6#3{cPGcuV_pseHNUkpJW@x@!%m{* z{)`Fs+q`KwXHL0FpOE4CPS5Jy1K+5lf5hyDW|#(#mAF{r&qQU*gu-ilX~pGlHW zaO`|r=T6esVdPB|1~tb%e{%ZY$ykG-nQ#y6&xBj_h?kpj#S9})8ax7jhCVE|n4Wuv zTwOa#sQWjF+U!)G`gLS^z~3&4pWoqd3%Sb>GWeL#e&v|{S`$acqKmW>g5(EAZF8Cf zcbUxExeN8br}O$j_<3(a)f?x^Zt^wbgGeTaG^8C}-Qhs4MuMNt$nmE*wb6Oq&f%P{ z`@z*K!q$QL#=0BZpKtXFo<&IgR2yo~(#H+Wos!u9&|KA;3nHVlNLu!mv?Z7Jrs1=_ zn}<=h08;~vmM2NnBj|cG~(o%Yu@dAa)y4;%?G#5>2CP(NZnqXgXDojEuQFnOuzk0-ryJ2R;bL zva0RX5^>@2kr;gzoYMpLf{@h&2dB2wVtQk_YNbLndUqjw`D78OFB>?UkDFBTQTZ!!AUm^NZ!fZlKU zy!*o*4AvenIuXZq2!#K?>h>V=#*7TkKRpH%kbhido~ z|Nat=t~->s&C|lahq%6;CnY;{Yp|x*n`Vr}e$P|uqw(ppv9;gFK*o#=wv|E00)HUX z*U9!zClPZ%tcg{r9&fb{2iowCJ-DcH-6A}#!&%Fxd#Qi3Fnc#~GUj=C_I((fySst> z(;q44ylZmMO~QqLG~>!Qi%Nah8JHbMgUm6|17DIxGQMOzmsECh@Vi-Q?u?#QlsXk> zhEJnf((v(0+)1TlA5 zykTd{?Xtme5t1Bv%KmlSai0FcrIhMSClrm&xYjU$T60 z=w*Y-xIW391Yg*3o^EeXaDI(}0U*mIvHGiYb5E=*MjnD>Yf7!j*WMUxn32~OZG!6B z{L1bzXdwP|1kBJ~u?t)+npv zNgdcEVBprM%EYcp8%rd*t@)d|W}P;|5VVc}(KFE6|H1qQ_pX^>qg~JLJwn-@MmT57 zdz%_XHaZvEf}A)it=CWOwrvq*3;oT9=9eiEs;rvb0tS*Wn?(HgK(9hEojCs4zI9k#F*WUgYPGu6U%OS3&xa-LgG~a?RLbEG;HGcCk2*(9oJB$tH9AfB3>QP92~B!9adTPp{a!y+^lmUV4RDN9<#*6K=Oe zO2rc>x?__x{_tlwtEyYWB!~=Sd5^B+8tt+>m7rjBuA2ITo4?z%DX41TkMJvz?hF$z zUt0sfQUaf&FtZR}TbrkP{6g7&-1Fdyu~O~!5B7xV zH@qumRQh;UpNpUO3*YW+zRwTkh_oIyu3QH8($bB?TPq9)Vp}us`aHv1oDVC^M(sQ2 z^Vn+N(9cFWhT^`)|JCxQ#)-g$%0oMTKPW(^pO9g9t8pg}mXTmO!t}PKZ2)CYmOZ9f z@GZ1p{>w}ZZDLL&K?G$N=+=vJmfHvwngBtVe2 z;?XFP`GEFa0Xr1}=Vil`sFz39-70RJf{hE_Zv`=`{bQ>n>nwJD-FDIL|y3nvK2oj!a`Olx5L6L z8$_uIZzrd}!SKGTPsFFy@UiJy{Su;h9esSjxZvIz|J6JF<4s8}1U5Q5>8@ z=-lQp`6EesUV%SW6Q}QlxsNc0Ix`%!8tlNX8}okiYQgHch)dsc9)pHkg%g3)_~c@xAVyjU;e)B5S&^S;F=u z>w_LUZT0j9XODIc8Ah`%zkhNOjJ^(v8(D{fv<@sgH}0*sjIn>{hnI1)ZreJvX5n_W zub$5$QGOC30ufU+;eaB5;A`NgsR0am?N&Q^PA4} ze3yWhtC@@DpoUj}RMY)m(T|B=7{y~|+zYf-;cRmsX}Nw*a=!6fo>kn-_-9Iu5a4m{ zqODLir(;&3_@1f4EKxTjHbuQ~CB0Oms^w$_`ZBPa6#{u3(XrR+#JAwEfIQ@EKM_6K z6y0KpF5OQ4j*xLQ(=AKnaedgpEnDJ`>7$L#=A!5l`srj6@fUOaH@mgxA#Y3kNpXNd z-4foeu(j;;XLUfhJ!ma)Jxlmzg|~-au_o^4K6l-~h2UgUG%{;;4Zx2u8%{cL;dh}S zF;(Sv{*Th(e!S#TW6EDcr$o(%&`DAzqzp6bE|fC2x0>E(&a%+Yp+HWCLiR4~iBrr> zpV;JJ+_I^p=#6u4>XSVBSiBOYf-#dH7@Yo#h=a2u zCac4uN%}SVsT?v6D_qh5;h;qw9-PNXs}@!Y*4x@yIw1rENXZ@5v#s^UjoL2F^@k07 zwI{=6VcsJ5G&Ga-qA@inh`sUvUeGyhqgviV%GBy#)6x`NW_7y&EBqeVd zLMMJ(PT0DW(A6H}oWT}yRK~?4gN1x|Jx$%}bn=NO>XpB3azKDZw5Xm2V4#Jt(nsSL zi;xO3q_>!p>G9}m&HN92y-z=`f3ipwjyKaNHmsR#WzrX=6=St@gBsnm=_?nmt**Jk)aSjZLB2HzY_N!c?hoE z7q#Sdur$yT~T$g}^#qHk2pV|4Zx4DhO^1#WcV8b6(jsfyRH zyce9ZtkZ+|L+Nv50E8bO@~HFXmMB6W;;Yn_!-VdlmLWaHh=|HPAI7c62|Wg9UZZ{+ z-YxbJZ74|Tv1%XI7PJ>JeR3JH2S7Iz4U z1_y0m$J&sA&@+3*H&)kKW*X4 zWn^nrzf!OscJ*)P3=Xr(9dy#MNoJ_8F{Q?Uuc_oMASEG$#4OvY@j!fcDYE0VE@k|N zL|Z&n|4qbvgFwy|ye!eb>N(tgKZ9q7=v6A}$JO4j zY%_5yy8k>LrnLpw6n zszu@Hq=l);tGi@{q@Si{i5&mAxJjKx`q+U#<^*q>-H-OMr!d0N5l=UvAYreg8(x@!DLXF%u>=z_-mbk2 zl7;9Qvub8i`r;XlYZFeQ`Yd;Y_hhJHt_M8=Y2Bd$r_u$lAjqZjb=vnMiH4XA| zR(qO@>i^)~yL4}f3?XZqzF7#kvl~<3`O(OM0j+&5y%atzYPU7r%){;oP(^qj>;8Gk z%+7b-9uASeJ<>grP3a7ip6+VPX4Q55@bJ55oR!upY6%!7#bYQCwWe&d`|dm7RdogM zVY+iZaPK_DWY5PGHrxE;#HCb&T|SzaxvFMb=D|Bw+EFT${~vwy>Wfil=?5E&q;~-n zg!so*gsSU*S|2f>JE0(0*8Fe~#Ye2`;f2cbo8Jh}d=`yY%zEZ#%`Lrw8rfOH8G385 zD5jYBqN}hdPu)n+7I!i%nyf_oW?Yoeb}dGe#=;&KH8oOgpuvt)YdT& zCCY@iTHBmv3U&uUAQiu&t|doOUkQwBj-svXJBc+B&1G=SudL{zLtORhSAtNoSEWf= zO-(ffA!Otkk2#yf`c7Zq>D8C@hKpJA0;RpZ8T--m9+F9hrfG)TS0P zDkt;8KH0R+>rnysG`Xi_i_A6m%aUTP!bpJx+xBGpNWnjDuw;@3{)eMUkA+Vh7SGjg zmf64CfJ_XFJa~4d_YmLm@8?_5iJLP_51zHd>Od8Q`ZGjhD+7#o(R|d+DN<|FA1~3mx<41>P z9J89d)581;NxBwYEGI>Z8lHG-zikVBwexFAzcM#r1_d~%)q{Y~688kBV!yi9nDWsu z?W@Kp$IZVCB8Y9_`{(ulK5ru+JQ>R7E8XK{$ zkpW_!P>^)uvcYmrA&&m^BnF?t_XzxMKF%-~gMYFR!v<=mvO?<(xJt|_*;|$^vq7+O zGQD^LJm+#r;Ges^50<*lV&Q-JXu3T%*7FXOdC-S(fNCn>GOu4!<|hIM&6xZVWE~n;CQjIcFRGKt+Z>H&UFYU@Mc7#y-2| zIeTb4c4kAXgeAD~hd%>{3y{CltzIob3k~w*0`oLj2df@f$P%VMYEGy|r7hc8SIHKR z%T)56PX9Kz=JR#vQ6=%uS`T9XM{m_(6qtx%tTJ%T+Avp$T1?>;4y3U5o}NKhA>r0TEI#s{E*EkD4bsvk<29 z;EbH?c9uJUh37o8?TtJrJ*Gl4>v)@A&hB$GUC82#$D)7lIZmzA+TkKK?#=uO8TTWA-Zmv=f>*^WDsgU})8j;*2S zz5h_Xpv`Cc3VqczD8O>wZ-9;V-O^8XGo`e5iN)9}`m<+6-AU&98woVRUrLyK4kw=?WAwTyP zBsgGTBqq@Np)i=Il?oW=IeV68?XG7)`$r^$h+sO=?;?wi&|ThDl=&J&(_wc9p30|9 zU>%>0Z?8cWI%MbN-@HAUTZ-u3#o9*cccNJ~Zh*>5bFX4V#DMw!62-SkpO$bxz{pceUNQXLOC0>+j*JSK@ zoJj&G2u9?UDf;a(DU}J~7M(#c>Xn!(p?+&6R=}HhzB*o7YBEC*98aG+fv_821O{?q zeZKm|aY7NvVb5KLhvle8;+#ybvu~O$b$Lbim$LEAP~T1n*DPK=a#TwwWU zrNf32t*fP}E|Ia~s?NkNT^@Lq2n;QOKhM{W-3dMKfTOuva~u-OhMty13Cwf8CCJ<@ zBOO%9cF|RVfvi5>Mhy;ZX8Ys*zUc{puKBxZ%cM-Pj`hxeNpy^bcJrh4Mx(F{x}fl@ zcqBWa(opW5M(C?QaIt2w1x|B=aJzco7h^*A4|(O_t2y3rb};~Ss&)3iQoct~oA!1G=q z23B)cc8-43I7{937hhqN>o%wD82L(a-A`V1+Jw&A$B#{$1+oW0hKG2X`-p_*KDS*> zALu`Ic8eL)3@hbZB{u(RPZ@S72{zum*(D)Ze?8fNKOyDfRP5)4 z7Ts!|od#d+Z-3F$PKMf<8h{K8VPk*?{&&1nxu-I0zYDy9=6;V$qWhKX$bdr5YioZ$+2=j|G#072c%;y;0D z0CBFarOUZncq|{icPgaEU_k%P)E&5QfesDzW<6b?OSnNx(@o3!%Jw$o+uw12<7X0v zdv0+o9zoAPgC`;6)@9Tsws70VJMKneC2hWPdM1RI>1h(7fwv*`;-6~P2C|jUBnKc# zv^jEi+rxzuT)aigDWtnQ)0>AsPO(V_6nAElv5elU@g$6pFQ?(%9yRx>)VxmhTegkM z>D6f#VD*bB7=kuNW*)eJxEXMn1rFs-K9g5*OgCTmKxQD1r#IsdF;Toz_KF7v1 z`f0uqV6$6;y6m3*_^T@MPwZhEXHhnU17bNm#Q7T2;i>lv&U8}XhkB=RHR^xlrLuJf zS~9$4c?)ADw6VnuJJQu`ba;?gtHYNL8#!_NSH_&+x#)fRcPi}sCMXS=&F63enl{o1Dzhqu28EI@ZiFp5sp|6;8 zSoVy747+0i>O*Mgwy1{S5(k=d9)07KYS{|GKZPBKlTEU*zugsphV`*mYu%|lYc03e z`Ss>*GKY-T1Ci%@u#5ml|8vKhH-JDs&f-r~ZORDcMczIXV=HM3TNbdLjovJp;W`Bp zxO9Vb9R@7{4<{nsbu`5vBpN+J=9-x4pBr;rT^$FoH_44u3mD${6Fc7=4fOX*hW_33 zRsH{roV(1xNvoK*Ipqs)KMW_qbpbK)kYTLx-eI(oXpsWjf?DK&koHQ~>i{i#3DzH% zB)Bc8&{eCjSl#L0mb4d|)c#r-8*{0ttU7>A5P zur4xZ5gu>N$8J_Jog}Fs8S|s-r}<{cCeJ~quLzl+VIi(Hyqpc8JhVMkA9Mv()7EG% zbW&L&+e3!t7Dkj!wTec-Mum0=u^f6tfvhs0weWu>AT-r3Y4FUvI-|UwDIvb<@;|*6 zp_%s^-)KM!IL|U|wS72b3nzm7EPgky>3OsK?~P*T-?YL{GQ};jV~*Bh`qpCP&DDvN z$+)a;TkW~lTBqIfgD9K{zdm_=XDvBDY6RsptuRD~Rz9DFS#5Vydkf?42%;&Zk{!Ri zm5LcO{gF3c=C~Y_Wxf;lR)TN!*C0Z2j#<&6^p2oZGKG1+8dyC;o$W zsJpn{*_{3wqcM4?8n%l3tW@=iC&+eI zC(<$uDChCEm{2LgRE~qg4_;7nYc?QENc~3Uq z8K&IQHN+lmZar`$jk8=99}?AxvgrK$$E=ZG-M#nYs>?()k+iQklx#}syZUQr$B$fB z89iN$7SOkRI{eKq+*10R?AXN$RglZ(u@h)-FROlR;P47j710UBo#(U_uA<5T#|7x$@1s1>yFzdmof6K#){F8a!TwUJj>dVOWsS z5p=RN&yDedv(R}J^j+&0@-~$7LphPy))Hy2uUSuSqxw%-$MJ>zs+gyYDD6-;I1^?s zae&1KZ*qWl*Dd7&AZOTcBW69tapJUMoYB-`=20CQa`Ug1mOMf2R`CfV|7$(N!$Qz{ zBJ4H9*Esv3?pOK!-U^7g3>7jy8rtK~$>Pb>a6o_+VQ&b*@5?NZdyUC6wh%soe7Sz? zM!hvQ)WEA8*uA3T$BFdhS-FqTkPQEJiG%9469$)m0%w0mVli$L4uX$INsLFi#eK)Gq%TY zgPO3g?kC7j_a(qH6Ybr1?j|39p0X6wHLXyDG0G}W_-obl<@y6WkF5k;cVGko;EZEz zQv-_g7RU`?AZ-1YCWN_MbIcM9BAgH<}GQc1mY8d)5x%O zibi#dMuXf>>q_xi&SM7Ksz6~{RZI&B!4%dT7Un6_&awWG=Y+N6;ZideMUKR{gBp{s zEK*~D=~}eU+g@^u`jT0wq?sCCV(ECPiORKtTcyvGMneP7TddXDxm5WMO|=Lu#E+r? z!nl&kO%8;;R=vfn(g6pIk$*9MhD1Y401kP;{;^$;HTAhw@At^CV|V>EI|%>v)GfLE z1bG%QAS_nCrUf+2+3$r9HwLf`a-E~g%vjRtrjsoSO5v0gje)#V3Dw9W5hf-T8 zk;i%HUs}T6`G-J*%i3?BF$OF+RYKE#oZ(uk5h;E#UG!4FJ#T0Xl7wSFe1H5NW`)k(O+y60DH2)YM_tK$W$YwHY#J60F?gvUTyyYo8Gfk?#M*@WAnjQ~U z;S%?H4}!=sd$zZI8)sDm@IY&cZ~3~4oYl#TYLey!V{KT-)By-7I=q*KQY`{VNbmNr2fa7KOG$4i-15-xeeDHHnhGTQTN98!XVbOAb}i`(Qd(n=pq9AUiq zQDc`|UaEj&7mg6LL-Y4+6}mfgMeZb^pCr^&i4{j7Ch@!1LmoQxhAh%ZTNzS6@-)of zSzSBEOO|13(2k2LzLik$ZFDx~_lWkj_cF2G;V^&uDEFqDB@9xp_zFph0TG(=YUsIe zNldaI{&;Z3C371KhY)!xmxi91;SCE$D^STgNd^p?d@LSHzPwjJQY~RC#{COVD_vnb z4XkhI`nJiW@zfh!i=Z+iBcL4586A7B5r&CjaQYrs&A48I43`A+2o?=e)cz0f6Ud9SXE9z*R1oTo9q6*6uo=(Qu#!5;tRkLPPNJNI% ziT+tmCPBlpMPXNbr8S={if87)28;_kQP_Jusk(|IiF&guzUucN@9HYg^!K5B3C|e0WMc@kM6D33jusLRwwhR zEY#e+v9KePuk0iPyiKBKvh=+{-xSy#&53osC4^e7>5IhK8ZgD4&2hzmh-Abz9nZyv_Y`xH6i;e?R5^dJOPeF# z#>7FpcpbF-o{QiRIjvL$Jd?)0V=W-{bBC|`vi>ziH@jMDW!4E^87^nMva|oG*4|Ny z4jv&WL=N?}{jJ*E@?an^ayQrPByWgcZT3wh5#veM(4cT2w;UGdR)Dy_&5PE%r~3{F z1bCI<6N}U2^q#C2sVE=qP`|sMdF#gAoRLz2jALtN>*ufpJGGlfF|Rg$Wtd`7$8j}? zNNhS;uRor*6<6X7B(wZ`jH*SL=Phb8u_Bg0qWW@Vc-$`ICI~Dx40AEU&%j~2Te*}E zX~xl#*~FhZxHP0%0wxoQkXt}LqjYM1ucr2a&rq`ACxOKVQS19Jxj`t z1ZWlflVhaOkYcpr_Zvzqyi<@Gy6!wERUTrWH>V%O8j4O?6@-Fl3pjrMy?} z3ZNl6shD@8*Q)BUPh0EwP(e3OYd0Y6ijL-BIDRGqQbZsox;EvGr5m#LoGuFSE2f6U z;;H@mesyII4WX<6gkB(rF89YY?cHR%IJY9U02gsNUwk=OHq)isF6LK?G2hG}DiI01 zn)UYYSBD(~=8b}w4u64AdwrVOQuGUNvAH0-q!MbUw>7 z2Ueo$69cbmRyPX^!{T9^O8`20TsvlsmGSzWmvPVe6o{HS<{^|6izN6*Gw*g^Za`L! zDH+doLk0fG$CNuvEge2c@i)Tpu9V*rF=N>x0Xho%LO~?D%EK=m;dlK@+U;59Uy3*^ zhR~DYana&=e8>|5faCpi|L^QZ%{&n=z4MQw$(d3VJmkx?p7SH;|79*CBI8%42J1F}cae z^__KdZPRaXDI!i5c1gcCfy@F{i{aGk(&-|X4QahQd-M+HjUND&24Njrc!Zdl%h)vo zLY4pg8Va)6SoEbX!&2gi6wc=!xkVTMPTYlYMb{n`ad)nqC&!DaiHkc!ZN%1W|Hs;r!;Ss{57yG_5}ovC9ky2{^N2+}UI z3Izd8TPw+&RW#XG`z$V;Mp0E8i=Uxernvi{ya&i_zd>OC)xFU*OKzn;$QBgMDwQ6~ za^NVYEX|VAKw^};lfK`RnOMWg_*mpcs8P26Hl2sifzm%9&l~}i{F+<+a$lhmdqk+T zo$l*DXPN!CFj&fwvD3ZSeekpVT_E;D@kNa_mss@YhuF~yKZ9r6>z}|*-tg=c{(yq` zdt#@J3{usj7@E3xI|Wo^e$ z^~UvVU8zkRU)i~gFIz$PydyvkxA7fO=QR$oMpW~6c?2}Bjf4d62Ub&lsTh!Nx=}mx zKM~+;ycUlW@c2@$g9$0UHt14m7d4AYnB zrFzG1YkL3H=rrv4-dYL(A`D=f!25xjAAFF1j$fM?sbNw_-EGo8hU&q7(CN2kDOqpx zvW|lkR^RBsO>P7LD(ut88%v9VrisDH<5C6t`R^C(GMo|nu8f)orCxwM zZAD&GX7fCZ3Pr4Gqhh1Ji&iXu@{fh*=SymidT%3r^Dtf_UBoACb<6I%TL~EkC2PuC z?^e?QcveLL_8$OMsHJ-c&@W5{*yt2mI~S567gHdY8sDU;{qs5D{n0}i^O;{~N2AMI zNxkA(yr^W{*rPnL%X}GnWVYG2KfXK^QAP&7rpV?;e&H|+z?o0r56w?%S>h!$O%lX7 z-n)_~>{a?Q3(|xX;@h$m4ezA1)Lni$LeFN%Xk?S^(_yTRP~>0(c_yLM*cshe#Hj)* zWk1K`1|>}4FShjqLZeN>Ek>1VX8<$xQ=337uJg~a+OJLVa;mSqeeoWPsEdBW@J-72 z^E-43=Ka|s7mZ0`&Nfm?nW`GUj1gGCM%$w0{uz3E_4`}s%N`Fd6&JF~;VUW-^{I?^ zyDqE~s&NI;k%e)YuY`n@GH>?p%~dxW?u?E00Jw!)*s$3dJ$BvM;fc$^`@_!8?gX2; za;^eu_JK3*aWs@3QB%upKw$uG^fkSRY1J93WCq-|i>^SL5{!)=(y@dpw=Fq@Xo5Ln zaHV-p7~&aJV+v^kS=y9cJnQT|kRtrF^qB$!fkwc%EPh6^;0)us*b>tg%e=beRUwbE z)%ATKhH^X#1a;y!bIV#W1j6GqBEP#FrFEH|O)JswuvzWK zu`I{;9UdZb9w3$1uWQZA%HBVBAeYh+meD719N%23rI?Zaqa8^k!X|4s)qYbUzSDU)urK84y6oX$VHI#!gA|( z4JFatmZ|BdY!Ai)&DA@nsG>w^s6X)a*_wTi-bjxWzneWwCS*2QZz30qq>`~J|EL1W zVli|Fb(=qd6dz^Es7Y^V>BUvdE22V8wv0jgENn}GQ( zJObQUJ2?%lgOu}C`g&+VW5736Z82|nB$KJwd5GHD5!0_mTm>r4>KBSnQM`116yf@1 zHqW|)k+ZK}$O&y+y_vtKSgDm!y=}N3kdFSys{Q#04%C?A+XH(DvDj&Sx zFIuxV`0MT)>&+neYaQMGVR(7r_5-mtg%noX8Tv$$SqDT1a*K*Z6!Zd>X_vB%?d~Zs zENYCkv;%3LgUz$Fcl)aB*bO90OIojfNX0w1Ai;*fRXLn56HGT4B)Bk0ZTy z>r~8wFjBysr=YJ2@HlKAtet9pIh+WC+FWmg9H7bcEqi>QQ2af@&FVH3=L)Ynv_jp3D&s^X|ce|{Z#k+-t5`S)5beg#whJZ-S6&m;AB2EG7dRU9B*E}?X2eJisI9ISnWRol1 zv63$d`FUMkf6BWT?=61SqXcvQ>iC3mZ4;*biOy3$$>(o=E@6zCrxwdIOmEKE2vq|d z@q}?fP#z1pVx0-yW4)mN>H<7eM~P^7W%Gt&>mIzfm{ePM)P#ZSfo=1v^(6Stru)q= zWUlg*beC3)UzN{ZhIn~3uMF_cGsO2uu$~KrS6CTVXkX}M4D31T+IpUl-Y>gJJAWq~ z$!dMVLorhy`DK~?sjt_7UNONESKK0u{tHfM(T%^8DNkAOH~U>7@hbL4IqPEiKb)68 z3@;d)>LcBp(Vlj;aFsXuS#bm)iqja#Pq}(bs{xOm1C8prp zahZqC4yD7OtHFvoHQ=yg1A)fnVwM-xbo$Uj&8G{=vGM z5~=MrXPXM%72;v$w%&<&aLc1NOStV1wy{_5#MxD6Kzw6-SBP&mcjr^=C1U*pv+4LN zm{{}vJCogUe!x??20eMZ4+bC z=UaI%M^0-@-(u(W8AINCEA%mcC3pg8n36#T(BC!hm;>6imP;KU@Q?aTviPbz245A0 zDDxi7)V#)L?W}HNfoQt-sfA>I#_+Sbr;DJ#Pj58}w`SR2eO>V}5@qBGdpL_I%*>fF zISYhLBAjfz@e59i^@n0SuG;D?t})`Ne!UTd&5>Q&BRofytMn@*$2@_AQ}nqU9vA8W zIE!#8%_WiV>fWVCG1f=LJ2wTG@vU>-3Yj!&c>IUfchU9aeuI;$xg(RNy|d+MnifGU zuGU*q2+gFeb1IY#mcDPe@(;2>jAmEGrH7)^rH^nA2J$wWd4$FP%;INS;>h^kp~^DF zfiU#S{uj(V=iSNZw6Y>wlT${~x6uG}nCjr#YzwwUl|E3vRKl$=L2hx64ysZZV7etI z{&-}k)80J=rvG;`P`%{=qUS~eb5Y1r8$vrCvb_)KA+>L9yUpM(%qJu{% zr@>8iT#$2Gs-jxRlTTe}WH)3;=i<*GeZg_&q-L|$4*T?+h*Agi$7&G_9^0N*pCo+& z5}+tpIdLU&$+Y!Hla?sok+X`7y*^%R=C=@Dd8@e8?Fy$blDUshp$(ssFdrp$cRxI1lknmN_h!6uEibSz_6fOkmuwTx__gn!PnNFL?qp1&CnUYhJK}TXz|q znweGFcGY2|Kkzwyn*IP(!R=nS!reDMwh{Y34Q8eZj^6cC96B0XyTeC(q#V*9A&@H;6i2{jv$6F!Oc_4pz^iv_>* zW%H=7)KQ}byC9gu&&foJuvKjMAXV04j7D3C@PwFQ zPRUB^G(zkC>k4n$3GR1h424~!Kvg;E!rU#tgvF=r_eV*?0E>ilia#_VlKB~FIMFGS zKe=E&GG{I}W3A+9x?3IKQo%H9!aQB(nwA5>H8-_;;I{B5JlXYnBrasdN zVG2!s$MNLNGoc~R%(sZ0NAi($SRDgO*xbSv%-E(v1m7vw%U^}eS1eJ5Xz>r+jwvZ9 zvakHik|8vzTzTsCs&Vjc!YQJNdVXBWOB<%=-fL_1({qAyz$f%4{Z^C43_|62NMC&v zxePw+fq5^<=4yG2hW~hu*^F;f)G9S? zfW!W?(3zyh&UOl67pUW(Ir=eGckLx6NR#Hn+DE&0u7rH-m%pE=GY&8syjb1XqvlNg zy~E>p;7*cEWCej}>bIq15BfiFHy(Z5*W2)!5VcWd+2d-0bhIcjcoz^pVMT{elVa({ zkX3PeYHnSYt2cuF^kexew2>p?{_hgXU92R?M52MVVUw@jEhZ%6jJsr5#(9Ikk%$h( z+t47f=``eWXvpo+{xMZB)f@@1&(m+6hpDejC>Z~NM_sX2zu8W2!O#i+Jo0I+ngpVi zU+I-BGDLGZY~tNEs#{q4do*9K#nD}bpvdZC?x({zn1S>k9$B3DuwrSpyH+8lRcao3 zwqJS_O~zp$5kLbc@*Wt0Cz3zoX4X~hYMS-e(C0V?yNCk%z8)S7MEi4o#3O~8 zdf_T0jk~O-rs$T5Dt=$Uo#@Lb?0_o!7k|m%YKDe-w7Z3;}s`dOu@hx)pgdaZIz_kB{wS@ zRZ}Kh12_KFG$C;r>0Lo98w24_Udejejd}qu5*<)%UT_^XB^Prp4qp0;24OA}T%Vmj zP=ATF0(C|W?56R)G2bbWYOx?u$ioN)mmwZnB0l-0?B~E@f<5r2Ea7!51#O$;Ukf4Y z5-1*tshx#~(xXLOfa%x%fipYC841>_{2@b%ZL?x8h6ca@c}|X(CLLn$lM3HjFsx!b z-XuLI%O|Z7*Q!T=pb~oO=DK@U{)ogO5v89@q#s5J-Rh6S^mh*|ow96|V@7+_>#F{3 zg9pkB!~s^^gC^~25V`~<3|USyldtYCYyB>>pf2AW){5R>GHm@`gTGej_f(lw;|)!O zh9mpCLqe8&pOs0bzD{hcg~BiKmcaK93iu{tIxOeoDAMfSI$*W?BTt^~nhK?E2DQyD-{1^W(-2at@L{g6ZL_EQ z`ad{RSHOd;5 zRy(!M@yph314EvGKe1nJ+b-l=(P;1SSryjiisFMI@PeLHepjs=w2ibz?9{`oSUtf zQ&!~IP1<_nfq5|aMK!V720=nbe#BmCENG!3FVxz_0V}`PjS_nDmnSQ~n$qRn5ut;T zCDk<;ZM^=6+E>65>}C|-f;FZlK8G1b5{Wv!5 zobyo1$b`hM=TPD2iMBbo4$Y6R{?y9lYumHfuz=eoc)I3oGZklV5YCU>a4%gR*$`7Z zt6fz;FtfSlsx)y}hWOF_T3~~Hl+C-B)rHrf1UVR)0xlwwJ!$@Dd~)P?95!>+KmL}L zniI&ex%$RzMCDJ0X^)MAPT2J6@m5?0iIR_>l{Rv-vqJY&ArCjaV>{4U}Vd zH%Uk>fP=_IkVxGXsv^0nU5Wxklx@g6SPp6 z&!}4GX6U!}bCIu{`=vHB&myu=bh1Kf6J6%|$}_+v}qS=YO6PB08>Wi^diVe-+@TT6IF&pS_;; zeAFU^Y-Xy`XeUG&fs`o^)1f~(TB|EOY34@vq7ff~fi*8Z17*29eROV;Bm#~A{Th_q z_;py6L_KWIrGB=vSFB>z)OfWcj&V(_74h;=Kun;hS#SA}-e0vCP!fB46gIy(of@B^ zKpWq;kt4~YXGqtGrbvPkFaEP{$k?i2^9`OuP=AAZC@-E43&`#OLIhWQLnZR#Y(n`p z{XI%Pt(jOU!n8H;U>3m_ZW*HChV^1DqvJ%bI=brwB`*Q=V9E#tf_maFg((>7&hDlm zFYwYInLnZM38aplKEg+~L|sod{pJ)p2*a7%NcWBq|JBgn={b0c`5IRwo$c?sv26-9 zZvTnb?KG226NKUaa@q?*^s6^*4K*7795l<3a)XCV>t zGTJ5QcY2lf#-fc|3mOo0mo+sYP5hk5yf&QB#tyZi*Y_V6j{P8icZ7T7KvPQuyV5D5 z_h3D^VFEM1kLmp>ok=}Hb3CgM{JXrWFsnx&!jYe2O~;M{EPm1!IG!cr!b4JqW;5mo zvq>$=3?)L^-b=^2Y)oK_28JA)HIi8ArL{|r0HCH=`7c$y9C;PMB{UA!8~@g{ndQTA zDEj5p=ZjkO4B!T>=(T8u4!q|3e-xv5xc+H2w!zH08Cb5(XEpdf`5gkmfQmyVH6J$( zyWEw&4Y-<0KxbQL1 zDO>DKD)StiMCi)^V0j)j@cbEu|G^tYM;T!yJj^GxcOEZ+s0H+%afWPo`U)|l<31X| z;mwqI4|vjoC(DTP@OHmscd3`ZRUUc5Ku-11C#)^kWx?>`r6wOqiz`un0dVm&^no$Ldt_rbexfA)&2sfCdZB#YldJcph8Wp^H7We%8tY}pq#hI*7| z(*lxAKUfDX=*1AxX>qrU#%4Dx`rhTTlfwm`^ToGNDB~y=mhfEmNB>O`+ea+eiC&%a zqJ8@KYkU^5G7EUGE%jHc+UZR16#7Uzgv8ah2_a3n8poqOwqOq13>jFE`S@gy+J~tk zQpFbu*G&VM;p{*Z&xc735K~rv=horqxO<`jzGOJ4f5H)#<{b|r>$``+*mzXc`VXse zpbiJ)1I=MW{v4?C9{nd5&ii*Q=_Y^Rs=z)u$!HT&zbkca7Jz_;m|2L##A0J#8jl#)5MDmWwpj2i~P7 z3YI2sq?o^U`HRoJW5YE7!o5Z|#HnkjEfc3qZ(ae?o3A@7!NbN!(c`8XWP3KYgfB&w zVo0p1pD zid=86k7%z1&nO`8?W45-Va|pixZCO>Gl41Nsyo2T=P;P3#6C>6Yr(bVnfdrXX1V=N zMl+ZlMOI=y%%Cp`Q(}eg@!qLh=8J+;tNY8hoHXX0jc-Yh8@Xci#tF-7Zb%%V8NOk=myktv$ zH_x292l>u_pNqhu1@G>bnus3jjFJ+@;T_$%eXZbU;~9}Uy1h=AH)37EZ)R$HaXOP( z$8WRK(dNW&XU2kN##vxfcG9s?=WRTC7uoW_OD#mF#fms!yee0c`N}9d!Y@k*eb_6% zCZ3-&dgiL(@_|mo|BuYt?k9&rj?yFOcv)o#L04|A-KQ=3moLp2b$|KPcKV4bZs;y^ zCa=}bzSxyGukAS7#!Ag)othvh{Bsgv?qNEWD+mL|cAFS~GXuB0FmAN&`~nSsV3dA7 zf`0vn#lf4)wen&1P(&qnla(phWa-~#3G7_&MWGnZ;s%REs^+)#c&xZ#W}w8UI4&Zz zR`RA;nuYXtk4!_UGq?tlTCbWn2x6;!dRET zh-=^C8~P#c*%$tX%UhqZ;&XE=QHSFjH^y=Ddo3hxJEMQS==_E%EtJ=xf=c zaPSff9$`M2(T=x z))g9?ba)x`N9Hz0ntQzgjB_5F-5arPt$&$H}6XxPX1tjh?9+aovL*b zKYx96gqCz%khMU;FC)IeRk*X{1KNXCQV(0_*Jz0}xs|}l`EVaHoxKr?bbphLT0dN| z@tG6t&bBPVpCAE`F_SY7?ST5>zTv~_^>vw3&38stM%C*!LL$(<69P%Via@5*@~@^c zB}2m)k4?(wSlzaS)P#5IyB~IKhKvtwaJGSu%@rB8e9JXRE>LNxJEQA7z215Ys{m`F z$43=V*j&S#G2?VIJ9|9Zi_}WO8X2LQSl*c1OeL@2ZuzCH{40#VNPg_+FD3_AuVq}oSM$3F0Ky^P+9pZ zOC!+vVuw5AS7@l}9;Sv{N?&!6s;sI`5)gD?6|ySHd3jimMcnUhxL+J9JLw;fq%kk> zRnQ6wL%nd`q2YHG+PE(#mBnSb_eEIurHzsXWak&Y+dR4sJ|bR_n7_OGSl7qKS1|S- zs*Mkq5#(ZKew!03y!qg;7t-lB_y%)pE_1r$5diKrO`$Rbn2B_muJ)Clf+_24ZkEMru!@iEi&z!w@vMFZn!#YFJkJ$N^@N1 zc{&ynj+}(@SbkK+?kbeJTM%(=Bo>c}qnn>tF7~H-V!iGlwMEcF)yD7afJYX;+3(T2^Wl!Oh5gZ1fRwJ35(nHeFuIP>_NWM`iG_D zo0s=j(>?SSjdsg07j(ylwnMr{m&HX7Z>e)wr8MmK;=JxX*0x5)a5L~uy3GQBtq*C{ zZ*<_PZFw78OsP(gAt!Qdm#K6}z^;7BYs8BCz&gel+Cx3r$;qUS1IJGE$?dIzMfnSS zEO$p|{nG*FaU~4jNsn$9Ey;YH+6CSCyM?0?u3lhM&EAbm74_%lTR7K==0%bDIA7)I zL2U;w{|Ha&(%h5SO@w(e5IQL(UddYNC>y24Cfw|W+<$Ey58+N8RG9*jq-uS~Q2$iL`LOf_Hx@oCmJcq2rt7i-@Auv`@Q&=(xf$>HhWadCpb zIOuRU+;h_?=CYJmMoJ$PQAS`tyw?kpVR0X)F=|-vB-@3{-tmwmeKTk=(#Z3E7Q(8v=97vh1d@p;o*$RBwSH>vDSSp z!^BYA&Z(htVXEWYAcrEcXzNc=jbDhGHJ5QeRs0lq8biCa>srW69t>sSP#v`rfq3DJ z0YW~=fJGoD{^_YAf4E#vewJq?)!d9bNw3azbOn8Il!Aw|9CnQ^vETj`nppWMxPfbm zy7*23{FyD*-OhMOr!h@+b-_!^KBdE>!sM)E7o*NX-u?YM?XyWg6v8W85Rx+L&9P|e zC%Jo_JQriT7;B_w7ejv@i!QAib__N|a^b{%lZqKQt5gDR`X2>6?|^r}qU=b*q!>qF z=yi;i9#y72U+qf8*_43q#r#M;O7&W;B zs}uR#RgVP$1{3ixJEz#I9a;z3x79|n#oZl6m-V{y;fPk^RYkW+M_Lz>NH5KkN)2W6 zFFZCW{pkH4BoH1a?E&utjAd2kl z$e0?@vlP$9n;0DMAVZ8~xLm{w?ANPC7BmaT-=@vq<wAR^ANmNzaIlD9U_X;7gC2aZdM9h)3|YlzYzh$nC&$}=Ax z*U}c9T6?>X-2K7s*=%X4PMglhI=u@?LnSXw>j;gWJe?Vjxrcmm=G4ke*-g1OU@tPe z6A%Cbq|NDPpf5CuW2VJ-{2PG}p*i&o24_s|vQ`vFB? zHXfEXw1w|B+O4kZy%m@=i)w2eXWx!N$CLlC077|Ds@xn)NeEid)2*`X&lXo;9}zy; z4c(aJCOlaS%06d8hOVVWpbwvV-9IG8E_H>2-8~Nznd3ot^ivPx;v6$WQ4cn5w((JT zU>}IX0%xXgqnHTcz1qpOKV$u>-}Kc?QU#L8`54aBE;P4seBg8 z`DT(OMl!G$7-2fAA41*r&*0BAtoBR%92!0w4WSLsoNBlO-qyvvNBnE2@unuIlra2| zk$&w}Ei*13|5d@l$Wq9C}F2mcBs*ny*UtqaWI)pm4&Pu zAN_E5MB$kfiBO49XVdFxIeN~E<`(zDvOR6e^e-I^jm3+|gb8o+N>v~319-(F%Ebb} z3Mb$#kL(7tNqU(13A`P^U0BI$HGhL&Ltk3)$s$H{>i(BHOv_k^4z}+-AJ3pb?`KX`Z!EN@3+&?>9xPZ~oW5W%18JJ{CfTOV{S<>29*&dfRrO5E7M0 zMiSXyx3cCyE$P0Q_J;tA&)D$u1MK1t_j@brgO5~P*!e=Wnw2N^@tT!K_S_a&3I*Q% z3w6e;`)o(p3ln8Cu8Q$03GpRIY$_+-iJSZ0^F6Z(>YxQVQ5ey4@AqV&19=mB0peKQ z@b-@l{C*cFFRbkC#?pNb!ow-*=T8w_Puzujp=4oA-(5r)W@!_zA?pbVTK>eoTA;lJ ziT0ZqVr}dr52W3xc2t3&s@OO-^i~F%-z$ZgLfDy3SEi-?xB)*hG2`sfTiFPk81uhH zThdT!k)g*#7Na9jhqKZ9tLWxO=_zi~(uOQ!OSXvVCV-#bQAcy^N0gv%#?YsDcl|yd zZPY!yWi?Pp8z0h+EoyPuZo^t}OXHrU00p!sm0lo%;4B%*g1(JunXcYDp%7cXzSUAn zwBhr_T6{Yk7Ull$^bOh4>=Iq8tEG5(I2g0Rtqt|M2|r#`Yf?~qjCN+B ztnUPDW$dm#^Qn|Zf0p7ydgV+qLPh50`nd`XD2UNp)O|E}#Nc*j!}HJsotP#@$@++k1h)ap9y zFpZ#{xWM(b-$!RwUfJZHTB=A+M7@S@LdJ+-%Ps_q1y$Ob#W(kG0oE8t*=pc<(8luX{;%|xrKGeXL|%?7 z_^tH*u=jJzSgt2WAF@$+UHJR0vTClbcFt z22Y@}N=kCl(r;eB-de!^wW-X(rTZn-;w?ecLC}x)e>kWE6Zu9bFj8a5kwbvh{Rm8){mNlhL}d#ZR{P>sXtw zFxK%2el@$6gENTJ`olFAn$gA}7W^NvSLo?SM;$Qm%9H-*C&szoRirJ@ZV2ZG8U4`F zM9Iu<)%#0=pe;%?uDh#G{Gb2y9l*gW)W^O)vb^G5ZKbP1le%m3RL`yIP?go8NFBDq zz6H(5V|hNMjp;aw%8`Eb4SN2@x|#NeseV@K3%1&(?+6IO&CgWwn2`EAXY(cCr{Z2N z#h>6A)0_m95Gs)zN?BcdK@8IoH47|F}$@yW#M{pA)DASdhKun+YIt8 z)_c=FHaN=z6@9;Hs5x(1N@wwf)!#RRjJYR4_Imy?ZnD4DoO7zLR> zE3Psg-U0PRgiWyiO-`z>c_L{8On}>~bHZC0)qz3!KSxJKR_~>?!rQemVp)ytY-Q7D zbzrN~VU2aWrDs~5uK7!LhfZD`{9abfPz<~6xs2flh0j1kW-?r7R3E71H+3B#D^+;Y z)Ks~#bmS!cw3Q;Qtp(N9P_pl#vF#n`;hIcJ-viT^mj~##ar3$4KYP?v4a+c6lAqg< z;Z78&a2pQ2kI_qfd3oP?h~FD*)@SFQU zZMX0hu|mKYz<289y|J&tO_$yus#ugq$IOU{`HSEX(gz2iy(})rnHyqP6ohiiNNaM>>p3PMW4YGXw1T~LpSiQC{<%BAjfq!9BI7q^bu$byH5H_=N% zfu(yfjnP4Rbp~YtF|^U&k1zTpQyM5G#8Z91c1@(!7x*b|y9I3}6?3XqwJgM5(r?qP ztS0tM#pPqHTVOX8&XA?i!a-1CHOPDh;&I`}t9@BKYoT`8zEjjb?(OZJAs$;oLmh4z zG93xk5z_emC)4R56cqxg(Y{I|v09#KegZ;GV7jrIy1+7nE+EA-4hzI2<6}9wIWhrV z5s?B=RZXJJRZ_`JD73%?I*5nf!~lgZ-bx?*tO_8ga*%#Vhi=mH3|4lr6w};v30&0j zvVe(;;7Z%~YwQUygqC4`1#NR<(5GApzpysCemH+w$+^Fe&wL$tC|~3s5Mpid8F)o< zgAY+=^|-^a$k!yCU&m|cWL8~%2*N)bdT z48_2RewN0_MKwaI^|*n9fFl33ns3$Z-X3<$k?ni%Aaie{sK*DUI51_UkGb7*q8XW2 z`GZG!Go|V5@n4CCOp!#~dH)LJz&HN38G9n@( zH1i20RoC5*WuQ>-zOyMWcu6(Nu-G>vQq! z9WJ2PFvN2KcJ#5c9K;iz#eCj)sv*(8tl#dJpthZ%ktq!+=S_Sr3)_V2Z51nbZGUD& z8p}g1?KWX@2C8&@w|j3^R_EE7lB?ZR)V=%s1K&V+Qux)~O!7UKt8ChE&?kcPKR^Z_ z<5X1;ecXrUfz+1{7;*V^Rf2g@t`37up9!n81b~uTlK#pYeM|gb^WFmrMVgi3XJ-kV zJ->Al9h3%Wr-&jRZA(UhQ500>`sVIj_PAanGx41fS?VIQvBzL`@Ug-tvEy2*1sB=; z%0}gxL<@iax76I5;pBt@=Ujx9sV$tol*7Og%{OB|Au%F5h+Z zkr5o%Ev%ZAfBIM!KFJ_;S}Eb{k35C7w`FUk;8o;0X494L_Hcu zi&qFbuUvWXp96`=`lr8JazK+#gwj}un`_65pPATKa5R7H)fF&&i-l)OPhXitKm8th zZ@is0NSsm@`l;yD;Jx8XQUG__gSp2Jr}Xu+2?0DqrO?t ziI#m%T=|wtUCicMq8U60_HGAa?_at%C|sP0hM2pofreX|cmBWpc zhKD&@Je~@NcxEb_j7*(DPd=`}U~T(vx7Ijndo%ULmGw~igaj5x%d8m?dNXflN|zR4 z5tGadyUZS82Z^RwqsZt(mWC^?^{YQHw0Cg4k zTAv^E0%3atJ14zt55yF0^Nq{m;7+jGIy%J2B5rTJ?RF<(VqyYo-EWOrv%|Yzb%efB zDEi5^9%m#oW765#>2jpzKu&%hEtdXsdYW3nO-WI4OrQI|#~zH28%0fhe+?EEn9kU# z!0-DQ7*H|^5yN_gPi&~+<=I`x&8Cahvzn&4?+S*E)m@L;sSB$}h=`gxI}N(Rn(6EQ zeALxVUtJ20udEsg2@SmvCamMux~*DT(&YDOlvW73-MVvF)z(on`uhw>ik|?ebpkGD zSaJJ`Dx&Y-P3mLMj*dXz@^*i;h^F`Ku#quI%Zlm@9$rEPoRrv)CyNFh*75Bnlo=&g zz*JB^#N4M*x6Pvd-_%+D5CT169`E>D^D7zIw{hcHo~F$OmwC)j&O(_woD)WWyaYgR zpWYIl!B^6HdUAq-dY?bzDxq%LN1)HXb>&U*@K68}^>}TmKmF%Xh<}u6_66AzYyQqT zVJKO6*^wtoFB3Y#d#|F19i>GO&6Jb_vb?Zr~gP_Q@? z@UJm2m1yLxl+xsdg#|14G6lJR_MbpGd;9y@<7qb^RSI1HXI3CdSXt5$XF<9GU$pr< zk8A8q+KoaI+#XghLe|^d_d_}?%?;Lb=~XIEwgWK=%YbPH*H8g$3+RgS_}z^X6SLD- zFMnOP*|(-k?*D&IR@I24mez1NAGe{-L z*v2sO16!)|Bb4R;-*KX=h)j$;8Dky<9#ym$UMXeu2+HG)Anz|edIA15up6+7cyPE= zNyjgCKvaAuIclrUXY&g4=d&0+LZ_|!7lv7ana$Dmj~@lATat8K`Dkq*ssqll&CmRR zwmPgXZj@Cz^lec$sMM2CaP^q|PbL)q$pp_}T;!+?A{$3n`UoYVhvQh+8j#fVOaobY zd9-k$0?EBt%kgL()#S4+?ec$p!f_wQ~icw+hTy>Rd@T$qoy z2Qft%g)Hfh|7iqIevYQVNAE0{|3uOA#W&r*BXayfPMOt-`QMfTsiJv~{ogiRpFqow zMf%s10@K6#Zw!&9?d|r7DF5Hz=Yz3%15e?v;|!jyTEP$uS~dXNqaU+pN)qwSuhh6a zOXvUn@CZ4YDNoKv_leF;&Fzud=*;xLN9A*JzMXo!#p6uGP+vu5Dl?*Hss zr($VIm-dJM-V~Bj`OgaQbVU3Vg$6-Mz6YP#()@R;SmsY@kFhIAJtiXCf4@@2`Db-Q zjl|f<$Q-$VPYy26UXTZVoTK-Ldj0+6Byav(HSm{s|JLt~t64!ow!gpbKY4(p#QyKe zt#Q%hbRyFDf2AAJ)cU{KklYADBGGnpt4<;M>m)xuNbR3`JNnX6`uVT@@1=)s{&_@6 zTHW2mB=7$Bcmm4*?aPVz+9glM|Fs@>=KnWwFs42}b?Q>kkt&Y=^9Y}RtL+JV1J$th z4(ER|0%`iE+AoxtnFB;{FxsY}$GxRzJM4tq81yXxdUH@}FeBArDjU z(9m4U`v!x7KC&cA5+&7y{|Og_tGNm1g|%n@U;M(!>+vVYTFu93#-B}^|AHI)JQfx4ymvXOD{Bb}|8=p}3eduy zFpAduxk1}j(#M$>w94RX=T6*NjMr<0M5)CYvaC#7D)9mYLpk12Yk>d;r|3%+zmwlO3S%oV(c}kB;|NJ9vZ+{-o zbbPE+(q&99_|@U5vgbk}XZ5b4eY(fsfpJ_`-#fLrgk9d+xzl4CjTKk2#M;Vr>l0l= zGq!~>&6`a<{33hf;(hG4Pj|1mGqs;}9Rs+U1z6sg=q26(? z)}T&5X z2^N|6*eyihW&HfH#{Mh$@Xy=eO%TrelTKL}03a$A$vNufjcbPPyl8HB!qXwl}ogUtDJ-PgKa zZ-cvYQ8~==BpX#qSU4Ny?~Vb&jUP&uTg-YEj;@<8a0`4fX9eoKpTV96pNE8%Tsr=u z%w-!-Ed0cq*85r^FKGPX?j)^9W`d9D_#kIk!G)_v@{%@9sE;LkeeQ*gHde^F>*5>8 z`kr*NsfdJfI^ z1|BY>PQ7wkq@BVc1aG)#I=EYGte#Z$XtEwqO#$lgp@okJsKH-wDW(Rs;Z zJ!45B2e%<>lZ(GskkFjEtJTCKTHRcZK7nw=yu>@52M3jr|4wCL{tMIUsOi!3?sC~# zD`Vx)c;yGLKJ;BRvO3EuIGIYl2COBBjCr=3eI+kDzbX*goPfo=K_uS@90brDA67zm6yBxJ%J|9CK$F5haC(DYZh2`?OyBd>$xHN$pmPEk&}n3OQACEDrPcQxkCRhJ6BD}(Og z5#M8Bhx*H_ph_Nm+1Wm}`^0@XXdBb%%p8Z5pAEvbw#W91-O_^dOs8uAqgYRUvU+pn z%thy!l!rCX>aISrS@_*)YuA<}mwo!7mi&RA-Ej#%hnzd!KmG4^u4-PxgUbPScU|o1 zAtP$30%DSo^%x0fw4cwa+UmHk)}K7N^WPGg?gn597p{Z~w|f^+z4~Z>eI&%^V_iJ> zl2Wni`o&X@lcD@S)GqdFXndj)0bXa=DEvYK$pBl3hHBPL%(!0;dBU6*u1dtB1cYFC zaRm(Ml|xUu)J8Epa0y-$j(F$t>B7SHpHkqfcd% zer{l=+`J__cA#ivIq6iPwcp)HNMzQgbmmHZYzp~~Zf>J`B(q-7-p6X?0sm?;%Ac|* z*#3wdWJ~`%y>oDH0lM2xdm)9S&|P(a6j|tY<0$K?-6|kZb4IfiNa@iv6O+n)zVf!9 zg}ISfwMlT-ilEx0##?V|`{%Gj0gnLGUft17<{*&!x|SJY;?B1?B?29pB2Xg)XLxWe z9k(#3nL@zgW_{M6q5Bj-unBgZ8o`vFhk+05Xv6m)5M}3+gp!49AchkllD5=Aap}kv zlQdww8L;K3B@plO&{cTjC>5@=f_0R0&TqZ*ly$@eKACoUrznO>FTcCTqpWtzGO0mj zWzA<5j<@Dh*LtfyRuCp%Y2Q)~gsZ{OB?aZ+|03+IgR=ahuHhRH6_FB?WZ=@UP2I=l@Y3c5k?(X`Izvp@1Kfal7K88V@aggg=`|Q2;+H0MIO9IIIZW$4p z&$zk^MsLrK*GaMB>r%#XXq}wXw2G0X1pgQJf!#nX%H|;b)xMH=IdA5(EyAeUOqcXW z5^Yqc&Es&0?o5;GVZ}F(%J!`tU13YWp(IkRWcM(YrMBwdP~}Ll)jS9ccOGR zGPGXd8q?7CHGES+$ZF_FwX(7cP!0TR!{`6VU;qaL82=Y&{`XhN=Qpf)T)hVD=#lc7 zudm)*0HnD@CXd&{M9Xj@beWSDo|7|pmOH2AFY?X6r^v2Qaz9)#W-=+nd=S4EjJiQk z#@ikM%DXOYx`a!Xt2Ox)CyhsM9gQ%$CrC>&_RM(RQt9 z)Z*YPm5p7==R0zJhZJX76dDt#fZgalck)(0x579F_4I;eUagVQyU4fy2UXJ3{d*ZL zIgx?Q2GlaIU`NAL{V$ci?}xG~1F=~}^+v)H*}Tr~uBz*cBCVfxmKVQE0r|yOL|@l0 zD##i({VMibVEg~k0<=jxAH2DaU2!tZSEf<}Lb*fZLK#ff3zYMB&K0z1k!6KpP+UOB z5`H)8LJ6ROog5Frd;sTkuRN$Jm)1?<77Jc<+~N1q*Q6xJY=jv2?9Vs7(^OQoRIe@h z1@x`OztRF*xm2&F+L>(QWAtHJA+!k6ZXUVoPn)=J#Yf&KyN@~gVAorzJh&OuN$jk!Fe{hC>MknxgUTO2DjkUX|E!6e#P1h%16Sab_>YW_e#k@ z`NOkU4!1vW=N{2JHLRaE4W{B0xQeu}Kjbv%TM*7PH51-Ym-H8e*nf{Pl1#b&`FaT8 zl`Mx6pRtazMIAfFBI&G+CpKD>JqRDx$X~Xamm2D(>zEbGOV0=!X z3vtk6UmKAWk+XfBr35$1I%$(q@!|81e*%@S67<^DsZ*zqhTDF8_7_5|>Q#QFwq0pX^6u6f8XzSx)l6BC1{#fj zAKSeSH6n0*{=;a|NU#Vs`@3J%&!X5b16{6gw%hD3tu2Nn zs7&8p$F`l-)qk>GZvKKb-Tg3jl=*DY?|9K-#i87=mO(tMb!cr#r&>#=gDTqpsY%|P z4ZmpdZin&oP-Rl%#SbQ$xBC%Fo>v+bHxc}P%XYo9yns%{9l9E`T^?}8^7}rZvN+bZ z&dAh{1bz%^{K|@2f}9mLMy~YROYyf#eGA8a)w4H1#KDX#f=`+f7MItffAIAoj^ zO6!`NT2^ICmvw9J%?;Rf&<=LoT5JVRu9zFef;{oUm6Rtnc^nKN@&SLpyZpB#&oTB=8N3Qk^@YEXF@U4_mbDbs&2c`I zsB!TDwP$m;kRyzW_kN$hngDD#=43?h)Rs~r^7bxa8m{emK3n4-nu|CtAG)%Bj{PO{ zsW9cZ40Jyv=9lnYFR4LXJ_6?Ws@I>!Pzq}hJQs*)s_X;(iL5}Ap^UTMX@e8kTUW8X z%j*yXMKg@WYM7_L?Q5;U!`juQJ2y}B$=c@|C~ELi#r;|6*wyfr zx5;xW(6c^W4S7(IJJIu0S!lG6)7{!3xE=eJi$c5Ep>c$L6VP*!x%C;rpjYm98mAf> z=UI=KgiA>Y*n5|Ixr>->@dx514W8eL(7+=HnVk0(9#Tz3M?XASYrFqcHrj?y^nC=< zYCS|90$R&5yhyB_+snCgk(VgQDinUMj~)(xK(!5h-&)_({P+6ZDK=HFJ$*bg2_pG8V=o2<-FelFJBdVOKwEO0~i*(i@ViTSzEqH0)qb1U~61-`{G|uwIlL{ zxWzwkO&z?(RmePyv}%>95?Mcb1*WypZ{643xpSn|W^vrBEc_Kp&Fw z58kXdZ{{f<(KL7&bZ)oB`??O465yAE)&vZZUYHr)r{NOEGXB&3GgLV(jRhY@q|%9u z*`5f3pjx&Q$66~2JX2Y2_#4X6JA-vE@jh%Hec~azt+d^B-5Cb@L=c@_*OzTA z-2vwp@}-AvD=K7k6B-|$*g8#W7}{8Ew|+uG!c_V=xEUfzoWjpUH_`@x;=;JdmWa-_1OX2|q5|p4kozO0?0r^`_xBc(-*OI`5HK1HE7E zk^l{o76Cb1B}IvmvDsytu?V!j%48x4%ro&UCF(Yy<>l!#&QfZro6o>z&q1MFWO5g| z**|m0Mw&c9y*(7JDHvBwOQP4({4p}}{}r0dY?sKh@J)aK*%1~G@(whUtVu)8CiAN< z5Rs(6lqA`RZs{JV#Y7#G%JNQ6#?;^R`-^*Cmgj(Z85c%D%^hpOk&>s=NT&0j*v>{W zh5euD<9^2(C!DFoZz>l^eb1G}r+GL&cHLWC!YZAgAXj9!U&ZXre%XAsl6aR+Y5gg^ z)mI;gG%a81R-My$GE~g_wx9pa!LM@g>;UpK@~4$ZS;AYN=#DUD-kR)F*-}pxGpU%i zGY~qS1No1-#%o$C*J%5Tz|~-4?J8AY=04FW&`pQt8H{-^{?{j|a6UU8=RGZ-VGZK( zW6R3o29bZpWK@qORbxDK*!E&3q$@F=`XY|aO?_oHMMc)K5h{PvX55V$Dcr7R*{siB zB0cA-t}> zy&gpqPC3(}oLdcZpXx#p=t@}6VOv@`%gW~+h>DJS(7s}AirdIdIFAjpqG!h%#^3=- zR{XF`0yFK;z>iGffdIfMH~qxe!`xc9Kstjhxh+ZwWEo05kkQ8T7%7}o_pzLIqtLFW z@A;IQQG-mcK9hR=r0$mO9oTbudHWFlWX35Xh$0m(N@b;a(VYmwu?xPSYe9{+uY5D+ z$VbbsU40JJ+7BAh4Wv2qJ&2`?tl)809@ThT%gSzS8_pb^Ps5yp)8ko0NsIF9uuGa3 zq|JuV2g6nQSdV-CY%-0`i0^Bnx;#r~k*V$7n|9NLjuA>iSOWN-M90RH1 z?_O=p-lkhHkVpb&45)@w-AHURKyzh|#Ab`(o$zxuQN4vuh{7}KpVLAVA2U+PiYODtqnUm>c9o<;Tf!}i z#Jie_1Q$EkN-p4wyTa*LV;c4cn|h$z-}$2*2+!~InfC!a9B9v)9?zKtNLeE!Log)# zt4G^~_8ZTab<8+bCQLl+wY?KeQIY=h5Iw9sJKuq}7NDl$l!uFXTAf&jLpUFOxL7a^ zUrdwN(l$n8rU5|_-ZU6nwlHMT{6&zg#;7h;E=3sO|5Cj&5y_-E0Yz+VJUd~LNb!u? zsK^+EPh5se?8ct+Dh`2I^u1kC-qC1sVFkkzKdLu&yjj#g+n{`)F=145$qDuFIGVGn zd|9QHf@UbLv6M@Y|4CKjVx$&CC_R`!KXrJD# z#%<3S_rg;}XR!!gjQgiRY8;k)e1HgEQqYgpXvPOC8c7(Dk9zvPo3lcXp;^3#^Ujhz zwHTBMZHr+R)x(YfJbMimJI1E)K+t0cYK>%q99H~BAffDYLBvdV<2%idSIu}Sr9T=7 z)7*{Sew+sx)!dzM`rAsw#O>2Dl#ZZ@qz6_>1nI%W?tj33*eC5QCH(Ncvix6p&Sl^x z_Z*|s+gZ(Y9CJA?6Rt`Pgw>eRFJoT_`XjfMQMgRqT62wcg}$#F5q#=&lzMzqiZI#V z_3i!NLK>aAWSkNw66(TF4K@ee|4kbJDZeoMKLi4#wEfS^YrD@obg#8McHX&?r&erb z96s}A4LDff;BZ|;?>_fBHQ>4(lYAossM7SpfrxG{*_y{Zp3uD&5|lwr)r(E{0Q8z* zr848}^Mdhs7u6JHEkR(1o8{Hpy8VS!61?c|(WL5?Cfs4LWEJx$88cdMm=l2AB7ZgK zl5ttg@h%)q0>+u~TR6x}I>zIys@>aV?D$2r#cWf!7J6eLx+TT+xx6jKpQt870zcBC zXGa>UU@sIa(eO?^sgyneAA%qTRyOU7#(QYwT}R67T4d7uxNK}hQd(@2Q6&DE|2fE?b`ei(92kb-T^J zdr7{a!G7tnu@D-{-Pr)Fs8(u<&f*N>`hC8j3f(pF`7|9J<* zl2xmDmy$}W6+1d}y%pY^?#%9il9J#@A;6sbn8p7sa~j`IY<7dFv*<-P6Rn73BFpj7 z5)ZXf*-`_MRqMGucY=;Nti6Byr7_!DRPS#7JX>pbpsyI0aw@iN z$u^wR&!kh8*>q14UUcp-$_;+vLx3(K3bAHsF^KMem(yvT&OA5S4YfXp)Xp-iNV@Wr{a=z|!{ijmn4=@*Tir+0WeX_DDy{ zn9g8_aNnKmREu&mGy^}@)Kk>VR*w!l%gOcNy!8{MTaC-8kI#B$Bc)1*3tvj^F+Uwy zCaKG%FrtorId2tPNT3mW#0e_j??F3(5Mf28qqtAR03_l(rnjm3fByj*nvSr(uYkIH z6p_}F%9@&yXyh(28pAR^O2m3xhh= z$}q@wZao2fjpX{LSOr!7FMMzR4D1bpGy@!OEgOr*&KHFErQ{?bsm5v`6$AICQ;MEJ zsB#C>J(D>WJ67b;YH6f|s&$U9>2NmyKuw7Wza6vh8k5PYsN#XT)OG~te}QRzyaSt9JI; zdsNufyN#FehOU4vY+4r}+nA|DiGA8l$GGas9smyR$^D<*lm~w?I*Qw_E6fO^k1;c6 zQCwf&_a?~p;<;P6Zb;xIHF{|wJhk_s!H%_{cck#;xKa2&+tl-?bZ&#$0-#-oD2nW8 z!j{GRfa(1>9ecQ95HN61PPLlN>VNg(t$@Tf#oLHU{Nb7SDK!IpJiPn|K&wl3ga=Ls zTRLru=d;dEz6m$P&g#~-uAITK{O7ziqb*{_QcS+eo(c^=*LE?6+D>OZc{F?1R8Eff z+~i${taMMpc!nE4mj#Y_3TFSTj$8%slcOd@!#>~L#`i9=AQAvW!HW^9$mhA3WGz$t zHL_SSpfrSzgKb!A>T;UR@OU=04eHzu zvki-_kl8yFoX$n8Cz+RYN29{G{fI7j0R~oSuBi2XR1y(JO4t)Rcz|(4GP+N>mdTDw zf!-Am9uvA7K5*{7=VmB>JMlNq^;^yE*fGd%RIcr-ROS-0cN{}9{_f3U|sRhH13cSLhm1qOIAeK zIrtyun0#oMGXQPWc2ik26TRMe`0{wZZ_j&QqDuZP@8Vj6rF3Mc-kvuFuo2p|HNRb^ zJxk=7*xI+})W)D&JA_)%w#(3da4JKYhi-X7?w)QTvF%;f@XHnc5QQDw;`exjf9b3_ zCu01ps}ib~O(Y(!zl!|ML~MFD9K*5g~D>myK_~9=Q6ijJjtZ zYE@%hISE=W>yWgJ<+9Jk%1^f`J^Eh-&LrEL6D+c_X)`7q&O2>+nVs)Dc+zJd{X1I(j09%Zd`K!ooIY=&oRaPo9jRRJozaXt$F&K0a*;9kJo(l zqjG7;K>9X3-j?^iSc*U`*tTq-?0X?i$u|Rm?G%E!FD+R{0$Dkm23SWkx|i;6>ABPr z#RXMFLakc%{X8U|sd-wNp(}-n$NiitZgZ|pAxije%8N0;tn=ogy9p8bfQrMW_<;rO z@Xx&m7?>{G4ISK=DrEK`A$?dCSNM>~Q=nh-a^aUv&FpiUl9ec(>4s{qP9Qb0DrR;h zAoH4I-!=Hgip>Jhnz?*->^P^=;|3)As5Lwbp1$dS1MXkt#Iohau&^Z@dNgnLjp1&$ z9*gn`W$TDcfPBBT4MnCJO#~>y@+zWkM~ou#>Q1uTYLTsu`K{P|pJyFsQAGAsvIl5F z!KoQ8G-S9!czLQoqfM7305lMByF*L}3w)F2UlP5n8@shwD>d$cQ6rDuWZd6O4ABBk!YK+-#gj{O;GmzU2nP<{Ex4ItOAjk zgS3Es8qlgc*OKb!G;_KctLYo6>BINhz)-g>yc*t~t!+YV>H`-F8irxsItsl^mZOrB z3~F_?(M2Jcls7LtSGc*5dsVm0O7A`yS&44iSbU8Pz3R%p=Rr$-#i&*K^_W&3&uQVe z3pk3wIg`XtrTqsF{HwG5;Jv|1ot)xFYC}t%wpnw13aP+mrg`p6A7P83KL1jzjuy&2 zsjG3Wsw}&MQPH8TfPkJ?Me5g%T$+5eP=UO&`}y^dA{b zxE&J{6CH!nrBBZ59D53R2mw2 zf@{h9av`S4OSIefyeP+u4Q|^;==@B^N1WxH9y41PS{pGx-*YW+s9Ti($Te*coZc-E z$s&|I3+GFLGL2;|B+@52(iw}>8%%9Y)lN#C;3bYe;tb|k`ye%|)_tw8^3j%dr*2rC z%y?K2IFn&9OJ*vx2cJ*&o|hae*$7WrJ@?_j*9>Pa8gD<4Hz1Xrt^C*?z8EAYjY8fC z^TpK^z4yoIqfS)e6Zy;ORM~-TdMxKVNb4s==!lHb)Z?-0DWdaT6vKxof0~Xg6!E3m zz#Ubc-JxF9n`He#E?)czk486tXm8SF=ssPm+hrUZU%EmO%ohv{gLxWaUsV3r;0x2M zw`>k5olMzdBQ^Am^mVnX>k}n?I8?uS?!!VIkb`W)@HcSjvG%Pe@aZ{D``fQ)md+hR z&AZ}4m)z`-&9M((w4p`x4(>V3Z+I&VHEuO|9}xwZ3AgNxGbB$qPgPHz>XXBV*I>>= zenW@L?r5%GuN;#(67DBstl_^~$3@#&6?wyO?Uf{FExqEAaZo5ql!U=(LRe1Wf~0YwJu~lQCNk?D#b2_kFkY~u{GOrPr6yy z&oENL6$9lT$`>2)t1QCHE!mm=Xf)~KeGbMd)?MG*(2hf0T~w}qJT{2PV5Gx&k^1FD zV5HTe&q*U=voAvpnlh0?*Ukl!KQD-EPp2y$)vKOv=e!9DOnbjydF$TbqVS7|=43am z*}mb@*nYo`mv44u&Z)QiO=1|)V5dbyBWZMHwG2C9J=r~;qEN^{lIJ`N6@L8#UFUo(M`$0tPMHG3-UeFSQvEnx5H|q5c z2tfMU@HBbnccu_8F&O0bw?M%*`XA%ZFM>NPfUEV%E zgMa%WJmtw~{2WsL$#Q$FW5QrV;?;7HBOb;U2F73Poo~>PgN+=iO`TL7kH!sayo@3J zF%%wr&eTngcfbGS54+fn(1azCqtT_TvQrOIjeaGGa0=Vm)EMIvReDz=?N;%CD>cR< zc|LaYf3yIyEOe-{)wap7;$J3M$WUcjgyg`t3M^7`EJBEsFj5mNvMgl3tS>#p1rd)C zQ(bj-CeL#FmpB8iHp%mp4GXJ>7MQNB1{U#FSGkw1H@>f+W#_{YZZXdc`|k>9&ZH?$ z7djx$rOV^%p z-X%7Y#!?)}YTQbZn@n=7&>J?{+>x38-Dmvy&)>>3@Lmccli_7-1Qeu4UgchBvAZ_v zd?S73<#XE`j*<4^koh1rGDPbz#gP^cBJxHWY@Zwz!*d_Ca3!|50vsuyMln4K&Z6co zBuL#5$o!?sJb8p)=Yf$Mzt>g;V!eyF5=<)Z5`OgMY*0^7W=OuhK0eM z@D$AkN4!iU@hEhbzyBf9J5fBaLK!QN4OS zNpPjtS$6f8d zN4#8-xF=rD=ua(}=ZfILHzV&ss z^nC|GMKpm>T2R7xeQNEzZFjRrj@ANC$_>HZO-6TN5!A_U)C&Q zcsx66zn)$?XNCN{kir!{pI<{U*Vx+<9HLZFIvnA4tzX>$I|%7Abb(3#GOCmCXoQ=K z^q`b-yL>$NEM|Nlk+s3R?BNrYZgD`%Lg@kAWs)%OtW6HdVU57CxtdnBd6$U(icN*1 zcuD_YVPBJWtXi3=CI-8;(`2XsJVk7nAp@cMo#bK`ijXAwHP_L&HFo%?{YxbF7SYik znO^HjEK)tY-Il&a{aG(oO&IR(B2Sw1G?npbQ#c1b1Z^m4$WZs|dBeX{Hg zFZQS+$>ZbN`C9qo*Agu7TsAFCv(h={1uJCh-ADAaie-Y!e5;9nyo1b!SitDot#YhW zBftBxaeG5C9H&{Y8JVP=N^pdsxg?JW;jX%>+nJ0I98zU^n42sdd~^}%vwFE+w|TV6 zjn1RTUD~B>5wD%vX-6BR;cn;Z7dPd{4gbuz`FtQ)3;DT&*2H&A3GwBGhw+LSJdfC2 zf!ZG3TPEJ~ayQv6)kw+oe0JBFRSeRFWJeIy(0CI(tl*yVL$%rKT2CCr`ftG%J9L-2 zH}A35Fb)ilpO$x2!X#K{0|(z*5FG{Ew$k*>HJg8#7X0`tt)+8&j@x~(1$B~;c6)*j zZ5Nt`mT40`kN!4-c-6>|sw&Nqo5(J=C6e<6ILMvofv~9( z1>}tD8WpxDCy)E$Kc*lp&4s8JY0d{7Kz^ysbX*UvoSQ|dJSbGyH$KTtn&U!#&-ILb zCE0iI5?_FY5C{;^_C`uXn=MZb)nuOtH@gj*cfN=D;%gsRGMZam;0+B@v_OGK&2LFF zZ0PN08%^#`%|#Ap&~aT}_P>REXWn&rXG$b$Xz`>K8+^DzaM?d$d(M`7cthuS-<$W` zw0Gv8r@zgMVT&@gFPkA2nTj;*6dsh8+NE`;Dd%ISa@7tpuc$vg$s*PF=9NI(R?zf2!CE`mYA z=YA9s;Rt|yX(){9QsKqDe|eiv^$Ql$vmh1+-)7@I8`Y~+jby%3fw%rE*ESUo>e=@B zVY2U5SEDslZn_(ev5;IQJC|$9TQ?%A`2cQ0AN5A?B<+*fw>%>^kH?vV++g33# zZ3F_Hj_uo@32w%4{HgN$vzV_CQF5kf-H1L;tEekxl`5HLwG3(UKbQy)9UBkCFK!a9 zTFkdin*2t?*;`5A@|Lip9J6Z}?R~iCMCJF$*Hls~Dz%nnxBV^uMTjc^moHGiZ!*+> zQx&)m!u5aXtNFu+UzFqb+R|q6byw03!0v8fhp%yTGIrji(RpcNafHK#Z*m}W@T_t` z!q+);IgvS5|B!KXijEtIy|Tf7MO*F6<&Ox{TY*0S^Aih3NbbuR zh7gPp&QtaevlVNO7mOEpErKgDa@Aw2Z0LJVn0r!=-080gC6i z#>e=54fecaW#+xt+drT2p35u~-9SKn*0k|{ftR-2uWnV-@=J6Z*v9NxOPY&laJ)Ql%NF`(E3}6*-mnBQU*{#a{1cgDlcRpnt2F zR{&OOqO&inD#^+U69|!B%2wJ{HCp1)iMYnwr8#fZjJ!JuHx_n5nW|{)c)7{B*=?jX zV3Pjr`NRFo#GeK+u6if5G6mew*caFBGc?TOg_R_!0x(45WN#tzMU`AyQAMd$r_q;$ zVQ%}kZOg6lkynd4)_5p*2e`XLk#JvHa&7yMt9tLqTX_D$zY1FXN&+Qb>*Mq}f?kf^ z_rd3}t!@%Qw(d*m{y$MVSUa z=TaO5=;fMx8LQAVP3`F&OIgXM!K<-{*1I<3qe1%jSs~?Df_?Mo{R4y_6WLE^;$nhJ zpz(y#May^Y7WDyISlU0Vu-rr2wP_i5{mQvm_@@n^%VYNaA;z&-=X-C4THgE4V;|t+ z?6Xl$)0u@HGY08;JwAnl20#twU)HeoCMxXohaj@_yX{nTFh763>uU@5?1!O16ODTd zjZ1ll*aeat4l5^Y;o}gzPU1{qK{_4x<4RK8{4TM=hcHi3TGS;hu1)aztIW=B;`0=(C9QdH>1mj%XsD7tw0L{`9h0I_Wf-u-vMDXY|mMUi`_{l z{_m_Y6kKR|gb-gfvl}$EF%4gsD*(XAun>D}ytP=OU+_x5w563$ap$ zQtGiGB?@BtYT~<_-_mRiyu30^gO}rz-3xCXDAO~noaixxRPw3CVgvcJ*!y847Zp4{ z=t@wvL}}VEHJliH^FUZhT>W6Laze1~AreK$!JcQxF|OOts)dU?i>sdSs;xy5#{Jr) zEA}rr_oq~h4PAy$heH!idr6Kc?)IEjlA8t$cQ@@n#e6PwM#9W;m2=K*7>z69Tu6DI zn-$fH#(A1}Emw*J7?lIQ>vayl)IfiQCyGmk05GKqk%DRR^)>;(DR)slO!j5)8N*to zqAi+{jZe57q6R3wD?Hq1yk}k5l^<_H@Ih2CzhzUc0z34H`TqA*FiV3V8>`7f^H-EJ z&OpjvaWdc8tH|>v(pyMU1g4iBV{IdR1X~?@p3RWhH&rC>cA7%=*>fIUicdmM89}h% zb*8VQ2vwh4u8p+17~pg=frgpzb=y7RSLY}N2^rdz$lDn@KHuV`#UL1ch;F{|qU;FA zyBx^sydm@~(#4Z)jUY~cKuHObQdJV2FW^r?Os8q0sGMX|Vb67OLij+h^(H?(uLN}> zQyL3B|Ad=k-&wxK`y|e#nahG0dNY@3tsu^T!ukU5K*%Zi=zi_oeuc1~uYzK`{8SO) zgAZXyqRRlvN@#U4GUQ`NYXeV`hDyS}cec8bbGGlESGH)b{wF)=N#7geNh@8vWWrj} znDc=cv*xai$3-O=7e;rZ@;qDQ8^yy##;9op{bQ}ais!%|S_RJekf-nDY5z*AFv}4LPr{mII#w{n<%pPN1^x^b9x`zVRqm~z z?Pa9RTF3S`#0ej(c-#5u?Lf6@RcN@rYPg}QKRC2UA1D0VzoUC{YlbTEi`KEH8GGsl z7cC`7evV=YDO3}?pjkSIPQ+xqGWqpO3#B_Edxx#eQp!(oq6FusXgF!%A@-SFaPH1k z9%V4>yWoJ!Edie7;AUQlvKahgf zvknee_S*m=rM6^tFEZa3c^yu3T|;fmwumMj&yMxMA|dRN?2v@mkik>u922{Cx7W# zh-i36c-?zTE`^xLGDcaaB=bI(^}yoyXYoI}6SsC@;qYD&gs&61JAAq_c^4D!wd}@3 zpj$^SE~0$JYXfa(FssQ8Yd#+Hi!O4r2r$1IvOJiUlE9w^c9(LCaCjJz1gQE zAtL62l<&vmi$jQz-!Sd^q4MXBW-HP0p3n!eH1quROGLX)aua&(*r9E}4!TgBZ_b7@ zTO}=b0jF$t48tA_?*nNYbU%Ow&w$ERi^$b$X^@qh0#9UoXNXNkQ2yQ0=dW#>BDfC4 zM-vk(58DrB^5Nc_vj_7JsJ#&TvLfs1ok_@OMYagaXXcHuC6p4ryFYPd#pn0i?h>vK&oOg*N)oG#<@#g22qx^y?Qd8s@dBEr7_!#mrB_FO2FINkKp5IMm|V zQp$`J;#KKCOw)eBOyLH|0e6_yI_8`$ZNy59Yy+RkE_i8CWU`Dp_L7~5RN>bdt(Vi0 z5n_@4fOON#hEZ$P75e=uEB?S;*D^)|d*!xo7n3R{eyEWiyy%@;-Q8<@y08f10cO2)9 z;J%G995_R!*lubwv!`M$*mf-(Hf!+Me(TGW_Olf z9t@G_+VjEB-oX}p-^PBGqsxJ6Ytcu(zf0@siH<@5myTM(Oi((a=;-43AW=?R%GN8` zxH?`j%+!%%rO+xUiNE*@eX;cI$ng@1pr~qa{$pr{GF+!_ISdTspjj0GqL9ViFm}8~ z^wR6>F71GNbZ0iiFAOcs$u3#rSwQSBPR5ZFJMJ&mJG&+OoV})v%ux9$c6-vJK!=Tc z3A3r9aE!e80(Sz-4}^y}yL0L{#95M;ZCPYh{tdnNbuSOilp3YB2Re8TdVQ%NUKsj+ z3QV?My*;ZvuAk@_)5h2|gy!U*3MyOnR_(9}!aC$G&wUxE{N`xl`!cfxr%7x*kvJmB zjqbJM0^mX@iy6eqhF^O9^&dY>yw44g{&b!BA6e@UpLrkeY+er5Vd==iHwsj}SsTl9 zc4!%~#sC&_;=b9o#edH~09>P3rZIPM#3AzLlQ2tL;n_2g-+z8e2iMKiF+zwi94;3w zi`tBluVTS{e^r@Z&sXbImAEtZ*k+`ygVN*u9CPj zelN!GoOF8Mzyx`1S3`hxv~x%qIrqB9pVO)Kec)xRHkH}uWQBr1|%FK1I-D0$YNfKG>54Upi|FNgUzG@t?~Hll1W zgyoL91t@3*t4~f7)JR(bJRLo20wyiL9*}Nn%cT>qv1Dv|q@Fuw;0(g}K>jE^lfYoE z>Mfj3VpgvGKJ>>!4zPIYDJS=Tmuht=p-0;QuEm_`pHU~n zculr$mECM_ibE23yGmNsL`aZG1@ND~8=cSztgU^xg4ncV7tmNER=Qpey}i9-C$O}l z&ztyG4wLa?r}zw<&4Y|@d{jd;De&Q{6p(uNdko5Dc3H8|PyP-Ae5%z(YGY+2VtR=pIiMYP2egDnNebHm@Dt zTO(t(RIzH57^b&$8ejkASQmxsw3A>nGIKaUi4a;&Ulmp$dOr@)rLaP}+*GOw^sA2N z@3?Mm;>Rf}ye*7Fe$;%WYHD!N4_VHgNm|I6;G+$yVFYURuarbs-8t zhmdlzVsY@>5r3n^vwWeWMOIQ@kp)lC^Z^*gI3lcA6aKnnNCg)#k=qL6v zo3p3T!+XV(-%%g14W)ehI$E`YpWUq?}2+1U<1)cNGSQ_!Ks9lF+g+k$fiRrT^slnvc9h zozNoCK7?sS9Bq$|n9lvP(X?A{Jlt>7O~37-mJgsv{0UYY@tJ)TAVKYIK!NpQ?neZr zhd;Li>f$D59m$jXEa#8^slEaR4dhD5$^NeV`tW*U4PUzayu^!;o_hz|wEmZL5EgS) zP`nCzNe;VUVWh}j{^{Ri9V}^`|0C@OKu$HmUR*Rka=TS68_Asy0jIe%IN5dP4 z@^DDtdGukMw&TYvCxbw}h4TteX|@OKh7#GFZ!F+sk3!rUhQ}lri1wQx+Z{cA-}Sya zbf_y6^|DUux%_6=$aC8Bgku!6fZ$9;blR_DX2%IE_stR~c_zawDy~_h1d(r}`NFVP z)izIq-X_W`WHPMxm?}z2=^TzmmooclE^D#Ytr|lpG-k4}UTurLU&nJSyh9 z8WmEil3P@r`r!2sHW3q<61u+fL0V#4w0{jrp=;NLt5l~59**>n$RKIlM8*5i*0TAi z`)OZ&iaj8BFZojJ1Uv42_j{lJ zM21@u8TFu*6Fx|8Q%ii=IkC1eOmCiLos*B<#{Eh6TjH0DepjILmLXTJ*? zTUV@s_#DuTQmTY8Q_X7^J*Qce; zpo}jnso*6!LKxlZn`XQUM@K_;vhogFNf*IUy?PiJd^7BsgX5rTGab2le+gb(uh)K0NB;UN? zl^0-vJatIhlULTg%ut``Y-d;C)Cbn?GH zKxg>wn4P;bNXE~vwho|HecF|u&P833AUmqzld51;+R;W$^#=wLZd}u*vm#wxchUM? z1@hh4+)1ot{zeguw&#)-Q>nRXcWcjwapop^m8edDmAG|6ADyfO zqmx{Ywpp|m{@_y~@yx2scRJ&%Vbz4gK{EQrDQ$;_8vp7QBZvLmoUOH=`61B9PH4}= zdN@$l3geX&#|3$D$zE(z+zX^gg3HwS*i7O%Y#ObHKP7a zZ&y}JHU_-2AP3Cv9og=7veSX5!G#$4;qnx(T!?PF>TxO-4Fd*C=raq@{b;uivt14~ zGIKhLOqdVgv8?dm+FGZW7nfZG^pfx`Chl*4yxn>F8PL}DZqEwq?=&uAHQE@+t$EYg zJ@*589)?bO>Lu&+?^z&U46|x0{D>WK12?L1A=o&@JGaT<#ew19V7`#BDB;IjfJ@%~ zttAH|OVwwH9;5ihC1}mh+7~Y`jSrHwbpW!7hH;`T3&V z|D{w`!;ClB=xUU25CyOK-sDsLScf*WI?4>M#lOQ!0D*g10h&xv(rFSJnEBuf=PpVG z^2aZ562FQLb;`QSIJM=~%Yv?=o=B(FmNSUjL%-Jz2!$Ono$fo|MyCm_x2OowANG?JcORXQmuLY zbkya%vS~*cPXmqQ;=r!2V&-$H-)_v$PX&S}52>x}IH*V8t_J`w&-09Z&=G)RmAUU| zza4%u3vDrbuGxG3K+NncfRyt04KZ1;=qr8!b6HW+-Lw?4kwbvOBrF4tnzD~+D9!_a+FPj|%621Sq zBjDD~E+IBiz#Hk^*L&z6RNT>|>N?6^(51f4_|T~pTa&3H$&2wct2Ue z^rcaH2?FuaB5g4|Oh2oAVW$B$?6;X?7-yZJN{8q6PSahSU7WI`W_%RG<@?IDN-EJ1TT!ciLiG%1#)qGiAq;Wu*G6c|!JuR68o)94eoh zF@qn9(}rif<_pMmSL0{Sm+7BGTn(ZVCVRTt{mU8Aha$kB^dfXR$^L7qk~{(eLXK&Y zp&BH=v@j#d7vIWPU^?^b#B8xS3)TDKJ4`CDq6I>>!uI74mem9$OHbd2A9*jQ8CP9 zP&j}DwwGlQk=V*XtLD$XxxbbC3m9c-YQmMK3#C+u)bkW8!@?j$ zU0{R65tGnfbQLgXr>5)tlBJwSqNJuBH@WzK_<9SdDw{8S^w3C4NP~csfFPaH-5?DD z(%qesf`}j`-6h@KN=kPl-HkNdIr{zncip@0x@#evbv);N=bhQJ>v{IGv^b@l$(#T=nV;6I(HM^9_46(TJ;_mU9XE zKI2;n5>r3a<@jd)_%Qm-2ZsZ6G-<+yt9i+Li5(1KR&vmm9% zPZ7vl^E$QGG&^4-Y!<4c?U3M!!zq4uhs;l!nW?|xxihr2`Ottr#s%0@QlbMZLx@n9 z^DS6|;p<+0L6WNVi*-M^_PldJuG~kSDXtxWX6M64a$SyL?P7CeH)#4E7>_mp97(zm z5puQFn;=5Uboq2591)0n&tjqduGFaveUdtrE`~e#sBY(?8 zpO<0e!sCJfiPB3ZFF4){Wk}L)_#V#gTs*X3B@B#p@iRnXPZNOe!!Wr6lxY3f{PzyX zc|7`@7P((;WAz9LHq<3dr2ZxdG17{ktQA7s$>-9<%u^Tj5qp_ zWu8G7o<*0$4*i=)m+=8HJc~XYF)7O*Hi-uALl%9~SljnO_XkFKWeBdkvM~XnVocFF z9XSI6yEqKsEh!lfvWpWlrNKf*%vxbExFIXtk>^e%J#IvwVa@05wygw=Kdf=bZ*a#$ zfyT(SUiZlHM{=n^wpNsUJL1n2j|i->c6)riDJj2Q?m$P1ij>g}UP7a9`qa-T&x%oKSg1ES!0`FdWvlE<}8n zk*OeREAFJ0J*72gR)aG(#&-3mII-$V3QlXNVdzD zQEEKVYLZ04wm$l>`8B-kh{RZxFNT@4?f-c9yJVB1F%DR=+nmymvfrPg4u8)GCP6Wm zYney4l<>)=WE!%GiaNYquBjZ zJ4Wb0QZ$VEWB1Dk=URb*OGu9+V-F0{jkIPY+`ZNccRa#_8ly$K(DjZ|dZSD_@+bC) z7=zL8glB~{p;-uT6-7cet0k)6XoxBKJ3mff{e~BIc6ngq+8#Ks}*~-JCY?d zPy!b;av;r!-EuOw`dbvo^>8UUDNR{f`3Z!zke!m>d7}5J6PfQvG^^2_BQebLW%ZlH zk`Y8mB!22*x|cK1G^)C*U#e4a#qp*RSV$Of<^@T37AJ=q_L#}n*!XkLRkYQnrnfYh z2_N=9hfG#)+YP!|Im%9Dsm37sN<*OR;zT)I2QIJ)Y(ro|CJTsHm|G8z?5 z`fluvSA9>jHUS#vbMPQEp&f`pBakZh%4KFVnHseG(F%~gc*E9_ubB54n6E%|V80^6 z!t&-#>6Yp(sFovt)fKU4m%3;+UF+SyIy-tkVEWQqXwy*b7|Np;9O1$ zmQdN`sx(;7RwlBUg#-nSv8%n+tacdhX3tg$&z6YX4wMCxlX&M^`!?GMW~IeTzUDwE zAuf0_^J%3$@KlMp>;&8%>=NZT*$sPF@#l;uext%^>Id8c8pgcj8y-Lhf#U5z<>(gA zKX;rdP^;OV;Hd#s29YgkcN7B-?BXVG%>;V^9^+)ZSgKM&{lD|I}x%w-QjP`hM#-NGjl_>%2ap zf?=wsHC`Rz zFA6pl;sEsCU&V(yD8P9%f-6l$@R`Gr)WM$=5 zggA1du)6EeKv|nS3MZZH04wyE|JZb&I|@M|E2(T_A3owYJT0prUUKuAw$ERw%nJt` zK-$aZo8_=avYk|92uc_UB%Da*4fjcK-Nf0NgB`jfy2c(mr$}E~N|)>xzf#i)SIT+9 zOjUv0!|9jfn49GKo_A+4Keq(QZsYx*MG3$x-?f-7Wgva7B5v_8o(0TlK;RH0XbH+H z(7X_bRSgmj6p zT^CMEs8w170412MFTW&?_d2)f^#R_`Mt=A!6>A4~#d`eKB3-9?1^C~45_|*z%VrS4 zVV7^a>qKYR0d^pGjS3xNe-xGWVtE@S?|XP*DIx@%%pCP|?s!iE+hT>9T%gtMv7<2N z@I@$S!%)2UdY#v$D}5FTmlLCHxk<%v4Fb5951yAZr(9nJ=U9zu03E1P;#E#Cet&5= zLKgXvx8M(O+@>|4;BtTSlN&4$*k5c-K9%+lWKJwsQENH>vE{cxb`tN|99!=ntNzZ- zMODpd5a2p5i~!-A7C(h#mF)iF2WD`!(x7>DaU^W`+ZaUu8jVM8wUkwQ7k$6sX+ZUb ztWaM7+90tXUVyRMcgxa4tv@f8{>Pp@_L}=6z!REcjcfPW-LTv$Cw|goYQ5R<*auQD z&HiUo&?-Qmug>M z1hVBVZ|{H1F>EV&e9h9{rdSGM*89R%aW1z>wKrq-8WB$4nhMA4fkg+vYooY=eNa?G zia`2U1snAS?D7=R;V*5ifZK`hXdxzt$Hagk3t?e0J_WRXfa1(oJy3pW;hUv{tYm() zcrn%vzE&iaP#*ARTZe8uh-rl%htr5-DjF^`dR>ABSsXX{^(UHtr}(8^v@ZL0Ci?zJfZn-uk`{@>Y$zn1^hJ0aP{syg=5~g(oA+YeEqL#~Q`v-eQs|$PP;l z_rB%ek_-x#kO-pv(L>#h`MZakk0-8f5gbbG>RKWw=>QpNALUtiG`Y)X$={YZL*w>v z5r;&n#}yh@zyI)te3ZNGsSc~F+4Op}Xz=G|T{Rva1M8+~PAeTiYn`gl^ZTSwvPSUq zBln6F|8qbo1*9-wE@+9x-{~pa+H|b9JN#DY0GcmVpbcM!|Qo#f!1qY<=QQ!s!yOj-OM~g9YP7RaB{>I?lTQr37>+@4juIkMvdQ zWZ>V!FmY?U1A3$9%5o}~Lh1{GD{>5Iqp_b^xf)$sWF?dX6=o< z2i@qwffwxJ#_EzBpYOuAleKP$&|mL>r+vBXC?2TJd&v?bkm|h})<^JAW9q&yQ#d(u zjs14mm}3A3I8MEcwFe6q8yd?6Ny*n~IQc^<|ML4}>0Db4;;2$)BArp7>yB#~=^Mrm zcKI(JndW}^V<>bcCjVkvi7f(dji=h?xL1NVi)mQ;0x@K)s1oAOnKMl>8{V7Vonnob zlhdKO{Gyeut#x~F-2`obAODftPm*uuwE>vbh~51cWkHB8aD@u|e#iUQfo5)=M{k4< zbqc@x9vp|3VdxO75jkXM!5VW(xX6<7@V|3bpVd?!n>j#lHGcf@^VACP>(F{25cwy) zg(-7)_?XRI<-cS_@pWIexw1T7^{t*|`-He$V#kNP=%g%WWFr9cIg#L*YJ(Msj-2_y z#yrFO_EW`|J9|JiMf3;Mkw=GCZ3PlAQ{GPKE4fzhdN4(He`J4``M@Zd7HjP^ zi6-YV;tCNUYq2i>URl`8!=gEZQH82O1P7AAyc9wr{gt=SiC&!N#%qT+dT-^*#O3+aelC1HRHpFShL@YE5QCJ+a;)fi z5U=N6>Ln(LOzY?@#~v5ZPL&|kcptr;yY&nYFX06_Pj|YH=ffIROo*FXTI&O{T&Z*s ztiJ_CjH~HEdWDN`%KURky^^b%6zUOXQ*s1Fhkwdkfbgr|-DRnG4o6cF0(+&l{l$Hs z9Wjbjxz?vP^y!-V9gdwB76A9=genm40mT0?>FaS=pv>s~mUHp;FJOAz5ad}R*|#{N$Es;dg7$=uy@&nK6PcWE1cS&L-2zP!n3$?ws zr_0nOzF$1@3nT9kw+*;0e~=%DEvK1H?8St0rPN8(^lW^wG26EEH7vIefp#)tvvs5p z)I%ZDo%4M2yQ`ZxP{#uO5W7q;hGDQCh!*ES~Ex;9XVo-i+I*_j+5e9 z|A?KY&Yo4FD+OWpPq&EHY8{)NqUvkSUe;}eXBOw`&aUx@VM>1E>z>l%eZ_M|i8W@G zdwV+0slJqlpPlXxN0C+i8l{P-p(Qp2em2mo*HhU4vR-H1R-a&2s!YiD@AriIm+?tS z)EE)@?moPJ(((ngv z{fP8;8gkmTr=4?E{hvUA2Sn&qyaIZok-|iTtKkzrByvb^A;WXGvt=1i!@l2`%WqCk zMmtr3hh-DQsm0NdFZ)WjMD8rc%P#e#4HV1y$vytgX8?YcG`-c!;qQIf{+0OiKO50y z&OdJClkTKmr8^bX8t>00RV45`pY)FezbL^Oa6nIzNSsOAn30fI%~n^NSJ9Z&)hqqZ z2;k0w@2_I@09)3>@7xLpt~=sb25SV?uL1K34`X=pMT^p$v~J`T=Ni5G#4H zWEM!%Gx1Rs4jUrWT86gswiWuc7#E2jdYyh5uJhtdUq%@@`F%f?fBPBHd4WzycrWG= z*Vz@%am;*$xh{XtuAiu*;E4ZJ`^^toIQZ+(z!c=e36Z9vI7#F zUF7}im0E0O2nYyIf;&Om873W0&~wGna(wEbE0iWGB3hLe1hgKj@|((ZBxy;4f~=Hs zFK|sv%^s^=^$ZP_dpr=aP|UwlS^zRUD07_rzK4hH(G(#)+#~q3_&9wg(-aPZb`}8) zWxnYIsPthMZ<^s9PhD*MlW;qNt2UA0H+*zX-s0F#`}pQM9k_6p>m)R(l-dMaoaPTJ zv+E;(_jFj0aLt6wF7GCnhr3H~B{ktgTxy5+BnW5#L2MVl07*2Sn)4s0q=|lur(Hla zQVCdSlzzL}PaS!O?E@M*`7U9CdK`5^@B76p67+k>@x>3nm>;U@?ds;L`K5LmZP2W- zh>al;HP8l8^CBNq;hd< zC*B4Ra?W;^pe(6M^p1&n$%5le(^6N~jF-uuC`xr8F3kEX3C{Z+v*_mw(ZZpT`mzLJc-x>^V_O-5*MiJx93q`H9qnq=w}&Y-%ww}2LChA zXNkrS#e$;n-AE)LB?1~>vK+{6^Y1U(@(S%!U(j*F6GIH@x1vZfi$V2??nH*MX7_Rj zZ8wJq<#vuY1_G|M>qH@SS9&VnHcW$n`$-!&Q_>pHcp*1g4D;0cLi%QjV5Bdj&t(Oyu3b_s|3Z;EUntllK7LK2QA`PS$ z<4>&$klG0^%E7TA1CAuesqKy=1l?5w^-TouB;FC`MCZHpB~WF+ zvvReyO`m}+@c#2S(`tNN`CWY;aULP3=Hw)%X>Kg$F5<2G{tnAG zqAEt!L;QBX+SS!{F|~(xMNd0Osm`2@6(aNzkm7dry$`7%SSWngOkeWoGnFz%%Sn5d z&B>z6V|{pZ=4V4|#)GoqQ&D0;O4-@g< zU2ir`-jxRhDZnCjg;G1akva33^Y|?-$O`oalwVvlRqYo1nM4hq4r^k8zrhJKhAbcM0lmn{;I;E02}*PU9Jcv(EpU-9d3M9Pe}d+v~B} zZf^0*U~0>%V7kD?x{*LKl2+Y~z8|2JWOXi}3iql+1)x{Av>>~2KA&jAUR}GYRVma3 z;Jbtb;((*EVi&%?Z*+W8HzomxqnO4fJtL#`UH?}Gjk0JDN2jx8->nt^rvOTy?4RB8 z;FH7GzK&(Bsji^Tg8cISxMQ2z$UD?)rYV>ku{rTPCFXOMU0HzVuVhRpISv{Y8z1Xy z1w~yjD8~~m-zW5$RGAKe7N6KC!654kGNQ2-@nl>h*0`Eg|3&MpITog<(B+PTKKdnC*=tuMxov!MBQC_3@x>S~q} z-FqXXw#y|iU*D3Q5Vymn`8W?;lu$bjZw}uD0yj4|TC@D$T_L`=%Vhzd)7D%1_p;a48V?f{8$6o` zplxEstRyOyL4e?b;*j3|vaPJy0D<;9^C5>j^ue%ULM7(k{NqeGdS1(+(2X4k= z{JyZI7Xtzh{Ti4K+l*OYHv_o6sBfeDN$S&BL$*X7S0( zKqpJbf|BnCv;4?fxL(nEG3wOVEB_^3{z=!G6FH2((oEl_KvG_i$OnloR}#N$4>1wt z{4)YG_SRAUUYT;nPZqIQtRg6!P_yyN7K$O*zx_X10#s(pvP9)77xRCNn0T*)%oKV#NB4^#z_7{_qgi%?%V!QNTl1HaF+i(j}5=m~xb7GWG|-jXu(p zgtJ@2zlVMR+(=XNIT4s{YF5e=J3Ej_&*5RMbr_duC4EYU!ezietY|gul43UbMbOm* z+=n~vK|>un$C-n_;2{olzL%oCMC^O##O$g?c5ATiQGC4GAvL7*S70%m zSwY_igsi8qkkVXn4NwD42_sw6KfVk(A3$gJb9>oO1bP+4yabbn>7%Gu~ zX>5c+0nK!Sl<@ig+g|^lTZ*BbC;%P0uxq;`|1MpQGs`tM<@@(p8s8hjE#M@0U@0aV6o3Wpy-dyL^qV> z1^!mzzu_Lx=DSrRb|Q`{_&Hba;0bW z2f|-6hjaC@TwvZ)NM5iU>p%XDRmfkD!USvuKy!fcKRrbO4uyYPy2q9<96mkPXW%kW zo=;|qihz3jAf*dL4m1|X%=Wu7j0JS?MD(|{kW zR9Dh^XQeJr2H|Jj_I>&pQ#Xm!><|IWd*f^9UILy5!rz^2(BD6E;|a~pUwAN@nl^i( zS!jI!PCkFHBH%kf2DE z$~c$gMJ1*Lb0Ut*qZ|Gd`73T12sn#|c-+-m4m8ev<*EP@fuQU(ddLuDcR-ytDEkIX z3_#L=gMefmDF1FRXuD86g=GzKuKh#&_=KdKqyb{j_}Iv4b6*oN-JZUxjR|vQ1yddQ z8|@xsXJjD$4({}cc{Lef`Jmj=mQ#kbo<9O|h{b6HipEsMN>&yN_4K|sK{GF5HW z^_F7E7RpX@w)o#L;h|dv0-5=_I}#QK{m)(}6Erl+*_0V}o7+|#x^u;4#z!N0R_6~pr1a*xDuf49N|dMHNj$K!CPZ7nA7 z(YjBYma08RRv+42;o-A$J5C97i+WP0h1BCT6z|`DMuG`De8wLY&z&rzP5tV(4)O3= zfWbyFSsNTaahw>vQ4Xs*jT!WpNzNPW+;q7EX4Tt)`#Td$!3I13h4f4X9e0(!mm;yk z6#dVKIibp2qy(yTMUYd`&4{QQ7Ut1~dj~+$fCOwiM_MD}cK4>IOGX>kB*PA{JtrxF zcWnp7+=HmisZRE=l9c2rRz_Pz@L+T#!?OsHYdpd$Ek35Ubm5uiA@2#oP0d3x30Y-T z*=5<#evYsYDxt};v2W;78)}At19IQ?OPLhE?Uw4R4*03B3f-3EAHafv`L=71Mn`@M zAOvE(pFsw-C_!Kqqh626@=QT_z?rgB7Lv~?p$YmX}3P$e~#LP)4`v)OVGXk!J z#f!hiTL}Z=5a4Uh+CTf)9gV*I)r=MhSt$134~QV^jnMsLA}k3>aE@?S!syM_Z<>}C zK7r&wBanX=dS(5eV*70J=qqVrl3xT=l3aGsD+gmuxX8ac4>}@LvVX5V!vO8K@%Cui z`^vCK4SWPL;^@&+qpKS8=M(e(&z~*J5<$%oxYm%64)BEk?&18Q>+c?z;Cu1p3B+Kj zc2HSw(H4(z3Icg+)`57CCEh6hDPnKhD<=`zBc`Ps3z9=m&)tvoe?|y~4qoq4%PB=! zuLynEV)DAI)uBct7NW7L3ok0;D$zlLR7Rj-a^O-NRcvg*`NM~D{5MY{2$+B8NU2O& z;Tr_9Dd087gg;KFOd5dRU1R0ZlJ+^`Z3Njtp(*7$$BWO{n@a!9-T(c-Upn!uSDbk- zH#&E$IHL^FTe%k3JHW?D|E#`W;RYVXzx5EoNBB3{EfUx`RMHC1-H^$rKF$0w{= zY?iDAJg_0&Wo=7UOya$$|B2tfd;Om;O7l2SLNw>&ZS1G4Gc06Yy6D^LHa&xQmo|o< zo}4&~K0W3BDjN0#*{sU6|z`g_+Xq$oL=JW{@1>!u|1sjkoxBn*iE@UO&oHZ&q>q9~l@6%|0T>gJ(a z5Bk;Z1yK72UhMjuj%F_oa3b4`)72DfBS69jm=khx+-rcCMwVniAUV>~OSZ`WlbACY z$-lfGvJ8UP0bl#}gM({T^8xLM9&50!nqV4?HQlQ8#Fg#oDG9c<0qB$p{bHjcR`Vh( zxTzkf2LNwO+|o7ZLgnCFtu5X--3K{DNQl1gQ)^BdMmZjn@Vex%sL&Y(2I*e;a6o(c zZ@iQr{(2L=RO?idu>K~Ohf_)Djyw>8$2%N|x7pySD;&6|q%cfkJIF~>C>NGyDXH!r zsAtGZ#c0LMN11I{|9cGQ3hpg-nSBpS=rcJ-t40vOK&BRSCFMVBR&_KRg|^faK<#cwuurYUJkjlYqar2Ns!R7`5Fgz{$t~?`UD^f?Uy*r;aI=xc(qz%YCU| zYj|J>zW=_bJA!~zz}on2txOg_^Q{<|?^jG)cY&am3cOURW9jG!uP_>%uS>48#S26a zfu=svcgCq59F}aK1JMXT{~C*1A;&ASjeX*FvMGo%O$kjiLhs)G`3uo{EVf(@7;QPqh0*RrqC21jrQ z$Ixc$@8J(>(SN3t!XP%C7#nr;<-t~UiNtI2%zs_jU{JW{u`YauvpeZiBk3mz? zzyv^tAK`sv4=GANRxZ0$c|q z#Y`2$$l2&^tJ@ZaW#vT+CNmq2bQu0v)6J?csvtoSbp~|21nI{$-f-MeKraNmbESV9 z{uEc*yP1)1-gCv@pa#~TgBcS7RSQje$R4pwg6VSQzzk%{3MICleyCBLBqCnGUyAo# zT-2%x#D!V1!WTI}#~2_Wi9fkAxg`D0_s8aNj1=z{vXYO(@P#Ao#(tII$`xA9J+z-9 z$*CJ8UJ4#Bb4j#c3@4xu9{b6rl1Ks(Xc)3%3KnieeAt*vu<^I}rBqk(0~_b?!v?_4 z45yiPs}Ms?yjia=Hm1FKK$8qeo1c6 zqwql{P0pT$7-#LE_PKLk=1`0++2K#pM~%F)7vnF(98b*iwf4-D66f4s<;(e4tTYP@|+#H9nVi<(hZPB9^mZjH$6y%vWo1abtB3 zpd)mGMl94Ui)o8KYgdWdxe_p9#6>(&l7W3|EfK5uP1|#-sV3TzT3ULmrUE(h4Q>AB zab9KW>V9aWMCXATBXxkrzH|bb+cU|Q~&}H;> zg&PNeqF|I193kacC(I#@Xh~xFN~EnnxErngHM*}lqU}p^;1Ht>0@+_HAI7WgP?}bp z1wdet!9PeMqJuV;F6swbHq!Ve#X#V)RxeX{q|QI2@~zJ|mk(m-5yu2NHn%tBd35j$ zqCQ849j<#tVa2m#Q&KOm+-F6ava*5@iG;*#-|RHiae_g{ohFBB9I%EEiN9Z}1>PQ- zu3i4sfoe*8wCKglKWewxzCs)t@_j?#fkP?)9qN>STB>Xo{IP=|N>1?Xb#Kw2F#flc zq%Q3uUzt12BTg#Kr|z=5lltWMgpm04SLvy)CFyAC0#MD)EBU(@M2U`d5ANer{DblQ z@>=89VXvxJ;@?dB zbvFm#NX#SsgntH#T2Io;@)kxfH4nifpXn46}X4xqINH?RJxL`XT6e$%Pa{chK139DNcp$wTZ zX5B=R@7$kKC`i-IYtQ(5o~+^wx<+oLU^2onBXc4hd8Z^I)%B$J1^#Mx2uR&(dEh?t zaFhO{I?ygi2x-Sq$~kj28WESGH_2h7hDxBU77#bqcy%fBkUV2XrkD}ql9b$B=nK}` zBK@EMNR6kCMYe~V;ccB&)#$?nGlJk@iI(G$ztXa;k2EN`f@+#iOM)Ds9;|QCSC&51 z$(Y{_jNZC`U>=D*$83gF&hWTPR+IwbW3|aUOip-uSP3!HqtZD{h~G1t&VK>l-!!Y< zT=?$bneQ(?mC@DK5&gbM=@asCW&>Tn$8SOnm)g@$iUPOXr&YI_+oNd*=jD(|FpUG; zn5w~3t{tSbpSAPUJ$1)Mpt$}00S7+PN>7}o7no?%p@OhmWnC}(gQ*6j)4L|NkSeh4Kg)S1`1p!fiNvtPvI%G>|1f74ZIU=15gN-FW&f#! zE6Rs-7^N2BL~)JT8x)&s!bojsPADs_CyYQSJ23N3PRq2?LFbg+$|>2GPwA-jPD*^> z*B1=t)Z|2hEbcXheFOw!kpSR&+(06&e11{V(wd3Qulwqi9cTv@hRLz8JE@)GZDm(% z>z4FB?p-XB`|(_){*D%HUW{j>o7UiQM}o|FG}VFg4_SD8(VlQlBoiewX)`)P4+wL? zfI3sJOqP!dJ#G?2E~$-fu9x{}Ec^Y1jyQJFJZoqx>?(QPFC(tKliA-46-YEqT&H;L zdN0Kt_i!w}QdMetaocPaI>h9$f?&4ZEkGqWt>x-#r~kcGf^lV3`ZeZvy%Wr7>`N8$ zRjUpgHnYX2_LHcYbFsu0by2YWYWZTtZ&u~Ts7(*wn`h9QJ-m7?n&5@P+@07tFgxn^ z#1~OqhMtXz!CgZAm~DJ*Sx={yKVLyt4b<*kq*EoQlQ?z@8{`-SzM%N7>yGRd6(8`s5R#<*HnH~A|$I!E@g(8&+V85LSn@~X;Gf3fywmUw(z#f ze2s%%RWzHi#eE?8^!ytG^=09$(qE%XfAH&&BS%EiG z)Z6RMNmp9HhJUx>{=xZxhb{C|nWF--jsBtQ^Go#Nl#+3rgx9P|S>~Yl2gJVNSr+y~ zW4Q8v4;$t->*GAE8nRi?hsBkzvlVTs&1~n91<4am#I0f{;ySHLboA0E&{#Q_AjDKN z$)p$lCkuPm2)pQO#=&yjff3sQz4`KLr+618XIn)!R*B6MuDM`i4~0~(978$5N3K#u z84NYg8s!3<1jpr9B0j2b{r9tH?+;0ddA5>qIJ6nG3SQ6QGY&qUJVcc0Te%?+^N*i2$kPhk&`>`Wf}DpMHu(R^A8T+ zTAE=B`m~q}m*YKy^@j)9KN^I8Xm934STl^@o#~FgTobh`_NrT#iRDH#Zo%m*3V88gw=B-E~6Y6y|>P$lC~xT z+ql1Iyy;&Sb2;#zV_uFtFTv8^Ir@T@ax(f`wPq-kcxHQ6rbh+biJUGB7=4C?TbpH}p)l#z+17eJp#i%Mb2j?HzXTAI7)@3tIE$@%~ z{`j4NgS!uZHP6m7?Jf+#+6bps>F>8zDA%DKG5B@q*4J)YNYLCGwr0P1KbaX~<_AtF zWVUk8N(YfH|3OR5hR$gcW-Grg^_kLFtK8V@o1yctBeHTQSO`18eh~(zF5ios@y|aE z0&#&Vp5SuUJ8SqYk%2fIx_R#6p=^~VdhlV`+c=~#S~3BFV6gq9baqS{K~^MAl41 zpAL;xg+Xjuo7>v0sXwo>%*?X0$FRJ&sqD+F*|t8zyjm=il|sP9%fUj=#$MuQ@b%k3 z!D6QP_25s0BNDm_ zBmoalcjk(8RDOm5emP-;u9Q3Fv<9p>BcQ$FMp0MjCy2^Iz)mtTSBdgBv6xb8$#9Vi zyk%cBd?GrIJAx)2qm0Xbz?dR5j<)z)ja{4#mkMLlfUdj2XWm9PSs86G3MG$STp3rK zPc|J^esm?pCH_q=eFe+QsP~vd*iRt`&s0j7oe%nLZ1ANON>bFY+SQ(EJU8Ni|9}ta zWx?2T$M7F#T1WWMTw4`v^De7mn`m$_w=sOsr0K_cGmy&Bhq7s0n>*+Jrv861vJ!={D8_(RQ!@I$kRPd$?LC zBSoJ3v3%`zlZ&_7uofiKuc+~MixFR=y%rYP8QJ((=;fGPVtmmP!GAi_sv}r@ROK06)+;eBPkEWYFft zcvYn+^{)nBOwZ`0Rdd^l9wd-Bz=HUPn|`WGUXdqj#n&SrvfP5n_}S@^_ZmO^&4tH~ zz%xF@Q5v|D|8W6U(aGK%E@o4;&8z9XwL|nz!M-olR?RO=vA(_r>1S#In`V$B_YCZ_ zO{@ET9q7^kiNZ^I!($t<^nF>S)y$`_Rxgg{v1N5oI1>)iu3w7iFK@zEtF}){DzN`^ zfE-mBk;fea^74EVv&65qQ%TE8Hsk8u#Ty&|MplB;#bdK9eQ zdvB`yjorFS-&F>+Fo7dlqBCH9n_qf_2Sqnu4&$#AUSB?3zjwSN81vD$v%K41`AQoy zNF5v<8e%qz8}}@h^#vmvAOGItpZiO&h7|o7(>-rCd|in?*)D}_ec?(|k- z-u8)0*1WIN5ECNQtpBTjuMNS!Ij^(rQ=OE#bbbQR9FMP-PWy*0!ET~g)=gk#>ZILw zUn>O9r$2lBaQOY+?z2yPX5wLrvgClmL+00)W;F6NRn5=&+wKp*wPR-QYa}1&bn$m^ zFdz=Q1!tGfxG@9pKH%N#viii;5ks?y7g_|?$BduF-5wihi_mI}|7<9umhM^SycEGN z0FJQwyUW3Rq}KbJ?O|i-z``AUzoEzL-Qg@Bs^&%B{Ka72C64p!O+$QF*xY)n$Nc)-A^OIJD|$PP1_>_~7W*{PEqZIYZC<;mP#l&@5`&2=qhHc)-3m+*e-CXS11Vsi@OD|J4KYn4dpHKj zSM2sn1Zs}_>eY+=HD}Eg`+_t|2U0@KlP0VZmFzBjmUIt=8LU-xsx=y7?}I{NIV zR$z(A@6AKgiwg*7&qyx{P0Q z*)}uuNPIffs)8HRuITX4G{Glui7JkdUy?O@IvR4Sz4$Zg!-)At?sa$s zr#E}gcydkjhzcCUP?Rk=Nwnva^Svyug^!X|r@I%JZc_gIDxy2%T_qD1NOR+!X!hdy z+_!vFunj_Ilq)bHmfE`t?3*$7?8!Bd4aY!-ETq-ZDL}5eb)0uDhZbo^AzI3VpH8+E8N zhKm468M(Hrbq!UQ!G#>RXUwlHb}u6_z%7$qrbI+PI9kec4Hv|CQK(H-Pn^Ljn)-1+ z5R00lu#{Y67eCDD zvI?-iyiF_CX&rEVFBPH*{UZl7l{X=)$)`6b-% z?X9UJ9etKaRWv2WguuvzO?E8*nXK@8R6hM$6f0sEQ*o{q7|`czt47;_=w(Qe7zK4# zZqSAXms-4<+SIvq(gfPnX+jAhup1Ag8Z|R=?|f@r_ub=M>bdN3B=UPYIi^p^OdjpeN`%1lJ&D3WOFy}PhGoM+$Z6wClS0T z>hRh;dxe{nr=2fFZCo|;G+A$qRXY@EHTnq(RkCK@RVYSCpCec%GSh#{ta?8S_16m6 z0Q?Zc8Fzq{kB^b+)rcF9tVcpN*lMQNr+zY9u;Ed3)LT!{%{4ZE09#^ZEb5ulKX1b$ z_|R`tJf{7L_?HAjek>~+BBEJO%_Bo~-F1H=@BPo?w^xL@LG){ZpeIlKwcy4VsZ-Po zn;5sHuFF;DP=NhHt{C`+d%dR`-+zm3C#5sqUk{MWQen_YL~2q3q=(jL_$)IfL@xYM&T@nSkVQK^X;zoNW&f ze&}yXsyYW}<_*m}k3<8VzIt#Nd;k|3YMdgIOsrqN|Rp5@UlDZegRn^SMyX1xZ3l;a8-tg-B-2q4G@m=|$n5uAeEqk9} z+CGy|DNUhIUooUJsS|ybr(;pY|~dyk`m&o>^R(#wLHa$PGG)jjDwJEvbrUOWO16I{jq8s3p7emDZOs^DS(kG$_w=$lEfywwnSnwyX zwd2mMlAIGeHccqi2re`V{_~O{J)VDjQxA;;yF)0CW{F)`-pB86-WmRZVTR>-uKTKU?sGE65H=8>*h%87 zIQM%!=WF{sp}NkR(auRi#+|qWr&2B2)bc~+lGNbaCIjhKr}IfGc@A5%9~_l^GuCyA zqp$Ikcjl*aGwaWE>YBH6`=My}aE(|hf;rhTj5Wo>6B=!AH&_tXXmdQnp)zr3d8FJ` z*hS*3c{xaF&Da9hx@vzP3512yCjl-0nF%llgnxfA@Z9QQ7bmdOeY(hNVwMTXY01Ae z(6!Ai=I-OXs-;`|CLL#(LmJkt1=MkHU??Mm9A443_tyyBW8B(j+NbeD3K1Ure;}Yy zs}-4wuOs(C8^!N2L%QC^i!BN=Jt(a* zqnsO9sP{?Dej~ZWgiBv$$k^`?fL+(bmSz$>{5n=+c|})gU0lpE0~_?l-Tzw3&hbdM zjqH$l7Tm!NJ&J#TJfAOA0(_U7EMK0|Y%#EF(OJ^xKTrO)z&4#>@g&@=uE7|s*&M*0 zfI0{e+F5?>RDBst0V=syDJEYXLQ}tZeC}#heazKnsE(J#v==O9N4wm7vvcl4z3|xu zBfMe)Y!Z~`+4@gGP-)$tjDRoX?dmJP_tA_07%~}5{1#JSNeJy^LhPc?Y%oluid6OV z@8iMO-hG#bt&l| zM%|hHUR!R%Hwx%H2~0)}A$||Nzgnv@lhoQ z%ZcHl?c%T6X>1$Vs$Acult;}h6!rHyH_sP_93Qum-m&MVMpyl6g`p|d+MlhyHLY1= zH~W))l2rXM$Md~|LmIHT_nSLvt8KUsE(LZC;B`KF>tEU@!d8xtEu9sRCQG?ag>O90 zK-{xxaNnh%>x3cv|7D;lA(2W+;9ybN6PXo@d#r zR2DI2Q@4Lse9PlhHFD9p${xYc5Kf&5?~{1- zP>1PGSZuD@QrS@5ON>jO$t%FKAUX9EmVi?%bIi09vK{Et3zLZuXE2-2RW%EDPd}?& z@$n2ZTCFy14j^6^`~zNlK<81m;nOsVV^S5V`cf;(!{y)mKFhM5MP&-9Fb}0+xj?}V zZfoY^n@*!d(QM_M$2Br+zPko1b+#`%FFTn-u$ zA^VxmGqJpv&JFm7*{@w~74<*v9J%yrC+d!l@NPNuEG8uf4lCVcjR+PfrLlkG4ptSk~Wx?q}JFMyxa}4v}??7{Eq*xKQQyp8FNvx);}z1{hV|%W+2)r z^vFt}OM5od^pKx^9&^azli!$%1bTOLd=kV9I0vZV9}_?lP6p;_$#49za?bII2GQ*M z)*2%_7M0WWtvvVpj3aT09|i12qb2Hp>b4r#dn;Lym{`Q_Hi-Aj;E#L=s&V>T;*Vab zo9H~ABn3+1X1Z5u{_dXBQa4%CsrtMSm$cZueD|DD!~x&|-Ly2d@P}-H8if{mMRkJA z=ki|#)Ze7$3^w)jb+5M9@YiNLE7hx>TlluBX_yTYo!i$6Sh-~gMp*>~rmX9xKLT;x z%)*-V9|0HGc%!hrzVcCT#c=n_qlq->8x+PW{$`0`M6q@geJ1W_A&TZN{7j@`n{;Ls zpC$wpsxHN@{nz*L$_bkVh8>U8$A<9=5dR_+392X;rfn}yf9T!0 z^(1;2UBZZ%g-I0U2DvaYkq?)#Nf6s=Yw z-V~C0M3a{k=|_8;H>N0{-^39Az}B;AGOCgY5EH$ykLFLF88AN4XWlR=(L$WBbOWK6 z$xa?({#~x!X3ei$3d+ngXRlGBrDj7cx1pKDv)Y6of>ILj;)t`~C`lvtHjtbuPZ@N0 zU52RCQ%-67n3!H1&r<}#>piFuPcFM8WsF$={briki%nU_WG~ZZ(m1`PL0HS~5?IOf zfM(Bw@qHo226>%jskr62Hic}8&)+oGR#Fytc*pK~OR<6}>I&o!#>|@G9BJmhH~lI+ zwMd|y53n=lXhr$3*qZXrS>Tl`BED6%Dp7q6L5&j>pt#-p{(Wnk3>giVr0l@>{3|b7 z-5tFE>0HIV3FIlpYpNfL3G!p`NU?KM#gtWQMIo3Yv~WM!_XZ6_-pOOAWn9urP(DN( z_!o4WQ^iPx09?!0Pzr8tZnH+$Y$Aqtdy&d$Ev;Ttmj{ZSciskWdke2BGw4e1IKuTux^`4}P%9%_NU1p8N zi2h|^5uG|1L--{!*W6KclvN*Cr!lBv)0-Yc<6&hj2yLLR@3;gO2ml~ZQhfmIU%`cP z>FKT!aPfDT@^8aFvC$j;j<2<(=Sj)&#};-D?#CW?mjF}B^G&M0D|_oaUR!s#XR<}n zV)F+oN`}14j$LBXfh0POwDNToLfNS>#Rh$E=vOGBzYCBifOGDH6={9(nIzKDD&UFr6OU+pi3LJ}($zm&BJozwoje7HA)RNJ^U9Y1+MBxiTH@ z#>&l1!U3z=bzEMr^zxSW8g!5R=mJ%lYtJISwFeyTjE|Y-u3B%8a;zC|OI7{YUM7F# zxf^AO!pO(NP01|gP}6r`Ss4YkDEs&djQIvHCF`p#)3Za<=CnNaaTNR0l@jG(;D@ZZ znIoI1r!>0;r_A+e9wURQ(XKF`xt}1Qy6!{hmeh*!Y22-4PI6<7c4CP8>pGY8deJs2 zjok!diR@FnoU=yk&)ujrQ(!j@h!LZW%7-1P_`VdP$_#$` zG6^%dyXZsk@i0j`VfWNb^c&Id?So_W#he;*)}+kHEX%!vb4S+CjSV;PRv%^^8+h+t zf`D`cvWE}M37p~3HSU>{fpl28>ZTL_)pxQkuM_BrIoJ;rQ~1pzt8CbE>P^O-$oq0T~_&x_E{mfqXqb8>u?(BXJS7+4s-)1H+m{ zbO|*?Q!nDOGB*N61_}}2pq^sy*(^O(JFlb*!;FErN?KY596{MB*3XL8yuUe}zB8LT z#hgp*Y;2J0n{w)x>_r}gEP>s!RG+8UmZ%DPU9NK&)-+G)%bK$6jS)qaS7m87|X|rr9Yh5I#Z|vyI>n=L7Qy*DS<#1UC7C(wy-NV7QQI;_ubs92Q0rh!~xhYM&eWSon- z_$-sMBPmb~aqJ*bc)Z zUV!cdJlz}{M%}x(TyB<9eakr6U9@CIpC-QGksaUUwNPd#$3PyE&CNyFY2 zRr!peqLH9-w-uVRPCWKS8jRpt0XQeAgeByPuyckfHH)$jL9i=*3&V0G#<}9f5uqIElcxk4|c7AwxiAm30 z%+76!*dG`MQ#lh(|GE4dJB((HfmnvKP!_xbfjn-OpJ4Pl>Pq?53NmOq-ikcUd2FwG z(M?%&m&{-IN!vD?(55f|GbLza+Y{{UP~*Ygmk&ScgQ9&N9(QLP;KHVnm25FV@r2%O z72vdLb;W2;sOXYZ^bS?CiHpc?t048=Uax0VKADSp)J}F7SnAjZpNOu`pRLfm8CX6U zWzPp_JY=zteNaqyc;!@me~MJqW(IS#l@kFS`kO6D{bN2WE9l%iyA z^=}O*|pzrG#Gf01ud=cv_@d|%kqU1uk^-S7;x6W8j ztMEomK??nUbr;dt+IO^ps}P~f#U3d}Xa-4ctrdcgt>-h2$btL>qd2rqyjA_I50R3k zv&X8@Ts)VthTi}$^&J+qm#^u|kRTb&{cIw_=hH~bR?&eL=44}O&x2qU4wUDU@Ow$h zUeKSi6Qxvtk`sJ;(Huim+io{?;)8T_X;vI*R`><7GHv>AlkG27 zk{iSiwjAV#R9QC3)QWleXm>r-!CUvlJ})||Y6iwok#barNk-(}D&G$!17^H$jz>Yi z*GR|Vc+oQt;|TRnS#giI_H$(?6An*LP#nqhF$rSgXA3)q!FH#+Ei9+!M9$rIQ3mz1 z=t1VwTT$hYhu-Jzuj|ty-q=$*R`cA0A&j=wj=^+qcLt9W)p1zappz%QdHY}rBhbc? zFzJ~ZI?&btp}XkNfim?vnQ-FKPY0=l6SQUkda+ z-cD}jr`&D=g}Mi)v+>*r4|WNVyP*eS<#~7X0d|iht59M*vTF3DcewR5PJ+Qqu(`Z* zVH19jU7guP%rT#I0yy-%+XK2ZR|Wg?{L?LC>7*h4R)u+>q@-w{I5sNWroUPuxtn|W z=W&qP49uH%+^u=i*5mTU5%p;rDH*^Vv*K9RPfPF+Fu}^ouNGYL!En5KUbj+zyK2Y$ zh&{#zb<_UR&Q7~OK%Jg_!_hgH+)n#ds^fByv@!BE=TcK57(2?_>3J)CR$VeRYrBgD z3ADoVR}<4OySUL#N&!`cn4a~7aTe(hgcGcF(l&O|^i*nu`D}kw5`_tA&E?_$#!fnm z_f^ZI>IG;|KJiVw;gYPqyk@n)|vz~cjvB~VD?BL zIqCCDlH>?v-b0+nod!f6@}G(Dhsr8+-MgNVJ&PjiD*XQ5_`;=+UJP6_lZ`tZG-q&mH4q{hc}Ua-zn=j-GeDp~1BXU+;^0QISaX zPkY`z-bX0Gt~lm{T8=pXIw|Tox9*Svn};Rfz=1~zsBz9I2DN|sP!t~bQyu2fyY9Oz zrxa>ts7NA9{2DiObEqM;r13~u^Z7o12f*X<9Kbat>ARc9%xl1?Q{QzwsrFIH*@p{- zO5?Qc9;1*ZiB|x&LD#*W7Ef3dXi7kj@ zc`L7rbFN+37}9=g$qfgp30&_JkKMQMwG($=@yTEFP6;O!?^A(R6WQ-6ANi8{t-QP( zItcWw_q&a)iq7xH*ku@9-be}=dLVR9b1}PHpS<1*qZ}DJHo_nj zs$ZliUwXifMJqyQ$PdI)Z@5nbzbsY{KtKNHep9HT73-g~(TvCMW!tD==R`s!wt)5q zBoq@T2H(6_2>9wN8jL}7wgv1|Nifzf=Q0$ZX_wd_)58+2#d8{vqOWcS8gN3HgRQ=A zW9tbLt&;%7;2#E)=R~~dXA$T52YlrHsA)CXTaw!bEN=Oz*ttyfM@dnqJEBJDocD;` zs%Z0^zx}GITDv(H3@X6)D1``U>r zIjV@_DUBB!$Gp?HUx(Lqi&8Jnx(X}9Q{#w>IC%%Gyn3v+vA=m5gpZpw7775vV>kLP zgwV3-A#BC0uzx)DqiBVq;jgXb)+uSRywzP)6uh`u@o`$Z?P{bC>e7{VXtUx8YCwQOCRANI@2zShq+B zNua%RC3bG=f^e(NgxHkIGr3r%kBg{ymlspBsFvJxukCaCr2dkFve*U2r9)q`hZj0&`<%y9_n#9r=}f=9&hdFpT3VW`IDU%;e2A{nxq4aP+j|(F zp~Y>BFSr7ryaKSrVdo0z>$dn>-9*Ry#^pdhnX<)yRO|0HU`(Wc_e20>EBDjT!!P~x z;!E$-YatukpAfmMHk3j1iC$sQcdyJxxTyAcc1tDK!p!^8TGMK1V}y!GXb)MVcu~npy33)-y>TpWUeM z$NNGrpO9d|bgQlE@*I%`)YV6hzoY&GVZ`B&aOfG;2;OOvf^^siG^Gr@E&s%*hg|4w%@ci zAhxHH9AuY=KU;3Q`xsm^b}WF{hO`M9ZPxt6MmfsVSH1!27>(?HJ97=MNlq_UKR{P( zUr%>87a4h^VWyV{@HxR&erR+EGpBC`d`>?bA3}82((f+(e;NObIC*k zED{t(867NV`D(oY>c+4N#@(Vry^2n;(Fu#Mb^4p~fFw!&U?TQFNXwbmUe!N#>HEg@ za7FH_47RjF`AuxonFxKf!8j$_;elv!`y}fH^-Rt;(Y+DjM-|MO>@G~{SRT3&&8rs>y zS`L@Ab34P(1Ni|(XeD?xTbnp)n@Ze zUzWRICgM-H?LfluSx-;nX5t#sS1+yIQ^&iu45!7rhUy;XtzF>>4x6=AZOy;LGht!s z>o?rj2PWxT6so6AT3^qdy#<$1Aj zNoOdHUu~dIBQ9)GtM)E!Ut~*krw&+!=_cddxYZsoG5D^8^Nm4^qFMv z*6eF)JQ1v`J<*2AIqKBmlOoGu-U>#{V(bJ^7X8ZAqI>?r&Zr+g7@PC*6EHNcH z+&_qNLyG59Q^w{6GFPt7cl_?fr`P!PdBt!;zuELYMlcGnxsL(3(w(dJSU7SFZM&i> z-DLAmbbi>&kxHad-V$KC!lA=GqAQ-!xchk6!?%~WJ`EnbcnRFY@ej1(SUQF_ z;HQ@>n3c~i1Na8#fep#n87CRteK4FwimJE?BKXSd`JOta&PS>2yxk}l$EGv|pr~@^ zeqqV=<`U-8v0ZSL(blj_aq{TV<|K~==S!9Von$O~-;SAEbjKA9(#zh$jKa6CzhL7Q zn7tY&CdL2-554sYbsis=`>dP)1y%HkkMlZ$odTft1Nabm@tP#%!z$00m3Ax-jh`^q zM(?vP%>fE;mBsD{rIa?Omtnv$_cU^m_NWzq_a6&a)}lF86gNS6TGw9-`AARya&@!v z$;Bh*hBmG3AK#vUg?eUY@@Es8+6V|lcd-(FERlZ?r_&Vn+d{4#J9+mXwN>2;FNY;i z&qQ~FF3$;@yqt~Ye(Tt|p3u$N25-QjWL-ud)GB7iz zRq6#QZzy_PEnB^{Z69kV&R*ORRUyjPG<-=OW2%BzC-f81*&hmdH4TB1tErNjg08zB z_Jdg9!9#*yx>{`k}y?5kH%q+k5A&ys{~QS(QDocIYxFon1V-Y0yfq zZ(FQkS7iM%Xm`1KNWQ8vAKYpvG!Y$|FiMpwE&Lu&mV|y<-sgGZe#S4rATTcf#?)5w z2M$OEf0UoU1>TIQc;j_f5a$|op(ukn5($niB9>Qviw+_D_x4yx89x72lu%YGtc`w8 zP7#3v+Hn6}Jp-{jC&4AxuPe6%ur3-|eO9iW-BPSUvz_|m*xN)rU`*a~R~wEH?S82i zLtF0R_9LOEI}+g~RuyHu`m-ynK@H(jS6sEGs$utGJz*Xb^>mLPe|bDJzgP?$nynoa ztV#F??DWBeMO3mmNXD4qVJW<%DQ(&Vfv2$Am8Z(U@2Hlb*DAvTZ4|p9yRWRG*a4LJ z1?^y)G_zvU(fA#j3c`Nf?`|&Ply<_FH=ne>#Z~H{-RKKSFON#j!$6YqAOcvVp$ww~ z^CgIhhQWS2WbYF=rW$Ai^bv}yHEAs?LpUGE0CX)N^CWVZ%=}HYPo0%lhSbNj#HSM<=_|(`lu4%@*T|GI#Z3*}>wy zDNsTWKKOMZV}8i=FA{{15r6>Nkt^|C&{ulgq1D1-0oC>1&wvNYJ^CjCYRE&%oS7q# zx7rG&08oQo#{=VA9A=Y z2pItk_9rOY(xP974gB^s?BsSvnI@eLao&&88pYIPX>bu^!u%5a(Hgy28nTL3)+HrR$rTUv7S2sA!2d8q#O*m;a0A_aVM{fk60jy5(Diy9O1^5KR$AC|r`HZ`oB}%QFB!5t*Heq?r?FE`J8d z^D;@SmX622E&%q9VKrXLPvE?)vhaoL0IoMnr`eDuM`$k6i>&Hif&J9&M5>2i*P;)T z>;(I7Iqqq+NS^C}BJfXB!8sf4H@w~b!D!9))fEQHI?QX(yc>V=A8i1ItEMYf1B_3R zE)FTM(Z;rrHS*5QliT;uDoY!kwtBg33=ZzWw7>9BxF^-;C=K16f( zGYXXtDyRrq(qF$U<6e^T6bDf3_r5hCisMS-_-{c4MNERlpZ{2uhS{Xq&mTH8GZ{u z2Zwywp?1qMRe6d|a)M^$rT_#g>?agYPS-~@q>r;hb_uTOa7-xpJRq6#n@RAbcrmTe z!?T4Icl1K6vy+%Wb;UqH$95u_NCppUmvK1sk`(*36n2E0os5T})=GzAdf(0kxXRzq zhj-5Oo%Ymds=UesW0^}`*P#LqXkm=!XKM#;4ge~G3=*_C@8(qS+j~h9Cz2-xP`g46 zNTmH_a@3jL0wyuZpnUQ|)qj+NKoujOm7`cU2QE}m-Thm!&Tm6)_G*9s7CXRYq51Nr zdVZhd^YoioC{(4DFBTJqh>6;#ozC4U#DRqukeaNltIruH=Z|bY1iev_MG-6M(R%+@ zKI8{Oe7Hkp*VjxCnkGN%@ zL;TI(AUv&qYjwF!4)aw!|E_4g<@qLcNP{%S$J3fBPS$qNo~rT|C)jH)%eC+3`ds6l zlUa;(ukKJ4xH1gQ$}ovNw=rwOHIYv^eMkXH#ofdJz$=%efEP-ZD1aB;fs0xXu|_-{ z5Krf0VB7`1WuEBh8`mUhm;C8NUO3#B8^^3F+vpmDpM9j9zop=Day^w>`6|`_POh{i z1b3sKzupm*pNy`FAhq$kGrG4!W#t()JvnzAH4cR5%S>Zk>Fd>}dMrvg`^?oElQ1}a zcO624h{yS$7tGjFSe9mUIhoH}>X<=rDdlM|XvbODYMu6U+MaofDc8b%`j0N4X#ecg z_fHEG;EUxtDcJmkFyH!Vd~ShvA+uO+0@QDXc`a^&ey5H%nF{k6VMhrqf?*tbu`KMW zi`#n0mB+9gqM}j$HFrx_Sba>#W!#e{nGrrfA_4*WG_s110@T)j{!F}B^(fyzXKbeV zi#L7X9)Kvb)P%3L~N7&OXwo9=8q{Cys-k7}Jlh zpEB@;{RqCIIq6d;P9PbLUC6I_&G)xTq5u+6gVjSg-)OhLOg~k=qLD4dE;}n5n!gJw z_ecRyN?`rv%|l>5RX=BdwCZd8Pn!O~u*T(B-NEzm{*s7ubaC3C0FJ?jHSnP>F6Gi7HO+DvM(jtO5zl{IYW^lFpu!Wof6I=;K<1 zb26e(Co&SKL@N-scTdKap!=_PiQ||I%F=Oy!0o9ro}=(e=;PC=%3w#&8D9&PZYsd5 z1<*ZYP-*(}UA;7f*rtx>et2vAxe!yg#~Q=;u!9FrR-ga=Z`k&2MvpMY6IHg>%Nc`vkAHDRX(CH#*UWnW#64R!c5Tja zd>u`;ZWrO$xi2Ze^R*zh>r2}o61$Ogy=uyDgX`+OS?Dnp(TxNGTh@F~3<$BRigMK);Mk|G;+(gB>!LO;Zn2IHpGa(>pmzs4^7=v(!k` z2z}V71H`xOz4kgj$a{noglB)^L`f8X+6Po21dgeBH&w>WJF`(KaKD1}0C6(Rade-$ zZut4j(F`_JGU7cf2-Ep}%dmDZye|s#lg06OJ;Ccz#(1PZpDSNr7`B9I$L;j!%Voji}e=b?zlQ18{S3*e@T z^enaN>^4IP@z{3M=dHQD{!@z1kpFo^4cl63xr4#U&cp{$qxIoD_2(KtI3{?88|iD6 z`A`R*+J(&0F3i>KW3*%@Z-O@tHtU9)EG3%@S z2izo|4edThZEZ8jxF$0lq+me;d|(6X9ICQKv|WJTDdmXTZf@RW_1V=sB|7qX+DE^R z;Qj68{TnRj6*XEm*sPQgqpkfpap*jvMRzHXf?~;WN+A8dZ}Ty>I3D~r9qzpNhX=_6 zDqTgoM(26FqTisqxNW%3Mc`Fyy8~_SaKrT&Uq+bff-MMIvl^s%W_8*U-;55E*j%HWW0FP&XtxG)4RA}%Ujd&M@zu&0=}+IL`TC| z5qr{G!i%@PBd5GZjuXID;YDJ7ysH_-aqmox z6#6`CLyxmVVcANfy3H}tIg(!lG^&Y3Z;yN{zRwXf>MdS(2d+D*z51K*I#D3^F^G+) zK}grTg=-SL$39>sRNL5hAP4LG#+gae{5g?J0WEpLxuQB__BpnJznV>RQ_JWnIR>qs zCqArQrOvBUg2arV9aeEWb|*P*w=@Xk2EfG%k6R_&tCJT#iu0{i54dn2?_=+FOPa7B z^w2YU$~z6?TN!8#qu_k8dSRD_@!eo_+s+Q(lUbCXr^`+!;F@>2#wR^rQQ_)5?L`i; zl}z06C0+eCSp{pnuws4{Zo8+|z-2f-^RyS>bHv0m;?ebPTz#0&`3E4P(c&Zy`xdcAt_8)oWF!9#&1Q!yTgGVgPox3&bD_o6Wk+ZnZEt<-@79xXzJySbqCdIFgzHFK{ndYnMCKIwDgMP*%!g6uFy%3Fp z-#DOG2L3LgFOMrXfzWtY^T$9Gm&JY$)5+N?RI5X?1pg=;>FeC!*U5N z7#%>7Z#F^f?S<>w5yF$UJ@QqVISe%X{wjWTB_x=TQ=d4tFSviqEKUW#g-p$^d7S2> zlVoc4USD+GCk~=0f!v`7!GKi`l2F!PyC%D~#@yiCp6^A9I=Q~;>CaF&n$=*wnH$P{ z|2EDzMyloNXMNq9g)?lA5)ozw7RLf2O%w$0&15Ut{%dp@w%w=`U(!~}s50B}mAipI zaFWRO!U(Eznh3zUIr*G3?|E3WgJqDShWCrDCXYdO`#_|1@q~K2)7G^Yd_3YhT!CKR z*Lz$RKlgUbw^skt?L9PoJYgKE{~D5K3xlnU=e=PA{W6~n_!F&&sddD_y1t$_H(&pV zH~$A;pRdkQ|4g~2g%>^nsAKCZch!uzI<1`?~kdSS^HGCY}sSKqAn$R&vqN${e$Z7m{M`PwK`HJQY=i&@f#Zrp2ibNz#HHWL< zs9px1Bsab}vXa>rw7Dbs7PXMh7?Wne9d8FHW9x!Y><82Lr^L*a=M2*#eVi$yRg8BB zinz}*Upwioqgg)Wt16d*U;H`UWHsc2(U3a^ssgT|uRdb7U8ubVCG@O76=An=L*X|^ zWjJS2$CqpggVZ0D=Jx-mPG`zhn@v~mW9pwS1zWAd9K3?nK`LDm`J3&;?+KeZOn_%A zQ|vvwk{LHTT56Nn`K^&lhS2tqHKeRSHSc_wpNG!5Ch0xI*%Q0D@hNw@l=4+Xf!{ZR zBdNUazvunV3n5av+o-nfHD+pC_<($Io1%BSv|Go?l$E3)trD&vPUA1OqS?~KQ_l`1 z-5M^>u9_cryK^}7s}C#s#o7S|q9Fy|lbwq3=0k>jU+mCQ)eFk+5r3EL#-CvZBf2(x z7KGyv_oHLY?rQ0Gt_^y~6h+D9&}d@x{sQ2j4t zezNMzYvLWhe+6;@>`SHSD-nlV*M*^D{O>yh%2K$dA-C<0IZR!{lTR5%&2L8^<8J0d z{suVZ{ZXpc%$R>f`0eoq+Iz}#01X~@cFOV3wtce>iJIJuS}tak|eO}FpyE7Pi3U-cXg z_=8OK@DZ5?`HT~rY#h7X*n zhv5;$^JBsD%X^CBeeb)?d$3&E;Uzz#+8mg(4Ho_|E2tTFVKhbZw-!P1aE=v-y2diT zZtCNqIJ43UTfLq7_5AiO0`nV;6An8Y5^x7nkI*l++WK-3#gqs;FLFI#GnA}`~G`OZMWqT&ZsBG?42zY zxL$i+2%FUdzXWT*}FDf z5li{K=Yxcf2H4s+#n20s^nowoqwnXu@SDq~-omgCu|q1;B`olko8qb2-*~7zu?XP# z0TT#YJ&Tdz*k)PE%5BDi`Zy9^OkyrP_7d~8q_U7a27atoay8H4c)BJEj>KvOr!Mq} zHBJEhZn95^-2#0tF z9Z!rL!iMVAyc{uRkJPfqx?~N{J__KJzO@9|*E^{^TIj?(kx_Hi_bsPhG>?R{_>SMTns7Uc`P4S6 z^zFWzOEk&L$c6n(e+g>|(1P%9{(u}_z{OqiG5goBj^D?t#Y&ozYq-t!(;Q5P*qP4m zAYvZxBFmmyYcPRq$|Yd1BTJU?@r>UxVX4Ssd)mRtURlbRR#ewiULj6h;O(bD$th0PL_S+V$)+*W)#)kOi8Fq=+q8pK|(6w_NZ%D?>s@j zoY5d|C!r8q_;=5N`??akVON1p;9%cICV12G>$`@O#Vj@f6PJijpg|!hB`1b%YM>;8 z-JVZT<)+E+y_Ko&?P%Ox4nOR4oWH?fxJIQnLu$-far>XSGY*ricuDt9z0T z+VDQ4h@GW3t`h`otV`&MsAY8~&I50BxMY89}A7w1_YlRYTUK%pv* zDLC+Usdo7syxpwNSgxPh09bu;Q+c=iUXZczfUXki3}BI#3crDXG<-M8fdSK0()Egqvr1J2FB6#4vlDFnp9nWQ+BCHbMGW*cs4VPV zU8r0ezgij>h5JcY+cpq>;w?cBgY7T=tgZoKyEFNDyTGh5eg-j8zWJHY?8eH5b@K7z zYymsW=*-)EAJQqM!M_ACPjaHG$Me|WjwGvnA*E@MJ;VB-1YiFi^#(VKs29UWR|HEo zRz6KyEKHrEV$suN9Icx;^CKHRCL9H6R2{XwRPO(9SN z547KZhbh_EGi3})B4I`Y0aLV(%UL1G*GJ~RZb~GDpO5KQp_D}O${2?5{w_gbG@5ug9?!CBb2SS7T6oTzpf0d|CP47ndy54kg4G0@r7&6yV21d{_<%w`nui) z_dr|EIb=7mY?RUH3kx&CdF}Kb^m&byGXKky<1RMKL$1jVJ!+d@w)FFyxSG3s(C49dMEo$^zvEn(}^SB(1|L+tO5ttTTblTF#K3Li*O-8Y%c{hN~PII}6R7z^d|?{XzS zv<@rKtk&v!Uvfl?`Ed~F*8isaLlhU|G!k?)^>sD%r>;bcKZ3TQ#_!WUC z*K=IdNUV1o0@(Rwrax_JY$of+JX4AjZM7TPVrG6%pLLcVN$2|mY&taspWyKIYIY~Z za=ll;qO@M_L`b=AEU%Q+awe!{2$lkL%gqfnI@+2=Uj_8sImYPrfO1slNetSEk5;5P z{n%QAQPJ8K81Ua09ntf!79-(KO%Z5A^^0zZGy<;CbqIW7P*xPngybGgn;sHh4kF`e z6&UP!l;f02Xj;Fei@w;UcKEdMUR+P?cz#~k`WYBkGI%fMLPgJs#{_{x{5yT%OvdGQ zFsElHbQpMI5l+6=(A8Nj>aZiqE@UgN>KE+w2x~m)I_TNTgjW!7{c*foTe}uaMDK2< zeH&T0oE}rrUc%m&8HK~k{mWN-X?<|BM6kxVzGUq7Iw0>0?1@+8{2sQ|*K-B7MR#?u zmL&Z$vyIA(i~$HxXRk3VwXXw1->ZA$m{j4x1;27-VxdUAw>3@Fg1T8oK>;ocP!m>_UcH3(hX&>B8#QyeB_uJu4 zVifCa!tEpPBiHtJT8j6TrdgHS$r0W&{Qjmfk#?%A_HBsy`w_4-d88u7m2a?S;@iv+PSi)ASNK4b+vN-rW?S-Y@6?%d}_V!)T8h8u70eU zkoX$SkJBhc6CVrT6|bXS;Wl2iSy!I{5B5YxScWl>)Ql`oZZBVV#vCI=Fo8K{mB7UJ zrfaR*zuib$^A)IL@uw+68XKsI-y0U-_a4L0iG@zE`7U@{f787}P8;=4>r(5lRBya@ zlR3eusyVJs_KT4HbvE&QunEKBrCfS;d_W=Yuu9<1Wrj=?8ZsZ-0kUy)kG!0Yp#gmC zZywX*Hx3z-z`BCwzlu*OnvmtYxlGR|Y=O#1Z>^*%k!dy)C`rfUF)y?I45!Mv7yQ1E zw+7>6fJcZU)IzyT@*~Z}>QQUy?!H3JP)v`01BEHR!pn$BA&2#3>M`P>W?yR;6qv4soFOWpP=ht!!1cjy5Vtk0q)h-H-T} z>O8>)-fYl69ZIFj0E@Vm4^}kE=8U$IqsYt*TCCT#FI}pmMrsd! z(jnd5CEeZmJ6Av7zuuX5M(@C#!@Xzkv-XN-J!`E5@wML52N2Q*RzF|EI~=Y5+1Vpf zAj^h#i3F|f5n?IFh==u-3!za{)r#48vK4W|tB&S9ROvc@sa7g8RZ)TY*d*-czMm2O z4;x(XN;5VHDeHTwJ7G!{3e0pS4{h#;ITTWqOG_d~qiEw-Tc|UJmfuOqP1bZ6P^DKJ zrejKsX@(t@aII{01ElaH(-;i0g7YJclUjfFb&ofQ6ncrn7`l2p5kd3hs?v}2VVXL) zc|_)5wEKKCt{Dt>)XLb@*WCo5rVdJ&LnS+;kG<5kp1^<)gmVm8nWT3=9OB}OXB*!x zO?7;FE#uH|Eku!dNrDI@STIezm|KkfVh%ccKJLSM_nHa}X&D73j~cC9(h>5~Gjr{J zts`+zZB)>uxBK~(MTSh-vBo1?`IY_qu~(V?-FNeWSVH#ONN0;q71ljn@Nb!Z8tTlm z`8czN=FnOTDkvVmWdb81NhFL`!u>YbycXO8oNtdB-Wyz06N)Eoazk!eyI0;En(76_ttkcbbW4dlT~#uyV*W4H6o5+ z8_l@E)gYU@?UrJ_Jr-95rQFw!p4^ca1T#sYt>A2O^W6SCaP{2k3d}xY@cyRZtw=1^ydv2^klBgu8-}?97 zViFovewe%MZ|(+nkx*B0Kqb7_+)5sc3q75@MSDY>uvxcZ`$Ho2Pdd|m3APNLPmnZP zk{3Nz!+Y2*ZSSm3CKGD!)EBB2R^E|*arGb~?AibGsL8(^jhryl_FE2rp_PTpiIefy zi9vZLqIWVIns#bPFYVa<#|!%yFLE2@+0X%g@#JNu9E#t;&+1N&#mA$_o4aOH%`2ou zb_Z)RT;MndSzKsgSC!g}=l$A;sh#v&`bR5g1cCN^TGIUoZ5O#h+}q1a!I2=0 z{@yc>RPv=8$H6v0cI@5qBQt3UKRaRfP{ZscG)9Dk7GKDg zBxL+pOzIQ1ulR`wba2e0Qc#zU9XBcO9-DpGF>M87MAe=bZ6iHFxY&m%2g|aU&1{@+ z08How-ehO!`}N4o>)LJO3p1a(^`18!K0Ge~qK!hl2UZ1sIW6h~7qyh0tYd4}wnReC}Q0&p4M z8pj6AQymFiNAxVoUzpJxEyk=YMK||S&yuh5%e*MCp8j?@eE!LhKQ3vxmegcnXgB}E zmkN%7Q;C&)0meHwas`Vor$4m?)b8-l4KGM6c4-O}FxS2HT#XQ{)}3Y()#uK{6XB2e~l*HQo0UTbM=T{%3(1=9*8tippI2>U1f zoS}_yPG}Q;ZBH8jZRK43CS#@WE@N`h(7@_V?0jQObAsxm=#tC(g!+~P+59|$V>Za} z!}mvxWz4(OG1sN{W|&Uj2G5DAPlWci5O=|>1a&fVN)d%}xzk+Y+POW!ZV!rP6^h7$ zmGp>h?#+u^rkP|heTO$o>te*<)@&_VVG_S(s`kz@;Pu9_8PZ?mTmT&?nCTm-d3!aa z&Ti*FPRKG#7KYHg67_w=*hteg@Bt&&Tgfc$Psma-3x=H$((NloVkp+~NFi5F=d}?% zq<7JYC{%bmYsM{BRz%GWyP85GzY#*ph7g@@<5mR)lAk3Cf2kKkd2a5H$CuQ*J|o5B zJ`qpJ^ix)#T9p>kM^zGc;HMuBwW=3qkU1JC($1Z&?0S8}+dDqibzo^W9Pil%udnfq z@E+InS-FuWym}9rDg*!=*jT2YQYqPtCMsqVcnR*Ew(@j_+n1UD2<-Vp1#ixadiaij znUJvW$42{qX2%oP_<@gWIACufMF^Xtq~WwR%5kU^C*@$Uu5&(ERWxhNduhRAS^Ica zd4%l0z4!!bz1N@({T6rvj&0$Vj8nGcNRl~U2-FoKuQF3t6U&O$=45v;#J zQ=^d=zq?rkwcCa~Ls&ap9i_INvHp-I<;%Yl=9-i8yc^41Gf#>odURdDJkX3<%Z=H% zS}m?{;}SZP`R^1wrXdxa&FPtGy^0)=WiVHB3($7D{4D8#^ena011WKz$#b^ESTmQC zD&)x(T)7YV)WSQiK=GihBmt0Rm@kIQip$b&wq5_G!)HIX>JUD{ECiR55+-(}K>>)z zzXT)}>3VIpyZS*Yki;P<$l*~jA?`Qv1K3EGgNWaw6Ze^_uv@nrEBN_WI46T2FiOUf z+*vg_+N7AMJ1_uI(@Q{`G`CfGHa=TElML>>TB^Tn{Z{emj2d_IaXz>Bu|RCG}>cx%(FfkA!YMOwK5zV3YdX+$)FF`ISp))k~qqGkf)7jug%F zi74THT_iwS7NqxRZ7(OwKrtFvCjQ7`{&TG2#a-3>4slq9P0 zPClj~k&vN^iQnOL7r#rTe0vpE^B*Ap)`-a#ANpHMVzpvfE%@G^EEtkNeUjewSl0mY zZjdrS!+o-KqGiMh0#V#@%-eFSm(xyM@Cv(IgL*DP-BPrO}T+KJ(P#X z;?fSJH!@&X$f!5_s<^+wWIVL`K?vpZtffhA(Mpa9F(x2A8w_rU#=W!$bE2b`=~Q<3 z@X*~uM;+`NK-)}<6mWEZM3Xh1XB(GRv(#lx%hjG^;ZF1ScNa;09_rF|xEIC~lJfQ8 zR}2WZ-|B95-&-L%p9}&ma2*%x>b?sBgY#9z3N`EHc?gQ%c zy{R9uUKUr>`iZda5@*h8;cyxI@}(tm8uP_UK=)qkR*@m11~ zsbInn?(r#d1^4^5ajIseIzJi9iRj!6RXCl5c0;je+t*PZojeWJ zT}#4MnU;@_s|=>*zbU$t5E0y!u(};RpUUc{-32$_f?O?R`^)P{8C3BAD=1GH9m)H& zV6}QGBRAf}6G?ZgkmKodv_Pl5@5iK+7@OGG?(TaH z-&!fmGCqmbnWY>f3FVLJNYd*r0z8A#Ba#R#?m1WObS+2LujkG}6e2uN7pCU|)fGQO z&C^(d60<=myWHJXtyu;i9_cRV%Vy&R{V>Xd;b3P30L`VCu5((KpN*GU-1NkwYUGbA zM@=^Mdp7k5=O?IFncI1=@Zb@+(Y0)8?_6RUZT_ZbpWjH>B>?b*GlECqa?5uF^#_9B z@WrB69q19odjE=#E8}A>SdnAG{mcOkxTX9!QR4vp^-0XPpmM=)eE?ZYQV6OPI-Rw+ z?|3zIw1#EA&}2ZLH^Wdoyg+jYmr(@!;;vi`${3MyfgFPOfxW7zd0yvkSB@{cbwK-m}BkN>wZ_ zVXl-!%;ZtJH|9=EpyP%j9B8d!y!Zd@%kT>HO-$xl_lDG|+F=NDWq9*vY;3Glz;sY7 zY0IY~)|%VkC%3)xK1&zYhB|vP-lY!E`@~DROQLN3az?I(1R?h9U4hwnrT3-PrXlkR z+cO*(&z>bhKGJ#g*15lL^8jtZ-cCv90Q}_&kTWLoqjKHXND7@8gyuk|jW3Z{d%x$$ z5%-M#RS;d!;rh;7{?&3rf4nq*y2WI_m5LuTAZBtP-SukfeBhM*?Te_E-JY(<^A^#< ziK=g5AKtY4F`dZ^TZEc#vJG!_S}Qc0^pT|-a=#u$efu^7>8)|@NAKa@rTV?KT{ASG z>APCGF}F%%x1TcyO%5xO;os@=p11Gn;*9&1NFPNMCyJ_$7SDP+dRc8cavNEH%{tq{ z)#yWLYH3e?l-%!HiKYqrrwJipUIRBoZtxQGVj}lcIDulxI~$+lpIlCrWQ{BEG6c?+*3^rf(1wR#0BCE?0?>Ws~QKvl`FrU_sSr3 zQDPrhv#T5>t>p-G5rfN)EvHcG{V=G7{Qf<``#@`(b0)K!ykEZ(4pdvepO3bmu6M5; zn7ss$*Gbi$l?QRONN?Gg>1qCSm#}KS1#H$i=UueFhdzmiiRzF#uh&Mr;^Sb~Guz4C zIv`V=4VJ$o;RH#la8N)S$R1z`!f6p!mQVzNcdgX>+aeq8@KZb{a;uA9ZSUX%5!R-i zr`xP42dXH0k{shUmE*RH zah5zb`(vG)wv>CP581)t*DvYZEA>9Zd_H74c#<0F5$7BtR<~s@1?`=7TR7u z{O<=*fnRqFH}K>0_aw68NO<9(+8~qzM9lN2zB1t>mo<=S@cWkZ582y`1H7Lnm+|*E zy&Ow8>X*e|M=;+Ox3^(l7Pt38UfMAF(7m0?#(0>L-jbv-T9e@O^60`)jwE5$CdAv3 zG)qW9@7adw!qO3S^kpM%$aur`9?SQXSEy(_`rkFs5c-juM`EQ>%_&w?K$_|4q;`I>C{L3{r7}8`+0-{0Ii2rV;&=N`;qbjO3j97B07+QT@Hu9 zoB!3b zwCP-qMLi3W=I!_Q$Blkja`X{OsmaRe{Yy>%ukC!)z2@U}v%Oq)? z1t6m-Bm4aM`lPKeFyAIJLM zvj^x?v2i98qBWa2N$@op!wtyGCoEX3H9#%mJVSpLXrsfapgi?@}uO5(_rX(&|4;Ul{Ke9o;9Pg2 zrZvZ5e#27b-0}pLXx2B=3el>zmn0g^B1YPwFw@2xA^Kj~F6PtKtEWg^)TsT`f5%ZH z(9UYVDGPVCKfTHe?nRgZfRoc*OQa2452L~J2J=zh;7CjRAOwOt&0JB;*L*>6LHTR$7EhlP02UACValG~<|aB2Pl+peK4Gqy=r! z$bg6I&*l#Jk8au{p#=8F{GsaCfMgj#yQ})B;Hd^oo#6OEJ=s;`#FyM8kb`{r1@ z6Jw9tiRoOG`(0obMQ0t%GfzCX7}?KGE_CD~TpUgHS< z2*z=(4rgMO4}G1?u9Py>E0&K9^tjXolm>F$!(jOYLh(DW^XObi#A|v!(ym|E-)fu3 zWUH!wb&q1Kn-CFj9L&D9nVwl(l-uzR3JzEcj^aGX%~ z)X)-Mk{FOiMFoy;^^H{qY~IxEMbjb=57SH)!6L$rJlh)DLW4(~CDx&jr1O$}^3r6y*vPkeHy&SHRIk|Ne zNiQM%OOO`Q4*$7{U<*@?quW%WXHKaK~l!F2G|43g+z=Mn; zJg`y*>EfW(wYSn#!#tybMN30nUP)I!)SRW-j^l&_7&+%SX*=hb(^Ub0Ri{dC*diu< zXyxhlLWDV_Ek36KYFdYHT-a^a3H4s1$)jm+f9lX8G%Isf8we|fiuNAs5APD`JAK)< zT`S(_JlP^Zj#F3_Nh&~OZDwYNt!NI=<*ydz59Y5n@Y3xC8U4}Cx`ITxU5al)AP(8P z0!tmzs0P+w&MdzK>V4~yp7sS(s9MS?z9Rsjb7JHpo`S(fuDt7k^VoUQH>_U-tja5z z8^K}$s}%-FXx{Tgl>ns@r450O%km8u1BLsJ2IIED^%?2DpP9oHB(>Ocl%<@+h z!IuW)T$w$;l*5=|`~Ih+ceS{1yh~%Bj-jHxyFmylbMpU$sWT%Y=kv`SWf)z3Bm_D68K3l#G9e$l8+1ga5{<>k1NLT0G8XpJ)$w7;7V zsuKsu0MOB)t>33*i;kq;KTxweQo;-;v*S&r^zFMgU;>pN9?tUo5W`j)+>A_Bot>Os z0lfy50WCU<`HxMBp-#msX4hrqd|@q8+DR*8f5 zVVMsvu>!v9358>^V*>sC{WX`DU%ed9X?XKVm0c<~T0M*+UIt1@wXc}{w;7Z7w)~hm zi1~D(;EZ)k{`|AFKbk-}XTjP^b`Z6@EfSJL2*W2IXCQ8UWjPKE&J2-kk1J28{&xT@ zeD*wrp{a7@}m;C@RCu5=xx*1M%W5#j*U8#5Bm zMBlBm2k7OXVQ&W5*8i018*)Cbpk=e!eit!u?vCxqTtixME276OVjoRr34@N?1|u7m zRmZeV$1qrM!g!h)=>W&T*L5b2k<&J7juGgAMCMA45jD&=je`qRNS)SgYHW0MCObjf zj;l_Pq3o-aZ`z=dz`5i|ZP5njh~Qh>Uh7`4PlXyBC%_iskkBc%5$j9OZLcayxbR-J zyFbNs%1lugehdPUui!nwc!ppgrrb9Z^GX(Am=i+G@XRIa-?Q4=`)wk+)8mTDs?OrW z#N9NN1*T!BOyEBz5j@iVn{zAy1r8m3^%jhb1jh8CAcIJ4ymN) ztDLe%Gpu4#KU%u&$db~3TbYYXjVMB|oekWzJl z2uEn(K%`(kT9&am+?v5_?dCS;)w)n$A6=-&cqs1&{SPX{=Eaz!1b0a~;hh|p8P$91N~Xy;vu=b< z@ltPWqXQcsVjyW8$PwR@B-`u0hoo!sNk5_-xt?vdrymV#*_F3ByO=lcwr%dkh1-!y zS@q9-{SKoe))$){^;rb?d4-X6mQG}L4ZzY(GO#txw@t>)Xcc|#j?htx#;4(!&HoIF zg{}3(%XdTRh~#F_r~L19_g;a;rQmM9@J-#BQuD^c-EIVxig*|WzEG-G z&bM3Sf`GWZNAeQk7DZ^MF`asnWZWybp#ZWV-R6#8gPDTnO?+8Dn)De23M8L02e?;w zmD0MK@)zQR1qh1NsZsgl-o8Q@U;q+O9w)Zs@8d!q6xEF3+oFD^qklPr78n~fej@?h zrl2uaodd%YO{Fx0Mf(97xwdU;^JS^22{{FT8i5yMX9s0tb6#l24JgH8|Mc7yxWp=+kEndN)m>8ihbq)@pNmQX zkJ9eb;9&-WdFfu2&fu0a+O*!f<8s62;5XnPGwc)MP3#OvS(@pma&oM6iLdNa6AtXU7x|>r*o3CR=S8eOx~T#)zOgF*o)` z=IurQODzs{q#z0wx~0V3vog}sAaK)7JvFgKaUV;I8hpAiQ z{3>`nBE;rL!HJ3gsgJ+iBuo?Yc*n`wK9NyS-)>Zu`6$%$wrD^7h=@Rr6pl#-yIK6^ z?&|8&pScT7_D2lEW431iTz{Q`GdiwGQrOGnDPWL-u-F`>CrVRNV#M$qe`@jnQYISi z9emi0XS}_ACD90`IH)1tZ5KEH*N6Z6pGiqKXSCdN<;g%;NGZPEdoA60tA4P! zI9KC;b_P6H`Yt#!pDc<4W%Ye3;FV(J3usc7slorJfQfB~qN{i{NwM7Z4n0h)qqsf83k;KuuHw zK9 zEG(qtz&|n`z*k%_c{dcWgL`I|un7){Qc2r%O50?`WeEfQj;aUH->8}Iu;J+Wx_-E& zciYW8gbXv>Yh4^@q!MP}RI8J?uAIimI6u4irN>55sDg4NkwtN2&+=inah@IN43oB# z-ClO|V~)K0Iu2wl&8PxPj(D6K6KK3}H8Vedc?BP7+<+O1K2uH2Wg$E_{)uYgPPn<~ zfIq+eD4Wj1Y8_$|V=p^`;!Tp!g9Ui6vYG6C8>rlL3DlOQ=aQ+&Wu9;Itoz<4Bf>-M zi)?-2k5(GXg+#ZXylPX+dVhh$`BCJIE06=Z43^k~RZ%0uPbZEKQMd1^%s3%;w6g`t zUh?h;a)5cM2_qEQVnbRhY$IBAJMIq>6H?r#JC}A-WaFct$Y1m4GLF7~ZA43JVPPg9 zu+$yeFA(h6?c)QAzB3nCiIUvO*qUcny}V<3%+W^pWs)_)FYd^Dq6&u4=u5dEiV;J( zV5Qkch~TvGnhNJP+Dbt}BLcOMl)wkGb)VOhPT3(onPgwy`M?Ba8h>F2fA-5H56bP+ z{PYHFdxt7OhpOj>s+Sl=KwzU{92xMQj=?-}fPi43@5@BmH*8EjH&Fvs(@nIJiZnAB zj6KD)8`sB8b36%0Mi3%4Aq4|RX&)v-jeGXRDaW!i;D-#;8t@yc4oDI_0m{G z+w%kGVw_%WS=;ozLX*?cIZR>%7iq@H0B%*j&0+8S^KxIA=_Q5E0Ejj@@DB!E2OT9r z?Wk}Le8(QC;JEFJ8sw%F8i)Fh?Jz|>T^aAc75(B;7*%?!=z)ik{{;w+lf6fxwS1YB zTdG>q2}614MIa=C5m=Hgm#j|KXiN6IFCo_w4hRh23$YC;%9I(YFo#0j{J&02XxZA{3|yKUN0PPWXlOy~M*yH_QR=+J_TNIZ z|55H(D<5vN>kR!RYAyquF=I21Y)@~8cUUtvbYn|%>783t_#1@=-=u%-XXU84Hu$G+ z8$?zI|8{$N^D!z4Xyc*|vv+_^0;&BnJf07wjR4Q%c6u!?gyJ|E8HS#+oY3M;`3yqt zU`vl#XMWC6P->74U60gvy95`+7z{v=?9tie(eL6AteM{5xvht`Y_U2?-N%ZZR;GEG zXki-OO?4LS0^Jt*AP07xS@;`}j}8Hlr|BbYL{CIoH8ll%3JlCJPC7mL)D8ng3?)JX z-Qa+|p=5JTamZ=-?P_;qR7u-LX!`x)g&I;G=|J&tji!c!(?Id%b*ueFR=AYM{Z@sP zg^I^BSRuc^TT4=hP?8j>9QSZix;>%>Z@1V4J_JgOcPg6EP0L{79sF*K7pRa(6od?N z*&Pspj}8tZ{4arkrY69%C?5bsjPqTWIRNR$tK<^@K=3-d5NM|!koO39zA)`NAvNPn z!5wJB1VEf0PVK#D)H3^Rj!)zueCh*Og?xS8>YbYp0&5EkRcVZq7}d;QY6U7 zvU^Z`s8l0NjxL=MB3{d_iTAx7VCex!Sf6!f;fx#3h6Ua&oy59M-Dd6;j|%l+ty zq6*IZFdKX@zeHbDr*%WND=^(KC ziGTt=0O2Knh3=y~|4EI`D^%17#2-A!5r_oDyu5G`f}c3&%=)jm&L6)s|9&4;?&E`C z<&K0jJSfT9SoNvi=!iA84@POkZ(PjQoH-`+GNe9O2GYUlaTbrc^9K9V{d@^w(76n-3Y@Z&p(pzox|15t_dqNSwkAvp% zRd8XaW5A`Ts8mxH0)%C?HaySmA!wBJy5B!mCX4~6jo+Xla-F$YEgUbeIzlGvOnC8{ zWUBseKPXT8TkA5IU{LUusB8LSC63YO#}+CU*RfNe0eRh5DpYklz)v^n{RK{{eE^6R zh60A%lKIIypjLnDX&Db1q+wHk=m&mwZj22(_@@2Q0NVQ4#D+GP|2D=lUECv_^W^+1 z{+KGmDNulnKjH)WS1Vco=wTHVRUZJ(zORS4T+LEzR;O@~?@kST`vbAzb^aL|-1@aw zmPd!mrFwR%WuRvYQ+;9oX=W^;U-AQqp@|JhzGg7g_aT@(*DH}scB%BrFb1R*OQ$h* z*(HY8NoCBPHsUqnkz&*+Zpcy7$m--5ZSmstP=aZ);|x(h2CLoIe4NCQ z|K^q78V{a_r9T+cz%d#ofc`GN4`vi2-B_~@IDh8rSADN32@|B7;uI4@qvsT=$Wstow4hUbhmV=bdyprC_tyExb{%uVN|2 zqA`~uW6MMC^JB#OLc2H5{XT?AX#}Qd;!f?X`-L7Iuv5~__HupL%Iz~89U&$jeN%2~ zjiQ_3?bI;-;m-ayh6NSaR@VZC^_dL>QutwxsjbL(HYwu3bgki%dqQ)i`c2iEoQxru z$&C0*oY7kAN@1^zd_L)vy1xnxlkRV<4abPBW=c^s1MQi;UL*Vd$Ex;E6K$0|!S$9Z z*F~0zcJH_RpA3MgDo&UtC(0X)ED)Y>qa;9G09SwLNJfbARRLs#z*UV95cZ&wl zvQr-8s@RfkvpA7c|D0;ltYU+yvs>|)x|IliRvR{gyVA!|WwrjEPq`EKV=k@YdVO81 z0z2>hXIQ4)DSnIsrFzmV$^QgrBu)dNq7)m=YG4Yg?tQq$TNadTiduxHU zv;Khu$s{Ea29OTg_;VKVg7146)5<~xM8$++k^DUbetf26uX%a-59c)&L??cXN(*@n z>oWY03-I-8tiHEYt2@;BHv9P*?F5K$>>L?5G~k*jC5x`VP<3KY`34S|ykj+yC~awT z%W5}#AUvc2N4cADzXQ-yAO1)WpCYSBe|tvn>W&i2!5BGmf4qm~K13M{J!qvg8H(yu z&=q8;U-E%-+NllU?Ar@o7=I=}I&iT#>?)z`Dd-Hs34cL0VTs}WapIY zYbiIPN0_%+g=!kTl~+}?yhF!F9RL`HZMJ)3fc za%;rJS;3;Em5toDa>!}qc;3E|GvOOZG(p=uF;RC{?-VRf_R13*=igq=OV5nh7Gs$q zgWxip0$6~WVYx!FkhrQ1SmjSC)D8%R$xEcW!$L!B$X;SA~Jm_$ot`t#?MHhXVq9yw{Wv>5V5j;Yq&x9AaoQPNDcvXjz{-_Hk~*9N$@4kv71Y^@dI zw-0%c+nv|}P(&HqK&l~VHGZF_(($ob{Uj?jb<+B3oR8M##0U0*_Can~U{xVi`2>9t z>Mu)ViT5#C-CE?^RCI3t2L_XG)D{S_uZDIwFaYYv zJt8{0ZsllTcxGR;h(YIl97Wh1Dm9+z$Y=FntUXv1%J7Qk#w1EE?nsjg4YIZBBAgbhyG#&rc-3Gdzf$hsF76o6a+=R#m{ zVD2iZZKn$5$z7!m%8IMoI~C%3uhhhQO2qwartqdYo2{Jj5I3c8Lk0b4oNSA!2tWSo zN9SH4g<%@QPRN5D^5u>rerzGP>j@id4(|y~9k=K83|~3)af3A$H&{l5Y{fsj_RqRv zE+v?(8Zog12RFY1%{l(0C$>7F1XBdQdz_ww;gbZ+kQVpN>z;}?Fim_x8|qVAP&}q4 zenTePNEox(%qz!Vji#V^=oRFfU!s(RsM9^{+huuRFT0-Pj9;+cb30o149fWiZcGuf zlBO2hD8?HJ)_hFLmYj3A6#m7bKlanXp9!Zey}g~yToP-Alb1S1mM8IGYvJu+I~&lp zRvkG7;CTSTidsZP23zpsAsJ%A_sq^n zmpe5MN*cH5SP3ulsJk9$(VZ$hPZl&wd$8;HA{$fiZjA*Ipp-1CgNC@bn5^P|ohXMC zPa-TUuzCS5!vzte5XKX}XnLVSo-wlv`h{va(f3O{lSa*@XProyN8O&hTlX>3Xc&Mx z&amm%wnPh?hpGt)1~#mxu>vaK8htX*QY3U3Ygsi+iTD|)ejTF1aXUjpNosm}US@K6)tG)}nd! zxW*C!dWNnOD1hhZBpwho`JtEMHl{PgvE8#yM1&YV->^l3+GJd0-aBRl{^C^YY&28b zWVQBUNy;q!RBCxQewTF>z_UlpTU|$2YX{W#F1RG7BT7#p-s4wC6FLd+r zZk77byF*d#H^&>GG;x9 zmYq;D2vBj?Jy~}tw8*{Cu~!z(A&i0{qg78Pn~b*j zVTJpOb?&)~(^7Jtjy~%FzF_9Uct4VzwAlHJg?bqEbMS=N9LLi_-2`LYNfuK{VT z3i}NpRfDm@UAdhZ&7DFnn(Mjue%?ZIW*hV?UIAKxroR)2K{$ss;&BAc{0PmQ+Nqmc z6^*W`3mF(yy|m7V$utiBty_+D$;4Ms&ucy&lOQT4o^yNG;c@=F|9)QQ6dEtsIHOBP zBk30#myK34r>FNWhuyTif|jJvE4UP;Uc zK0KEi+ko%5*IlQ4m1(9i9e?g)#{pf5CSU(#ggesXn4AWw<@4DFpIW7WjXd}fHWgAn zdSH~M&?v1XB4v9HQOXLCo0qWSzMZrWKWu7}`Zfi{W3yt6Pql+L7kyE!%(>U7u+98v z@18Yg0p_v}Xmt6FM5EGYPi4~sEF@yy5vb zd9SE!*P@%_*>VJ{nJCScbsmF}nisM0Qb0z2lkj=hKm~L{hP3IYaocXA#3+00k@he4 z1Ik+2nh$=EgyQv(D&Z~c>3orW77WMGsa!P>X)P0-8$AAr+=mx%#9V*)a=egD6l0m1 zHqOHiw2BS>c5mZg6zg}Wx=2t&l|pXzOa-X>7=X41cgrMfL#~cpi;1rRJO$6~q)gEx z^y;{k*Qb{)y2Q_3+$-P5K?6X9n!1m==x6qzv-2ZIq}5U+lsnF)|B*u`=%mhNxR6u0 z?9(k4RA}E{sR2xXt+amJEg*htJUesX}Kg1a~^ zd*ryd^x5L?(fcp#IpXls;pN4?y&YcDMY@8t zpgzLP{`d|^zzJoTlSU~Rg|*PTdqy$u1|KHs`bty3KAh&r2{nyviafEvyONL)MZsxn z)j*yDE^!Z1y>G_vy?z-v#;1*LN@`YH2uHuI;5qoGBLzb{U?A=JBb30fv|NY*Vmpx1 z%~q3{7pWfH9snN8shLA7IzkJSk8%^nT;KhL&YeP8YrDhKiwV(g9NEz!68inOK4^6C zH_)=pz2!=->_0)A7&l#z@+5sZhJPX%q$miqfef@|#FvcJOnIS{y*~eB-PHGQV2WR6 zMT}J*@*eW#ykHP%GJeedxHkTb`5|}KQSy}9S1az>D<0| z&`8Amfh>byx?dj}!n6j_C78coAHf`vh)_J%iy2xizIFJwE=Nt8cxd&rjD#_#HSfMm zM&(b?33Fm^XxK+ay#`NUGtMhh^&5R}UY}=e2<~a>`|qa8K5=3RFTgZ7+5FSFPT8VK zU-6^3(b-MGkNFc@f|)`ohRz>*8H3zV#HtLd5~26kJ1qR?mE)Ib|M)?c5mB{()78cM z*EKM`D9pyt=S}a?72=+E){j|h$mGK=e?VM@tNJdJ%)p(h5o<}N`q0)>*_a0>)zY%l zNdLLJ4tI>cxT;Rign#4Tr|{Nv{P&z=qz!5v%kVz=t*4q0cZ1Qk#-4upv{`AcTeLM>;%5;CW`s>?Vw+94#vcFi$P_w9<=I{*2;{$?q=w{a_Y2Cht2cW zvqL?Bn)&!nv2(#FIKE0M>>Fcoe;T%x)Pdt}5R=2eY!A22ticnDH+C6%C zNZY38Z=9%Ww39x&Ptg4`_aUgZ*sf=`ihaYy8|Xmz&lQ9J2dOO+z#-PYV3{(t{F^aX zP|zm3V{}@Nt9GpHAAM^AwbWmxatJ$)%7>o)x?+C$Wok1KSryMvK1a6@vqMfo=>^*W_>o3xZiKh zw4v;|4BdHJ=S!cWa?cTN(Y;YZscrsQg%L+mlD6_((vZ5n+@_F=)j8|;67-@A(R=c? z=pcd*=kyu}l^>Jeg&i3@(mP2n_{RFrlk2j(ASNAEAd-O(jA_%bhvm7(?78D*?rs<7 z3G_rlJkE)csH{gAC^>E)x8!HQqPVgCUUwNzwwGNsSC;Q6-k{SF)D-v-p%t>E#q*#S zrs7AcTknosEZvvkPRzXpMhEKxkq8w<3g=LOlwx`ztrM+3tf>-~cJuvIXlUrEeHQlR zh_Ku8L8vr#iSO5EIFvDOoWn7muSen=sqq{uCMFcvY!;P_tt61!)3za6K;oYg=v4zP z#=n|qM$A`!nJaQnvr*_H+!owihMhjMGUi|`@BY3eB$#hcQ~v3545oFk%v{(Y$gb?ei6`qngZ zQ4%$0AG@USqYC#(&PAkcPcfPSHo2G-{|O=&86u*8d5xsFq}mH=|%s))~{DhxDv_*;@da4EsQ((_x`x!w=G)u11_iL zL#?^Uf`OfCU)=W+KTyL|YDgcHr0uCzwjTyQ-S>mQ))3;rLFovL81z!z1S%uw6^uwd z-N-MTRyB&BKlotDIiO<(L3zFqrM7h|gh~G1U#e6|QQ0TNnRy%0v)&Cxao z63<0#R_2&F;$%7Gs+RP$4D?0k^k2sTtqNI+7+F+lp6_k-{*U$}DkybQD~a{TQx-I% zmvpXH%$X)LSRcOdTK+AoWwiLWv?c*$v{yifAv3u<1T>gBYR(F_*WdzC_=&5fy$Wz? zq4D-+lA+tlP$4F_qdwDw>@VvI)My_fs(FdZ7BG(N+3BBQJToNjS8k$YvjO0%f!Z6X ze2FCRV94v{Oqt|(uDNi1z?>qQSfx3AT#wvze?Jx46PFpN6Q+TjWRp;Gv%CGPqPM>FN;3YHu&Iv)Am5)Vf{^ApylHCOGgxlnlG z+qBpIr)}TJsH^k4a?Bor_5hSSje{zbl}Pd5-V(5j<6hu8h6+xT<<=4G3Fp34c6pAs}hP@PtLnfWz0)**?z z%bS%QFfI@KKbpQes><*A`qH8zNGOfc9ny`Ybhngrcc&mNUDDFsDRl|SOG6ysbY?r6@J_YTr-lAm7`k}kUk*M02c=GkKVIF>+q?#ke4u@| z?4UH{E2L5ph_L+cxKKFYCaU&|!MvHal&9KQqvm5&cVv0em{D8k;XQXv)}Ycb+?KY8 zqlvH~V@=H~xFI9U)8RLn^7L)dWiUFH1Y}7BAKWIPor_;(a

mqyS&Vlu6l*MFgz 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

_B4-8Dse3|R*}F9x4(*=Gjp9kgd&Kb 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 v.id + } +} + +resource "google_network_security_firewall_endpoint" "default" { + for_each = toset(var.ngfw_config.endpoint_zones) + name = var.ngfw_config.name + parent = "organizations/${var.organization.id}" + location = each.key + billing_project_id = local.project_id +} + +resource "google_network_security_firewall_endpoint_association" "default" { + for_each = { for k in local.ngfw_associations : k.key => k } + name = "${var.ngfw_config.name}-${each.value.key}" + parent = "projects/${regex("projects/([^/]+)/", each.value.vpc_id)[0]}" + location = each.value.location + network = each.value.vpc_id + firewall_endpoint = ( + google_network_security_firewall_endpoint.default[each.value.location].id + ) + # If TLS inspection is enabled, link the regional TLS inspection policy + tls_inspection_policy = try( + local.tls_inspection_policies[each.value.tls_inspection_policy], null + ) +} diff --git a/fast/addons/2-networking-ngfw/outputs.tf b/fast/addons/2-networking-ngfw/outputs.tf new file mode 100644 index 000000000..d2646e6f1 --- /dev/null +++ b/fast/addons/2-networking-ngfw/outputs.tf @@ -0,0 +1,47 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + security_profile_group_ids = { + for k, v in google_network_security_security_profile_group.default : + k => "//networksecurity.googleapis.com/${v.id}" + } + tfvars = { + ngfw = { + associations = { + for k, v in google_network_security_firewall_endpoint_association.default : + k => v.id + } + endpoints = { + for k, v in google_network_security_firewall_endpoint.default : k => v.id + } + } + security_profile_groups = local.security_profile_group_ids + } +} + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + file_permission = "0644" + filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/${var.names.output_files_prefix}.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +resource "google_storage_bucket_object" "tfvars" { + bucket = var.automation.outputs_bucket + name = "tfvars/${var.names.output_files_prefix}.auto.tfvars.json" + content = jsonencode(local.tfvars) +} diff --git a/fast/addons/2-networking-ngfw/security-profiles.tf b/fast/addons/2-networking-ngfw/security-profiles.tf new file mode 100644 index 000000000..5549a4163 --- /dev/null +++ b/fast/addons/2-networking-ngfw/security-profiles.tf @@ -0,0 +1,66 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Organization-level network security profiles. + +locals { + security_profiles = { + for k, v in var.security_profiles : k => merge(v, { + has_profiles = ( + v.threat_prevention_profile.severity_overrides != null || + v.threat_prevention_profile.threat_overrides != null + ) + }) + } +} + +resource "google_network_security_security_profile" "default" { + for_each = local.security_profiles + name = each.key + description = each.value.description + parent = "organizations/${var.organization.id}" + location = "global" + type = "THREAT_PREVENTION" + dynamic "threat_prevention_profile" { + for_each = each.value.has_profiles ? [""] : [] + iterator = profiles + content { + dynamic "severity_overrides" { + for_each = coalesce(each.value.threat_prevention_profile.severity_overrides, {}) + content { + action = severity_overrides.value.action + severity = severity_overrides.value.severity + } + } + dynamic "threat_overrides" { + for_each = coalesce(each.value.threat_prevention_profile.threat_overrides, {}) + content { + action = threat_overrides.value.action + threat_id = threat_overrides.value.threat_id + } + } + } + } +} + +resource "google_network_security_security_profile_group" "default" { + for_each = var.security_profiles + name = each.key + description = each.value.description + parent = "organizations/${var.organization.id}" + location = "global" + threat_prevention_profile = google_network_security_security_profile.default[each.key].id +} diff --git a/fast/stages/2-security/certs.tf b/fast/addons/2-networking-ngfw/tls-inspection.tf similarity index 57% rename from fast/stages/2-security/certs.tf rename to fast/addons/2-networking-ngfw/tls-inspection.tf index 7e6111679..11aa11b7d 100644 --- a/fast/stages/2-security/certs.tf +++ b/fast/addons/2-networking-ngfw/tls-inspection.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,54 +14,43 @@ * limitations under the License. */ -# tfdoc:file:description Per-environment certificate resources. +# tfdoc:file:description TLS inspection policies and supporting resources. locals { - cas = flatten([ - for k, v in var.certificate_authorities : [ - for e in coalesce(v.environments, keys(var.environments)) : merge(v, { - environment = e - key = "${e}-${k}" - name = k - netsec_iam = contains(var.trust_configs.keys.dev.cas, k) - }) - ] - ]) - trust_configs = flatten([ - for k, v in var.trust_configs : [ - for e in coalesce(v.environments, keys(var.environments)) : merge(v, { - environment = e - key = "${e}-${k}" - name = k - }) - ] - ]) + ca_pool_ids = merge( + { for k, v in var.certificate_authority_pools : k => v.id }, + { for k, v in module.cas : k => v.ca_pool_id } + ) + trust_config_ids = { + for k, v in google_certificate_manager_trust_config.default : k => v.id + } } module "cas" { source = "../../../modules/certificate-authority-service" - for_each = { for k in local.cas : k.key => k } - project_id = module.project[each.value.environment].project_id + for_each = var.certificate_authorities + project_id = local.project_id ca_configs = each.value.ca_configs ca_pool_config = each.value.ca_pool_config iam = each.value.iam iam_bindings = each.value.iam_bindings iam_bindings_additive = merge( each.value.iam_bindings_additive, - !each.value.netsec_iam ? {} : { + var._fast_debug.skip_datasources == true ? {} : { nsec_agent = { - member = module.project[each.value.environment].service_agents["networksecurity"].iam_email + member = module.project[0].service_agents["networksecurity"].iam_email role = "roles/privateca.certificateManager" } - }) + } + ) iam_by_principals = each.value.iam_by_principals location = each.value.location } resource "google_certificate_manager_trust_config" "default" { - for_each = { for k in local.trust_configs : k.key => k } - name = each.value.name - project = module.project[each.value.environment].project_id + for_each = var.trust_configs + project = local.project_id + name = each.key description = each.value.description location = each.value.location dynamic "allowlisted_certificates" { @@ -88,3 +77,20 @@ resource "google_certificate_manager_trust_config" "default" { } } } + +resource "google_network_security_tls_inspection_policy" "default" { + for_each = var.tls_inspection_policies + project = local.project_id + name = each.key + location = each.value.location + exclude_public_ca_set = each.value.exclude_public_ca_set + ca_pool = lookup( + local.ca_pool_ids, each.value.ca_pool_id, each.value.ca_pool_id + ) + trust_config = lookup( + local.trust_config_ids, each.value.trust_config, each.value.trust_config + ) + custom_tls_features = each.value.tls.custom_features + tls_feature_profile = each.value.tls.feature_profile + min_tls_version = each.value.tls.min_version +} diff --git a/fast/addons/2-networking-ngfw/variables-fast.tf b/fast/addons/2-networking-ngfw/variables-fast.tf new file mode 100644 index 000000000..0d940305a --- /dev/null +++ b/fast/addons/2-networking-ngfw/variables-fast.tf @@ -0,0 +1,65 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description FAST stage interface. + +variable "_fast_debug" { + description = "Internal FAST variable used for testing and debugging. Do not use." + type = object({ + skip_datasources = optional(bool, false) + }) + nullable = false + default = {} +} + +variable "automation" { + # tfdoc:variable:source 0-bootstrap + description = "Automation resources created by the bootstrap stage." + type = object({ + outputs_bucket = string + }) +} + +variable "certificate_authority_pools" { + # tfdoc:variable:source 2-security + description = "Certificate authority pools." + type = map(object({ + id = string + ca_ids = map(string) + location = string + })) + nullable = false + default = {} +} + +variable "organization" { + # tfdoc:variable:source 0-globals + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "vpc_self_links" { + # tfdoc:variable:source 2-networking + description = "VPC network self links." + type = map(string) + nullable = false + default = {} +} + diff --git a/fast/addons/2-networking-ngfw/variables.tf b/fast/addons/2-networking-ngfw/variables.tf new file mode 100644 index 000000000..186c3691a --- /dev/null +++ b/fast/addons/2-networking-ngfw/variables.tf @@ -0,0 +1,245 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "certificate_authorities" { + description = "Certificate Authority Service pool and CAs. If host project ids is null identical pools and CAs are created in every host project." + type = map(object({ + location = string + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(any), {}) + iam_bindings_additive = optional(map(any), {}) + iam_by_principals = optional(map(list(string)), {}) + ca_configs = map(object({ + deletion_protection = optional(string, true) + type = optional(string, "SELF_SIGNED") + is_ca = optional(bool, true) + lifetime = optional(string, null) + pem_ca_certificate = optional(string, null) + ignore_active_certificates_on_deletion = optional(bool, false) + skip_grace_period = optional(bool, true) + labels = optional(map(string), null) + gcs_bucket = optional(string, null) + key_spec = optional(object({ + algorithm = optional(string, "RSA_PKCS1_2048_SHA256") + kms_key_id = optional(string, null) + }), {}) + key_usage = optional(object({ + cert_sign = optional(bool, true) + client_auth = optional(bool, false) + code_signing = optional(bool, false) + content_commitment = optional(bool, false) + crl_sign = optional(bool, true) + data_encipherment = optional(bool, false) + decipher_only = optional(bool, false) + digital_signature = optional(bool, false) + email_protection = optional(bool, false) + encipher_only = optional(bool, false) + key_agreement = optional(bool, false) + key_encipherment = optional(bool, true) + ocsp_signing = optional(bool, false) + server_auth = optional(bool, true) + time_stamping = optional(bool, false) + }), {}) + subject = optional( + object({ + common_name = string + organization = string + country_code = optional(string) + locality = optional(string) + organizational_unit = optional(string) + postal_code = optional(string) + province = optional(string) + street_address = optional(string) + }), + { + common_name = "test.example.com" + organization = "Test Example" + } + ) + subject_alt_name = optional(object({ + dns_names = optional(list(string), null) + email_addresses = optional(list(string), null) + ip_addresses = optional(list(string), null) + uris = optional(list(string), null) + }), null) + subordinate_config = optional(object({ + root_ca_id = optional(string) + pem_issuer_certificates = optional(list(string)) + }), null) + })) + ca_pool_config = object({ + ca_pool_id = optional(string, null) + name = optional(string, null) + tier = optional(string, "DEVOPS") + }) + })) + nullable = false + default = {} +} + +variable "names" { + description = "Configuration for names used for output files." + type = object({ + output_files_prefix = optional(string, "2-networking-ngfw") + }) + nullable = false + default = {} +} + +variable "ngfw_config" { + description = "Configuration for NGFW Enterprise endpoints. Billing project defaults to the automation project. Network and TLS inspection policy ids support interpolation." + type = object({ + endpoint_zones = list(string) + name = optional(string, "ngfw-0") + network_associations = optional(map(object({ + vpc_id = string + disabled = optional(bool) + tls_inspection_policy = optional(string) + zones = optional(list(string)) + })), {}) + }) + nullable = false +} + +variable "outputs_location" { + description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "project_id" { + description = "Project where the network security resources will be created." + type = string + nullable = false +} + +variable "security_profiles" { + description = "Security profile groups for Layer 7 inspection. Null environment list means all environments." + type = map(object({ + description = optional(string) + threat_prevention_profile = optional(object({ + severity_overrides = optional(map(object({ + action = string + severity = string + }))) + threat_overrides = optional(map(object({ + action = string + threat_id = string + }))) + }), {}) + })) + nullable = false + default = { + ngfw-default = {} + } + validation { + condition = alltrue(flatten([ + for _, v in var.security_profiles : [ + for _, sv in coalesce(v.threat_prevention_profile.severity_overrides, {}) : ( + contains(["ALERT", "ALLOW", "DEFAULT_ACTION", "DENY"], sv.action) && + contains(["CRITICAL", "HIGH", "INFORMATIONAL", "LOW", "MEDIUM"], sv.severity) + ) + ] + ])) + error_message = "Incorrect severity override token." + } + validation { + condition = alltrue(flatten([ + for _, v in var.security_profiles : [ + for _, sv in coalesce(v.threat_prevention_profile.threat_overrides, {}) : ( + contains(["ALERT", "ALLOW", "DEFAULT_ACTION", "DENY"], sv.action) + ) + ] + ])) + error_message = "Incorrect threat override token." + } +} + +variable "tls_inspection_policies" { + description = "TLS inspection policies configuration. CA pools, trust configs and host project ids support interpolation." + type = map(object({ + ca_pool_id = string + location = string + exclude_public_ca_set = optional(bool) + trust_config = optional(string) + tls = optional(object({ + custom_features = optional(list(string)) + feature_profile = optional(string) + min_version = optional(string) + }), {}) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : v.tls.min_version == null || contains( + ["TLS_VERSION_UNSPECIFIED", "TLS_1_0", "TLS_1_1", "TLS_1_2", "TLS_1_3"], + coalesce(v.tls.min_version, "-") + ) + ]) + error_message = "Invalid min TLS version." + } + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : v.tls.feature_profile == null || contains( + ["PROFILE_UNSPECIFIED", "PROFILE_COMPATIBLE", "PROFILE_MODERN", "PROFILE_RESTRICTED", "PROFILE_CUSTOM"], + coalesce(v.tls.feature_profile, "-") + ) + ]) + error_message = "Invalid TLS feature profile." + } + validation { + condition = alltrue([ + for k, v in var.tls_inspection_policies : + v.tls.custom_features == null || v.tls.feature_profile == "PROFILE_CUSTOM" + ]) + error_message = "TLS custom features can only be used with custom profile." + } +} + +variable "trust_configs" { + description = "Certificate Manager trust configurations for TLS inspection policies. Project ids and region can reference keys in the relevant FAST variables." + type = map(object({ + location = string + description = optional(string) + allowlisted_certificates = optional(map(string)) + trust_stores = optional(map(object({ + intermediate_cas = optional(map(string)) + trust_anchors = optional(map(string)) + }))) + })) + nullable = false + default = { + # dev-ngfw-default = { + # location = "primary" + # project_id = "dev-spoke-0" + # } + # prod-ngfw-default = { + # location = "primary" + # project_id = "prod-spoke-0" + # } + } + validation { + condition = alltrue([ + for k, v in var.trust_configs : ( + v.allowlisted_certificates != null || + try(v.trust_stores.intermediate_cas, null) != null || + try(v.trust_stores.trust_anchors, null) != null + ) + ]) + error_message = "a trust configuration needs at least one set of allowlisted certificates, or a valid trust store." + } +} diff --git a/fast/addons/README.md b/fast/addons/README.md new file mode 100644 index 000000000..aadb6960c --- /dev/null +++ b/fast/addons/README.md @@ -0,0 +1,42 @@ +# FAST add-ons + +Each of the folders contained here is a separate "add-on" that can be used to add extra functionality to a specific stage. + +Add-ons can be thought of as additional thin layers on top of a stage, that reuse its IaC resources and leverage the same IAM configuration: the same service accounts are used to run the add-on, and state configuration is stored in the same bucket as their "parent stage" under a different prefix. + +The only dedicated resources that can be optionally defined for add-ons are to enable CI/CD functionality, so that a dedicated repository can be used to host the add-on code and pipeline. + +Add-ons are currently only implemented for stage 1 (resource management and VPC-SC), and stage 2 (networking, project factory, security). + +## Add-on configuration + +To configure an add-on: + +- its "parent stage" (the stage the add-on augments) needs to be enabled and applied, so that the IaC and IAM configurations the add-on uses are present +- the `fast_addon` variable in the stage controlling the "parent stage" (boostrap for a stage 1 add-on, resource management for a stage 2 add-on) is configured and the stage applied, so that the add-on provider and optional CI/CD resources are created +- the provider and relevant FAST output variable files are linked or copied in the add-on folder (e.g. via the `fast-links.sh` script) + +At this point the add-on can be run, and operate on the same folders, projects and resources controlled by its "parent stage". + +Add-ons typically generate their own FAST output variable files, which can be optionally consumed by downstream stages. + +This is an example configuration of the `fast_addon` variable in the resource management stage, to enable running the NGFW networking add-on. The CI/CD configuration block is optional, and commented out here. + +```hcl +fast_addon = { + networking-ngfw = { + parent_stage = "2-networking" + # cicd_config = { + # identity_provider = "github-test" + # repository = { + # name = "test/ngfw" + # type = "github" + # branch = "main" + # } + # } + } +} +``` + +This configuration will create `tfvars/2-networking-ngfw-providers.tf` and +`tfvars/2-networking-ngfw-r-providers.tf` provider files in the GCS output bucket and local folder (if configured). diff --git a/fast/extras/0-cicd-github/main.tf b/fast/extras/0-cicd-github/main.tf index e0657233a..25b16e397 100644 --- a/fast/extras/0-cicd-github/main.tf +++ b/fast/extras/0-cicd-github/main.tf @@ -42,33 +42,13 @@ locals { for k, v in var.repositories : k => v.create_options == null ? k : github_repository.default[k].name } - repository_files = merge( - { - for k in local._repository_files : - "${k.repository}/${k.name}" => k - if !endswith(k.name, ".tf") || ( - !startswith(k.name, "0") && k.name != "globals.tf" - ) - }, - { - for k, v in var.repositories : - "${k}/templates/providers.tf.tpl" => { - repository = k - file = "../../assets/templates/providers.tf.tpl" - name = "templates/providers.tf.tpl" - } - if v.populate_from != null - }, - { - for k, v in var.repositories : - "${k}/templates/workflow-github.yaml" => { - repository = k - file = "../../assets/templates/workflow-github.yaml" - name = "templates/workflow-github.yaml" - } - if v.populate_from != null - } - ) + repository_files = { + for k in local._repository_files : + "${k.repository}/${k.name}" => k + if !endswith(k.name, ".tf") || ( + !startswith(k.name, "0") && k.name != "globals.tf" + ) + } } resource "github_repository" "default" { diff --git a/fast/plugins/2-networking-serverless-connector/README.md b/fast/plugins/2-networking-serverless-connector/README.md deleted file mode 100644 index b955d138c..000000000 --- a/fast/plugins/2-networking-serverless-connector/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# VPC Serverless Connector - -This FAST plugin adds centralized [Serverless VPC Access Connectors](https://cloud.google.com/vpc/docs/serverless-vpc-access) to network stages. - -This plugin does not manage - -- IAM bindings for the connectors, which should be added via the stage project-level variables -- firewall rules for the connectors, which should be added via the stage factory - -The plugin only requires a specific configuration if the defaults it uses need to be changed: - -- the connector-specific subnets default to the `10.255.255.0` range -- the machine type, number of instances and throughput use the API defaults - -To enable the plugin, simply copy or link its files in the networking stage. - - - - -## Files - -| name | description | modules | resources | -|---|---|---|---| -| [local-serverless-connector-outputs.tf](./local-serverless-connector-outputs.tf) | Serverless Connector outputs. | | google_storage_bucket_object · local_file | -| [local-serverless-connector-variables.tf](./local-serverless-connector-variables.tf) | Serverless Connector variables. | | | -| [local-serverless-connector.tf](./local-serverless-connector.tf) | Serverless Connector resources. | net-vpc | google_vpc_access_connector | - -## Variables - -| name | description | type | required | default | producer | -|---|---|:---:|:---:|:---:|:---:| -| [serverless_connector_config](local-serverless-connector-variables.tf#L19) | VPC Access Serverless Connectors configuration. | object({…}) | | {…} | | - -## Outputs - -| name | description | sensitive | consumers | -|---|---|:---:|---| -| [plugin_sc_connectors](local-serverless-connector-outputs.tf#L47) | VPC Access Connectors. | | | - - diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf deleted file mode 100644 index ef562508b..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-outputs.tf +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector outputs. - -locals { - plugin_sc_tfvars = { - dev = google_vpc_access_connector.dev-primary[0].id - prod = google_vpc_access_connector.prod-primary[0].id - } -} - -# generate tfvars file for subsequent stages - -resource "local_file" "plugin_sc_tfvars" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/2-networking-serverless-connnector.auto.tfvars.json" - content = jsonencode({ - vpc_connectors = local.plugin_sc_tfvars - }) -} - -resource "google_storage_bucket_object" "plugin_sc_tfvars" { - bucket = var.automation.outputs_bucket - name = "tfvars/2-networking-serverless-connnector.auto.tfvars.json" - content = jsonencode({ - vpc_connectors = local.plugin_sc_tfvars - }) -} - -# outputs - -output "plugin_sc_connectors" { - description = "VPC Access Connectors." - value = local.plugin_sc_tfvars -} diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf deleted file mode 100644 index 05733c467..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector-variables.tf +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector variables. - -variable "serverless_connector_config" { - description = "VPC Access Serverless Connectors configuration." - type = object({ - dev-primary = object({ - ip_cidr_range = optional(string, "10.255.255.128/28") - machine_type = optional(string) - instances = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - throughput = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - }) - prod-primary = object({ - ip_cidr_range = optional(string, "10.255.255.0/28") - machine_type = optional(string) - instances = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - throughput = optional(object({ - max = optional(number) - min = optional(number) - }), {}) - }) - }) - default = { - dev-primary = {} - prod-primary = {} - } - nullable = false -} diff --git a/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf b/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf deleted file mode 100644 index e70de806c..000000000 --- a/fast/plugins/2-networking-serverless-connector/local-serverless-connector.tf +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Serverless Connector resources. - -locals { - plugin_sc_subnets = { - dev = { - for k, v in module.dev-spoke-vpc-serverless.subnets : k => v.name - } - prod = { - for k, v in module.prod-spoke-vpc-serverless.subnets : k => v.name - } - } -} - -module "dev-spoke-vpc-serverless" { - source = "../../../modules/net-vpc" - project_id = module.dev-spoke-project.project_id - name = module.dev-spoke-vpc.name - vpc_create = false - subnets = [{ - name = "access-connector" - description = "VPC Serverless Connector for the primary region." - ip_cidr_range = var.serverless_connector_config.dev-primary.ip_cidr_range - region = var.regions.primary - }] - # these should be create from the main VPC - create_googleapis_routes = null -} - -module "prod-spoke-vpc-serverless" { - source = "../../../modules/net-vpc" - project_id = module.prod-spoke-project.project_id - name = module.prod-spoke-vpc.name - vpc_create = false - subnets = [{ - name = "access-connector" - description = "VPC Serverless Connector for the primary region." - ip_cidr_range = var.serverless_connector_config.prod-primary.ip_cidr_range - region = var.regions.primary - }] - # these should be create from the main VPC - create_googleapis_routes = null -} - -resource "google_vpc_access_connector" "dev-primary" { - count = var.serverless_connector_config.dev-primary == null ? 0 : 1 - project = module.dev-spoke-project.project_id - region = var.regions.primary - name = "access-connector-${local.region_shortnames[var.regions.primary]}" - subnet { - name = local.plugin_sc_subnets.dev["${var.regions.primary}/access-connector"] - } - machine_type = var.serverless_connector_config.dev-primary.machine_type - max_instances = var.serverless_connector_config.dev-primary.instances.max - max_throughput = var.serverless_connector_config.dev-primary.throughput.max - min_instances = var.serverless_connector_config.dev-primary.instances.min - min_throughput = var.serverless_connector_config.dev-primary.throughput.min -} - -resource "google_vpc_access_connector" "prod-primary" { - count = var.serverless_connector_config.prod-primary == null ? 0 : 1 - project = module.prod-spoke-project.project_id - region = var.regions.primary - name = "access-connector-${local.region_shortnames[var.regions.primary]}" - subnet { - name = local.plugin_sc_subnets.prod["${var.regions.primary}/access-connector"] - } - machine_type = var.serverless_connector_config.prod-primary.machine_type - max_instances = var.serverless_connector_config.prod-primary.instances.max - max_throughput = var.serverless_connector_config.prod-primary.throughput.max - min_instances = var.serverless_connector_config.prod-primary.instances.min - min_throughput = var.serverless_connector_config.prod-primary.throughput.min -} diff --git a/fast/plugins/README.md b/fast/plugins/README.md deleted file mode 100644 index d4e7ce787..000000000 --- a/fast/plugins/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# FAST plugin system - -This folders details a simple mechanism that can be used to add extra functionality to FAST stages, and a few examples that implement simple plugins that can be used as-is. - -## Available plugins - -### Networking - -- [Serverless VPC Access Connector](./2-networking-serverless-connector/) - -## Anatomy of a plugin - -FAST plugins are much simpler and easier to code than full-blown stages: each plugin is meant to add a single feature using a small set of resources, and interacting directly with stage modules and variables. - -A simple plugin might be composed of a single file with one resource, and grow up to the canonical set of one "main" (resources), one variables, and outputs file. - -Plugin file names start with the `local-` prefix which is purposefully excluded in FAST stages via Git ignore, so that plugins are not accidentally committed to stages during development and staying aligned with our master branch is possible. - -Plugins are structured here as individual folders, organized in top-level folders according to the FAST stage they are designed to work with. - -As an example, the [`2-networking/serverless-connector` plugin](./2-networking-serverless-connector/) implements centralized [Serverless VPC Access Connectors](https://cloud.google.com/vpc/docs/serverless-vpc-access) for our networking stages, and is composed of three files: - -- [`local-serverless-connector.tf`](./2-networking-serverless-connector/local-serverless-connector.tf) managing resources including the subnets needed in each VPC and the connectors themselves -- [`local-serverless-connector-outputs.tf`](./2-networking-serverless-connector/local-serverless-connector-outputs.tf) defining a single `serverless_connectors` output for the plugin, and optional output files -- [`local-serverless-connector-variables.tf`](./2-networking-serverless-connector/local-serverless-connector-variables.tf) defining a single `serverless_connector_config` variable used to configure the plugin diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 83a15b134..49bc06b3f 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -43,6 +43,7 @@ Use the following diagram as a simple high level reference for the following sec - [Workload Identity Federation](#workload-identity-federation) - [Project folders](#project-folders) - [CI/CD repositories](#cicd-repositories) + - [Add-ons](#add-ons) - [Files](#files) - [Variables](#variables) - [Outputs](#outputs) @@ -636,6 +637,10 @@ The remaining configuration is manual, as it regards the repositories themselves - for Gitlab, rename it to `.gitlab-ci.yml` and place it in the repository root - for Source Repositories, place it in `.cloudbuild/workflow.yaml` +### Add-ons + +FAST defines a simple mechanism to extend stage functionality via the use of [add-ons](../../addons/). Configuration for stage 1 add-ons happens here via the `fast_addon` variable. Refer to the add-ons documentation for more details on their use. + ## Files @@ -644,7 +649,7 @@ The remaining configuration is manual, as it regards the repositories themselves |---|---|---|---| | [automation.tf](./automation.tf) | Automation project and resources. | gcs · iam-service-account · project | | | [billing.tf](./billing.tf) | Billing export project and dataset. | bigquery-dataset · project | google_billing_account_iam_member | -| [cicd.tf](./cicd.tf) | Workload Identity Federation configurations for CI/CD. | iam-service-account | | +| [cicd.tf](./cicd.tf) | CI/CD locals and resources. | iam-service-account | | | [identity-providers-defs.tf](./identity-providers-defs.tf) | Identity provider definitions. | | | | [identity-providers.tf](./identity-providers.tf) | Workload Identity Federation provider definitions. | | google_iam_workforce_pool · google_iam_workforce_pool_provider · google_iam_workload_identity_pool · google_iam_workload_identity_pool_provider | | [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | @@ -653,7 +658,9 @@ The remaining configuration is manual, as it regards the repositories themselves | [organization.tf](./organization.tf) | Organization-level IAM. | organization | | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | local_file | | [outputs-gcs.tf](./outputs-gcs.tf) | Output files persistence to automation GCS bucket. | | google_storage_bucket_object | +| [outputs-providers.tf](./outputs-providers.tf) | Locals for provider output files. | | | | [outputs.tf](./outputs.tf) | Module outputs. | | | +| [variables-addons.tf](./variables-addons.tf) | None | | | | [variables.tf](./variables.tf) | Module variables. | | | ## Variables @@ -661,41 +668,42 @@ The remaining configuration is manual, as it regards the repositories themselves | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | -| [organization](variables.tf#L290) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L305) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [organization](variables.tf#L277) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L292) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L38) | Email of the nominal user running this stage for the first time. | string | | null | | -| [cicd_repositories](variables.tf#L44) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | -| [custom_roles](variables.tf#L98) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | -| [environments](variables.tf#L105) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | -| [essential_contacts](variables.tf#L139) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [groups](variables.tf#L156) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | -| [iam](variables.tf#L172) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_bindings_additive](variables.tf#L179) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | -| [iam_by_principals](variables.tf#L194) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | -| [locations](variables.tf#L201) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | -| [log_sinks](variables.tf#L215) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [org_policies_config](variables.tf#L271) | Organization policies customization. | object({…}) | | {} | | -| [outputs_location](variables.tf#L299) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L314) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | -| [resource_names](variables.tf#L325) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [workforce_identity_providers](variables.tf#L357) | Workforce Identity Federation pools. | map(object({…})) | | {} | | -| [workload_identity_providers](variables.tf#L373) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [cicd_config](variables.tf#L44) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | {} | | +| [custom_roles](variables.tf#L85) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | +| [environments](variables.tf#L92) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | +| [essential_contacts](variables.tf#L126) | Email used for essential contacts, unset if null. | string | | null | | +| [factories_config](variables.tf#L132) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [fast_addon](variables-addons.tf#L17) | FAST addons configurations for stages 1. Keys are used as short names for the add-on resources. | map(object({…})) | | {} | | +| [groups](variables.tf#L143) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | +| [iam](variables.tf#L159) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_bindings_additive](variables.tf#L166) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | +| [iam_by_principals](variables.tf#L181) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | +| [locations](variables.tf#L188) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | +| [log_sinks](variables.tf#L202) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [org_policies_config](variables.tf#L258) | Organization policies customization. | object({…}) | | {} | | +| [outputs_location](variables.tf#L286) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L301) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | +| [resource_names](variables.tf#L312) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | +| [workforce_identity_providers](variables.tf#L344) | Workforce Identity Federation pools. | map(object({…})) | | {} | | +| [workload_identity_providers](variables.tf#L360) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [automation](outputs.tf#L159) | Automation resources. | | | -| [billing_dataset](outputs.tf#L164) | BigQuery dataset prepared for billing export. | | | -| [cicd_repositories](outputs.tf#L169) | CI/CD repository configurations. | | | -| [custom_roles](outputs.tf#L181) | Organization-level custom roles. | | | -| [outputs_bucket](outputs.tf#L186) | GCS bucket where generated output files are stored. | | | -| [project_ids](outputs.tf#L191) | Projects created by this stage. | | | -| [providers](outputs.tf#L201) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| [service_accounts](outputs.tf#L208) | Automation service accounts created by this stage. | | | -| [tfvars](outputs.tf#L217) | Terraform variable files for the following stages. | ✓ | | -| [tfvars_globals](outputs.tf#L223) | Terraform Globals variable files for the following stages. | ✓ | | -| [workforce_identity_pool](outputs.tf#L229) | Workforce Identity Federation pool. | | | -| [workload_identity_pool](outputs.tf#L238) | Workload Identity Federation pool and providers. | | | +| [automation](outputs.tf#L112) | Automation resources. | | | +| [billing_dataset](outputs.tf#L117) | BigQuery dataset prepared for billing export. | | | +| [cicd_repositories](outputs.tf#L122) | CI/CD repository configurations. | | | +| [custom_roles](outputs.tf#L134) | Organization-level custom roles. | | | +| [outputs_bucket](outputs.tf#L139) | GCS bucket where generated output files are stored. | | | +| [project_ids](outputs.tf#L144) | Projects created by this stage. | | | +| [providers](outputs.tf#L154) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| [service_accounts](outputs.tf#L161) | Automation service accounts created by this stage. | | | +| [tfvars](outputs.tf#L170) | Terraform variable files for the following stages. | ✓ | | +| [tfvars_globals](outputs.tf#L176) | Terraform Globals variable files for the following stages. | ✓ | | +| [workforce_identity_pool](outputs.tf#L182) | Workforce Identity Federation pool. | | | +| [workload_identity_pool](outputs.tf#L191) | Workload Identity Federation pool and providers. | | | diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index 4dc44093d..82568d684 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -16,15 +16,6 @@ # tfdoc:file:description Automation project and resources. -locals { - cicd_resman_sa = try(module.automation-tf-cicd-sa["resman"].iam_email, "") - cicd_resman_r_sa = try(module.automation-tf-cicd-r-sa["resman"].iam_email, "") - cicd_tenants_sa = try(module.automation-tf-cicd-sa["tenants"].iam_email, "") - cicd_tenants_r_sa = try(module.automation-tf-cicd-r-sa["tenants"].iam_email, "") - cicd_vpcsc_sa = try(module.automation-tf-cicd-sa["vpcsc"].iam_email, "") - cicd_vpcsc_r_sa = try(module.automation-tf-cicd-r-sa["vpcsc"].iam_email, "") -} - module "automation-project" { source = "../../../modules/project" billing_account = var.billing_account.id @@ -219,9 +210,10 @@ module "automation-tf-bootstrap-sa" { prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.automation-tf-cicd-sa["bootstrap"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "bootstrap" + ] } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] @@ -236,9 +228,10 @@ module "automation-tf-bootstrap-r-sa" { prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.automation-tf-cicd-r-sa["bootstrap"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "bootstrap" + ] } # we grant organization roles here as IAM bindings have precedence over # custom roles in the organization module, so these need to depend on it @@ -276,21 +269,12 @@ module "automation-tf-resman-sa" { display_name = "Terraform stage 1 resman service account." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - local.cicd_resman_sa == "" ? {} : { - cicd_token_creator_resman = { - member = local.cicd_resman_sa - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_tenants_sa == "" ? {} : { - cicd_token_creator_tenants = { - member = local.cicd_tenants_sa - role = "roles/iam.serviceAccountTokenCreator" - } - } - ) + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "resman" + ] + } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] } @@ -303,21 +287,12 @@ module "automation-tf-resman-r-sa" { display_name = "Terraform stage 1 resman service account (read-only)." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - local.cicd_resman_r_sa == "" ? {} : { - cicd_token_creator_resman = { - member = local.cicd_resman_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_tenants_r_sa == "" ? {} : { - cicd_token_creator_tenants = { - member = local.cicd_tenants_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } - } - ) + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "resman" + ] + } # we grant organization roles here as IAM bindings have precedence over # custom roles in the organization module, so these need to depend on it iam_organization_roles = { @@ -354,21 +329,18 @@ module "automation-tf-vpcsc-sa" { display_name = "Terraform stage 1 vpcsc service account." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = merge( - { - security_admins = { - member = local.principals["gcp-security-admins"] - role = "roles/iam.serviceAccountTokenCreator" - } - }, - local.cicd_vpcsc_sa == "" ? {} : { - cicd_token_creator_vpcsc = { - member = local.cicd_vpcsc_sa - role = "roles/iam.serviceAccountTokenCreator" - } + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-sa[k].iam_email if v.stage == "vpcsc" + ] + } + iam_bindings_additive = { + security_admins = { + member = local.principals["gcp-security-admins"] + role = "roles/iam.serviceAccountTokenCreator" } - ) + } iam_storage_roles = { (module.automation-tf-output-gcs.name) = ["roles/storage.admin"] } @@ -381,12 +353,11 @@ module "automation-tf-vpcsc-r-sa" { display_name = "Terraform stage 1 vpcsc service account (read-only)." prefix = var.prefix # allow SA used by CI/CD workflow to impersonate this SA - # we use additive IAM to allow tenant CI/CD SAs to impersonate it - iam_bindings_additive = local.cicd_vpcsc_r_sa == "" ? {} : { - cicd_token_creator_vpcsc = { - member = local.cicd_vpcsc_r_sa - role = "roles/iam.serviceAccountTokenCreator" - } + iam = { + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.automation-tf-cicd-r-sa[k].iam_email if v.stage == "vpcsc" + ] } iam_storage_roles = { (module.automation-tf-output-gcs.name) = [module.organization.custom_role_id["storage_viewer"]] diff --git a/fast/stages/0-bootstrap/cicd.tf b/fast/stages/0-bootstrap/cicd.tf index f6f766079..1c2771c45 100644 --- a/fast/stages/0-bootstrap/cicd.tf +++ b/fast/stages/0-bootstrap/cicd.tf @@ -14,9 +14,25 @@ * limitations under the License. */ -# tfdoc:file:description Workload Identity Federation configurations for CI/CD. +# tfdoc:file:description CI/CD locals and resources. locals { + _cicd_configs = merge( + # stages + { + for k, v in var.cicd_config : k => merge(v, { + level = k == "bootstrap" ? 0 : 1 + stage = k + }) if v != null + }, + # addons + { + for k, v in var.fast_addon : k => merge(v.cicd_config, { + level = 1 + stage = substr(v.parent_stage, 2, -1) + }) if v.cicd_config != null + } + ) cicd_providers = { for k, v in google_iam_workload_identity_pool_provider.default : k => { @@ -32,45 +48,21 @@ locals { } } cicd_repositories = { - for k, v in coalesce(var.cicd_repositories, {}) : k => v - if( - v != null - && - contains( - keys(local.workload_identity_providers), - coalesce(try(v.identity_provider, null), ":") - ) - && - fileexists( - format("${path.module}/templates/workflow-%s.yaml", try(v.type, "")) - ) + for k, v in local._cicd_configs : k => v if( + contains(keys(local.workload_identity_providers), v.identity_provider) && + fileexists("${path.module}/templates/workflow-${v.repository.type}.yaml") ) } - cicd_workflow_providers = { - bootstrap = "0-bootstrap-providers.tf" - bootstrap_r = "0-bootstrap-r-providers.tf" - resman = "1-resman-providers.tf" - resman_r = "1-resman-r-providers.tf" - tenants = "1-tenant-factory-providers.tf" - tenants_r = "1-tenant-factory-r-providers.tf" - vpcsc = "1-vpcsc-providers.tf" - vpcsc_r = "1-vpcsc-r-providers.tf" - } - cicd_workflow_var_files = { - bootstrap = [] - resman = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - tenants = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - vpcsc = [ - "0-bootstrap.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - } + cicd_workflow_providers = merge( + { + for k, v in local.cicd_repositories : + k => "${v.level}-${k}-providers.tf" + }, + { + for k, v in local.cicd_repositories : + "${k}-r" => "${v.level}-${k}-r-providers.tf" + } + ) } # SAs used by CI/CD workflows to impersonate automation SAs @@ -86,17 +78,17 @@ module "automation-tf-cicd-sa" { prefix = var.prefix iam = { "roles/iam.workloadIdentityUser" = [ - each.value.branch == null + each.value.repository.branch == null ? format( - local.workload_identity_providers_defs[each.value.type].principal_repo, + local.workload_identity_providers_defs[each.value.repository.type].principal_repo, google_iam_workload_identity_pool.default[0].name, - each.value.name + each.value.repository.name ) : format( - local.workload_identity_providers_defs[each.value.type].principal_branch, + local.workload_identity_providers_defs[each.value.repository.type].principal_branch, google_iam_workload_identity_pool.default[0].name, - each.value.name, - each.value.branch + each.value.repository.name, + each.value.repository.branch ) ] } @@ -120,9 +112,9 @@ module "automation-tf-cicd-r-sa" { iam = { "roles/iam.workloadIdentityUser" = [ format( - local.workload_identity_providers_defs[each.value.type].principal_repo, + local.workload_identity_providers_defs[each.value.repository.type].principal_repo, google_iam_workload_identity_pool.default[0].name, - each.value.name + each.value.repository.name ) ] } diff --git a/fast/stages/0-bootstrap/outputs-providers.tf b/fast/stages/0-bootstrap/outputs-providers.tf new file mode 100644 index 000000000..e32fad605 --- /dev/null +++ b/fast/stages/0-bootstrap/outputs-providers.tf @@ -0,0 +1,99 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for provider output files. + +locals { + providers = merge( + # this stage's providers + { + "0-bootstrap" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-sa.email + }) + "0-bootstrap-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-bootstrap-gcs.name + name = "bootstrap" + sa = module.automation-tf-bootstrap-r-sa.email + }) + }, + # stage 1 providers + { + "1-resman" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-sa.email + }) + "1-resman-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.automation-tf-resman-gcs.name + name = "resman" + sa = module.automation-tf-resman-r-sa.email + }) + "1-vpcsc" = templatefile(local._tpl_providers, { + backend_extra = "prefix = \"vpcsc\"" + bucket = module.automation-tf-vpcsc-gcs.name + name = "vpcsc" + sa = module.automation-tf-vpcsc-sa.email + }) + "1-vpcsc-r" = templatefile(local._tpl_providers, { + backend_extra = "prefix = \"vpcsc\"" + bucket = module.automation-tf-vpcsc-gcs.name + name = "vpcsc" + sa = module.automation-tf-vpcsc-r-sa.email + }) + }, + # stage 1 addons + { + for k, v in var.fast_addon : + "${v.parent_stage}-${k}" => templatefile(local._tpl_providers, { + backend_extra = "prefix = \"addons/${k}\"" + name = "${v.parent_stage}-${k}" + bucket = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-gcs.name + : module.automation-tf-vpcsc-gcs.name + ) + sa = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-sa.name + : module.automation-tf-vpcsc-sa.name + ) + }) + }, + { + for k, v in var.fast_addon : + "${v.parent_stage}-${k}-r" => templatefile(local._tpl_providers, { + backend_extra = "prefix = \"addons/${k}\"" + name = "${v.parent_stage}-${k}" + bucket = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-gcs.name + : module.automation-tf-vpcsc-gcs.name + ) + sa = ( + v.parent_stage == "resman" + ? module.automation-tf-resman-r-sa.name + : module.automation-tf-vpcsc-r-sa.name + ) + }) + } + ) +} diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 876b269f0..c7beb1606 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -18,15 +18,15 @@ locals { _tpl_providers = "${path.module}/templates/providers.tf.tpl" # render CI/CD workflow templates cicd_workflows = { - for k, v in local.cicd_repositories : k => templatefile( - "${path.module}/templates/workflow-${v.type}.yaml", { + for k, v in local.cicd_repositories : "${v.level}-${k}" => templatefile( + "${path.module}/templates/workflow-${v.repository.type}.yaml", { # If users give a list of custom audiences we set by default the first element. # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} audiences = try( - local.cicd_providers[v["identity_provider"]].audiences, "" + local.cicd_providers[v.identity_provider].audiences, [] ) identity_provider = try( - local.cicd_providers[v["identity_provider"]].name, "" + local.cicd_providers[v.identity_provider].name, "" ) outputs_bucket = module.automation-tf-output-gcs.name service_accounts = { @@ -36,62 +36,15 @@ locals { stage_name = k tf_providers_files = { apply = local.cicd_workflow_providers[k] - plan = local.cicd_workflow_providers["${k}_r"] + plan = local.cicd_workflow_providers["${k}-r"] } - tf_var_files = local.cicd_workflow_var_files[k] + tf_var_files = k == "bootstrap" ? [] : [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json" + ] } ) } - providers = { - "0-bootstrap" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-bootstrap-gcs.name - name = "bootstrap" - sa = module.automation-tf-bootstrap-sa.email - }) - "0-bootstrap-r" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-bootstrap-gcs.name - name = "bootstrap" - sa = module.automation-tf-bootstrap-r-sa.email - }) - "1-resman" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-resman-gcs.name - name = "resman" - sa = module.automation-tf-resman-sa.email - }) - "1-resman-r" = templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.automation-tf-resman-gcs.name - name = "resman" - sa = module.automation-tf-resman-r-sa.email - }) - "1-tenant-factory" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"tenant-factory\"" - bucket = module.automation-tf-resman-gcs.name - name = "tenant-factory" - sa = module.automation-tf-resman-sa.email - }) - "1-tenant-factory-r" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"tenant-factory\"" - bucket = module.automation-tf-resman-gcs.name - name = "tenant-factory" - sa = module.automation-tf-resman-r-sa.email - }) - "1-vpcsc" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"vpcsc\"" - bucket = module.automation-tf-vpcsc-gcs.name - name = "vpcsc" - sa = module.automation-tf-vpcsc-sa.email - }) - "1-vpcsc-r" = templatefile(local._tpl_providers, { - backend_extra = "prefix = \"vpcsc\"" - bucket = module.automation-tf-vpcsc-gcs.name - name = "vpcsc" - sa = module.automation-tf-vpcsc-r-sa.email - }) - } tfvars = { automation = { federated_identity_pool = try( @@ -170,8 +123,8 @@ output "cicd_repositories" { description = "CI/CD repository configurations." value = { for k, v in local.cicd_repositories : k => { - branch = v.branch - name = v.name + branch = v.repository.branch + name = v.repository.name provider = try(local.cicd_providers[v.identity_provider].name, null) service_account = try(module.automation-tf-cicd-sa[k].email, null) } diff --git a/fast/stages/0-bootstrap/variables-addons.tf b/fast/stages/0-bootstrap/variables-addons.tf new file mode 100644 index 000000000..cc505db46 --- /dev/null +++ b/fast/stages/0-bootstrap/variables-addons.tf @@ -0,0 +1,48 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "fast_addon" { + description = "FAST addons configurations for stages 1. Keys are used as short names for the add-on resources." + type = map(object({ + parent_stage = string + cicd_config = optional(object({ + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.fast_addon : contains(["1-resman", "1-vpcsc"], v.parent_stage) + ]) + error_message = "Bootstrap-defined addons only support '1-resman' and '1-vpcsc' stages." + } + validation { + condition = alltrue([ + for k, v in var.fast_addon : + v.cicd_config == null || contains( + ["github", "gitlab"], + coalesce(try(v.cicd_config.repository.type, null), "-") + ) + ]) + error_message = "Invalid CI/CD repository type." + } +} diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index e1fd0bd9f..042207e04 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -41,54 +41,41 @@ variable "bootstrap_user" { default = null } -variable "cicd_repositories" { +variable "cicd_config" { description = "CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed." type = object({ bootstrap = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) resman = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) - })) - tenants = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) vpcsc = optional(object({ - name = string - type = string - branch = optional(string) - identity_provider = optional(string) + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) })) }) - default = null + nullable = false + default = {} validation { condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : - v == null || try(v.name, null) != null - ]) - error_message = "Non-null repositories need a non-null name." - } - validation { - condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : - v == null || try(v.identity_provider, null) != null - ]) - error_message = "Non-null repositories need a non-null provider." - } - validation { - condition = alltrue([ - for k, v in coalesce(var.cicd_repositories, {}) : + for k, v in coalesce(var.cicd_config, {}) : v == null || ( - contains(["github", "gitlab", "terraform"], coalesce(try(v.type, null), "null")) + contains(["github", "gitlab", "terraform"], coalesce(try(v.repository.type, null), "null")) ) ]) error_message = "Invalid repository type, supported types: 'github', 'gitlab', or 'terraform'." diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index 91a120761..25dbc05c4 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -150,7 +150,9 @@ tags = { ### Multitenancy -Multitenancy is supported via a [separate stage](../1-tenant-factory/), which is entirely optional and can be applied after resource management has been deployed. For simpler use cases that do not require complex organization-level multitenancy, [top-level folders](#top-level-folders) can be used in combination with the [project factory stage](../2-project-factory/) support for folder and project management. +Multitenancy is supported via an [add-on](../../addons/1-resman-tenants/) which is entirely optional, and is be applied after resource management has been deployed. The add-on needs to be enabled before use via the `fast_addon` variable in the bootstrap stage. + +For simpler use cases that do not require complex organization-level multitenancy, [top-level folders](#top-level-folders) can be used in combination with the [project factory stage](../2-project-factory/) support for folder and project management. ### Workload Identity Federation and CI/CD @@ -245,17 +247,19 @@ terraform apply | [iam.tf](./iam.tf) | Organization or root node-level IAM bindings. | | | | [main.tf](./main.tf) | Module-level locals and resources. | | | | [organization.tf](./organization.tf) | Organization policies. | organization | | +| [outputs-cicd.tf](./outputs-cicd.tf) | Locals for CI/CD workflow files. | | | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | google_storage_bucket_object · local_file | +| [outputs-providers.tf](./outputs-providers.tf) | Locals for provider output files. | | | | [outputs.tf](./outputs.tf) | Module outputs. | | | -| [stage-2-network-security.tf](./stage-2-network-security.tf) | None | gcs · iam-service-account | | | [stage-2-networking.tf](./stage-2-networking.tf) | None | folder · gcs · iam-service-account | | | [stage-2-project-factory.tf](./stage-2-project-factory.tf) | None | gcs · iam-service-account | | | [stage-2-security.tf](./stage-2-security.tf) | None | folder · gcs · iam-service-account | | | [stage-3.tf](./stage-3.tf) | None | folder · gcs · iam-service-account | | -| [stage-cicd.tf](./stage-cicd.tf) | None | iam-service-account | | +| [stage-cicd.tf](./stage-cicd.tf) | CI/CD locals and resources. | iam-service-account | | | [tenant-logging.tf](./tenant-logging.tf) | Audit log project and sink for tenant root folder. | bigquery-dataset · gcs · logging-bucket · pubsub | | | [tenant-root.tf](./tenant-root.tf) | None | folder · project | | | [top-level-folders.tf](./top-level-folders.tf) | None | folder · gcs · iam-service-account | | +| [variables-addons.tf](./variables-addons.tf) | None | | | | [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | | [variables-stages.tf](./variables-stages.tf) | None | | | | [variables-toplevel-folders.tf](./variables-toplevel-folders.tf) | None | | | @@ -265,21 +269,22 @@ terraform apply | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables-fast.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L71) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [logging](variables-fast.tf#L116) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | -| [organization](variables-fast.tf#L129) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L147) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables-fast.tf#L43) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L72) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L118) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | +| [organization](variables-fast.tf#L131) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L149) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [custom_roles](variables-fast.tf#L54) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | -| [fast_stage_3](variables-stages.tf#L95) | FAST stages 3 configurations. | map(object({…})) | | {} | | -| [groups](variables-fast.tf#L88) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L103) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [fast_addon](variables-addons.tf#L17) | FAST addons configurations for stages 2. Keys are used as short names for the add-on resources. | map(object({…})) | | {} | | +| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | +| [fast_stage_3](variables-stages.tf#L83) | FAST stages 3 configurations. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L90) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L105) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | | [outputs_location](variables.tf#L31) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | | [resource_names](variables.tf#L37) | Resource names overrides for specific resources. Stage names are interpolated via `$${name}`. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [root_node](variables-fast.tf#L153) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | +| [root_node](variables-fast.tf#L155) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | | [tag_names](variables.tf#L62) | Customized names for resource management tags. | object({…}) | | {} | | | [tags](variables.tf#L76) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | | [top_level_folders](variables-toplevel-folders.tf#L17) | Additional top-level folders. Keys are used for service account and bucket names, values implement the folders module interface with the addition of the 'automation' attribute. | map(object({…})) | | {} | | diff --git a/fast/stages/1-resman/iam.tf b/fast/stages/1-resman/iam.tf index 68a9dcb28..3116fd19f 100644 --- a/fast/stages/1-resman/iam.tf +++ b/fast/stages/1-resman/iam.tf @@ -21,31 +21,24 @@ locals { iam_bindings_additive = merge( # stage 2 networking !var.fast_stage_2.networking.enabled ? {} : { - sa_net_fw_policy_admin = { + sa_net_rw_fw_policy_admin = { member = module.net-sa-rw[0].iam_email role = "roles/compute.orgFirewallPolicyAdmin" } - sa_net_xpn_admin = { + sa_net_rw_ngfw_enterprise_admin = { + member = module.net-sa-rw[0].iam_email + role = var.custom_roles["ngfw_enterprise_admin"], + } + sa_net_rw_xpn_admin = { member = module.net-sa-rw[0].iam_email role = "roles/compute.xpnAdmin" } - }, - # stage 2 network security - !var.fast_stage_2.network_security.enabled ? {} : { - sa_nsec_fw_policy_admin = { - member = module.nsec-sa-rw[0].iam_email - role = "roles/compute.orgFirewallPolicyAdmin" - } - sa_net_nsec_ngfw_enterprise_admin = { - member = module.nsec-sa-rw[0].iam_email - role = var.custom_roles["ngfw_enterprise_admin"], - } - sa_net_nsec_fw_policy_user = { - member = module.nsec-sa-rw[0].iam_email + sa_net_ro_fw_policy_user = { + member = module.net-sa-ro[0].iam_email role = "roles/compute.orgFirewallPolicyUser" } - sa_net_nsec_ro_ngfw_enterprise_viewer = { - member = module.nsec-sa-ro[0].iam_email + sa_net_net_ro_ngfw_enterprise_viewer = { + member = module.net-sa-ro[0].iam_email role = var.custom_roles["ngfw_enterprise_viewer"], } }, diff --git a/fast/stages/1-resman/main.tf b/fast/stages/1-resman/main.tf index fbff1284e..fee31c65a 100644 --- a/fast/stages/1-resman/main.tf +++ b/fast/stages/1-resman/main.tf @@ -15,11 +15,6 @@ */ locals { - # leaving this here to document how to get self identity in a stage - # automation_resman_sa = try( - # data.google_client_openid_userinfo.provider_identity[0].email, null - # ) - # tag values use descriptive names identity_providers = coalesce( try(var.automation.federated_identity_providers, null), {} ) @@ -35,6 +30,14 @@ locals { ? "organizations/${var.organization.id}" : var.root_node ) + # normalize parent stages + stage_addons = { + for k, v in var.fast_addon : k => merge(v, { + short_name = k + stage = regex("^(?P[0-9])-(?P.*?)$", v.parent_stage) + }) + } + # combined list of stage service accounts stage_service_accounts = merge( !var.fast_stage_2.networking.enabled ? {} : { networking = module.net-sa-rw[0].email @@ -72,6 +75,10 @@ locals { top_level_service_accounts = { for k, v in module.top-level-sa : k => try(v.email) } + # leaving this here to document how to get self identity in a stage + # automation_resman_sa = try( + # data.google_client_openid_userinfo.provider_identity[0].email, null + # ) } # data "google_client_openid_userinfo" "provider_identity" { diff --git a/fast/stages/1-resman/outputs-cicd.tf b/fast/stages/1-resman/outputs-cicd.tf new file mode 100644 index 000000000..4ee38e6ad --- /dev/null +++ b/fast/stages/1-resman/outputs-cicd.tf @@ -0,0 +1,60 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for CI/CD workflow files. + +locals { + # render CI/CD workflow templates + cicd_workflows = { + for k, v in local.cicd_repositories : "${v.level}-${replace(k, "_", "-")}" => templatefile( + "${path.module}/templates/workflow-${v.repository.type}.yaml", { + # If users give a list of custom audiences we set by default the first element. + # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} + audiences = try( + local.cicd_workflow_providers[v.identity_provider].audiences, [] + ) + identity_provider = try( + local.cicd_workflow_providers[v.identity_provider].name, "" + ) + outputs_bucket = var.automation.outputs_bucket + service_accounts = { + apply = try(module.cicd-sa-rw[k].email, "") + plan = try(module.cicd-sa-ro[k].email, "") + } + stage_name = k + tf_providers_files = { + apply = local.cicd_workflow_providers[k] + plan = local.cicd_workflow_providers["${k}-r"] + } + tf_var_files = ( + v.level == 2 + ? [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json", + "1-resman.auto.tfvars.json" + ] + : [ + "0-bootstrap.auto.tfvars.json", + "0-globals.auto.tfvars.json", + "1-resman.auto.tfvars.json", + "2-networking.auto.tfvars.json", + "2-security.auto.tfvars.json" + ] + ) + } + ) + } +} diff --git a/fast/stages/1-resman/outputs-files.tf b/fast/stages/1-resman/outputs-files.tf index 166b8558a..b268263de 100644 --- a/fast/stages/1-resman/outputs-files.tf +++ b/fast/stages/1-resman/outputs-files.tf @@ -17,152 +17,8 @@ # tfdoc:file:description Output files persistence to local filesystem. locals { - # output file definitions for enabled stage 2s - _stage2_outputs_attrs = merge( - var.fast_stage_2["networking"].enabled != true ? {} : { - networking = { - bucket = module.net-bucket[0].name - sa = { - apply = module.net-sa-rw[0].email - plan = module.net-sa-ro[0].email - } - } - }, - var.fast_stage_2["network_security"].enabled != true ? {} : { - network_security = { - bucket = module.nsec-bucket[0].name - sa = { - apply = module.nsec-sa-rw[0].email - plan = module.nsec-sa-ro[0].email - } - } - }, - var.fast_stage_2["project_factory"].enabled != true ? {} : { - project_factory = { - bucket = module.pf-bucket[0].name - sa = { - apply = module.pf-sa-rw[0].email - plan = module.pf-sa-ro[0].email - } - } - }, - var.fast_stage_2["security"].enabled != true ? {} : { - security = { - bucket = module.sec-bucket[0].name - sa = { - apply = module.sec-sa-rw[0].email - plan = module.sec-sa-ro[0].email - } - } - } - ) - # CI/CD workflow definitions for enabled stages - _cicd_workflow_attrs = merge( - # stage 2s - { - for k, v in local._stage2_outputs_attrs : k => { - audiences = try( - local.identity_providers[local.cicd_repositories[k].identity_provider].audiences, null - ) - identity_provider = try( - local.identity_providers[local.cicd_repositories[k].identity_provider].name, null - ) - outputs_bucket = var.automation.outputs_bucket - service_accounts = { - apply = try(module.cicd-sa-rw[k].email, "") - plan = try(module.cicd-sa-ro[k].email, "") - } - repository = local.cicd_repositories[k].repository - stage_name = k - tf_providers_files = { - apply = "2-${replace(k, "_", "-")}-providers.tf" - plan = "2-${replace(k, "_", "-")}-r-providers.tf" - } - tf_var_files = local.cicd_workflow_files.stage_2 - } if lookup(local.cicd_repositories, k, null) != null - }, - # stage 3 - { - for k, v in local.cicd_repositories : "${v.lvl}-${k}" => { - audiences = try( - local.identity_providers[v.identity_provider].audiences, null - ) - identity_provider = try( - local.identity_providers[v.identity_provider].name, null - ) - outputs_bucket = var.automation.outputs_bucket - repository = v.repository - service_accounts = { - apply = module.cicd-sa-rw[0].email - plan = module.cicd-sa-ro[0].email - } - stage_name = v.short_name - tf_providers_files = { - apply = "${v.lvl}-${k}-providers.tf" - plan = "${v.lvl}-${k}-r-providers.tf" - } - tf_var_files = local.cicd_workflow_files.stage_3 - } if v.lvl == 3 - } - ) - _tpl_providers = "${path.module}/templates/providers.tf.tpl" - cicd_workflows = { - for k, v in local._cicd_workflow_attrs : k => templatefile( - "${path.module}/templates/workflow-${v.repository.type}.yaml", v - ) - } + _tpl_providers = "${path.module}/templates/providers.tf.tpl" outputs_location = try(pathexpand(var.outputs_location), "") - # render provider files from template - providers = merge( - # stage 2 - { - for k, v in local._stage2_outputs_attrs : - "2-${replace(k, "_", "-")}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = v.bucket - name = "networking" - sa = v.sa.apply - }) - }, - { - for k, v in local._stage2_outputs_attrs : - "2-${replace(k, "_", "-")}-r" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = v.bucket - name = "networking" - sa = v.sa.plan - }) - }, - # stage 3 - { - for k, v in local.stage3 : - "3-${k}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.stage3-bucket[k].name - name = k - sa = module.stage3-sa-rw[k].email - }) - }, - { - for k, v in local.stage3 : - "3-${k}-r" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.stage3-bucket[k].name - name = k - sa = module.stage3-sa-ro[k].email - }) - }, - # top-level folders - { - for k, v in module.top-level-sa : - "1-resman-folder-${k}" => templatefile(local._tpl_providers, { - backend_extra = null - bucket = module.top-level-bucket[k].name - name = k - sa = v.email - }) - }, - ) } resource "local_file" "providers" { diff --git a/fast/stages/1-resman/outputs-providers.tf b/fast/stages/1-resman/outputs-providers.tf new file mode 100644 index 000000000..8ed7b6c58 --- /dev/null +++ b/fast/stages/1-resman/outputs-providers.tf @@ -0,0 +1,133 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Locals for provider output files. + +locals { + # output file definitions for enabled stage 2s + _stage2_outputs_attrs = merge( + var.fast_stage_2["networking"].enabled != true ? {} : { + networking = { + bucket = module.net-bucket[0].name + sa = { + apply = module.net-sa-rw[0].email + plan = module.net-sa-ro[0].email + } + } + }, + var.fast_stage_2["project_factory"].enabled != true ? {} : { + project_factory = { + bucket = module.pf-bucket[0].name + sa = { + apply = module.pf-sa-rw[0].email + plan = module.pf-sa-ro[0].email + } + } + }, + var.fast_stage_2["security"].enabled != true ? {} : { + security = { + bucket = module.sec-bucket[0].name + sa = { + apply = module.sec-sa-rw[0].email + plan = module.sec-sa-ro[0].email + } + } + }, + # TODO: use ${parent_stage}-${key} for the addon file output names + # addons, conditions are repeated to prevent inconsistent result type error + var.fast_stage_2["networking"].enabled != true ? {} : { + for k, v in local.stage_addons : "networking-${k}" => { + bucket = module.net-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.net-sa-rw[0].email + plan = module.net-sa-ro[0].email + } + } if v.parent_stage == "2-networking" + }, + var.fast_stage_2["project_factory"].enabled != true ? {} : { + for k, v in local.stage_addons : "pf-${k}" => { + bucket = module.pf-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.pf-sa-rw[0].email + plan = module.pf-sa-ro[0].email + } + } if v.parent_stage == "2-project-factory" + }, + var.fast_stage_2["security"].enabled != true ? {} : { + for k, v in local.stage_addons : "security-${k}" => { + bucket = module.sec-bucket[0].name + backend_extra = "prefix = \"addons/${k}\"" + sa = { + apply = module.sec-sa-rw[0].email + plan = module.sec-sa-ro[0].email + } + } if v.parent_stage == "2-security" + } + ) + # render provider files from template + providers = merge( + # stage 2 + { + for k, v in local._stage2_outputs_attrs : + "2-${replace(k, "_", "-")}" => templatefile(local._tpl_providers, { + backend_extra = lookup(v, "backend_extra", null) + bucket = v.bucket + name = k + sa = v.sa.apply + }) + }, + { + for k, v in local._stage2_outputs_attrs : + "2-${replace(k, "_", "-")}-r" => templatefile(local._tpl_providers, { + backend_extra = lookup(v, "backend_extra", null) + bucket = v.bucket + name = k + sa = v.sa.plan + }) + }, + # stage 3 + { + for k, v in local.stage3 : + "3-${k}" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.stage3-bucket[k].name + name = k + sa = module.stage3-sa-rw[k].email + }) + }, + { + for k, v in local.stage3 : + "3-${k}-r" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.stage3-bucket[k].name + name = k + sa = module.stage3-sa-ro[k].email + }) + }, + # top-level folders + { + for k, v in module.top-level-sa : + "1-resman-folder-${k}" => templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.top-level-bucket[k].name + name = k + sa = v.email + }) + }, + ) +} diff --git a/fast/stages/1-resman/stage-2-network-security.tf b/fast/stages/1-resman/stage-2-network-security.tf deleted file mode 100644 index 627fe5008..000000000 --- a/fast/stages/1-resman/stage-2-network-security.tf +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# automation service accounts - -module "nsec-sa-rw" { - source = "../../../modules/iam-service-account" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["sa-nsec_rw"], { - name = var.fast_stage_2.network_security.short_name - }) - display_name = "Terraform resman network security main service account." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["network_security"].iam_email, null) - ]) - } - iam_project_roles = { - (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] - } - iam_storage_roles = { - (var.automation.outputs_bucket) = ["roles/storage.objectAdmin"] - } -} - -module "nsec-sa-ro" { - source = "../../../modules/iam-service-account" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["sa-nsec_ro"], { - name = var.fast_stage_2.network_security.short_name - }) - display_name = "Terraform resman network security main service account (read-only)." - prefix = var.prefix - iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["network_security"].iam_email, null) - ]) - } - iam_project_roles = { - (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] - } - iam_storage_roles = { - (var.automation.outputs_bucket) = [var.custom_roles["storage_viewer"]] - } -} - -# automation bucket - -module "nsec-bucket" { - source = "../../../modules/gcs" - count = var.fast_stage_2.network_security.enabled ? 1 : 0 - project_id = var.automation.project_id - name = templatestring(var.resource_names["gcs-nsec"], { - name = var.fast_stage_2.network_security.short_name - }) - prefix = var.prefix - location = var.locations.gcs - versioning = true - iam = { - "roles/storage.objectAdmin" = [module.nsec-sa-rw[0].iam_email] - "roles/storage.objectViewer" = [module.nsec-sa-ro[0].iam_email] - } -} diff --git a/fast/stages/1-resman/stage-2-networking.tf b/fast/stages/1-resman/stage-2-networking.tf index 8ca86e8a2..a61dae41a 100644 --- a/fast/stages/1-resman/stage-2-networking.tf +++ b/fast/stages/1-resman/stage-2-networking.tf @@ -57,21 +57,6 @@ module "net-folder" { "roles/resourcemanager.folderViewer" = [module.net-sa-ro[0].iam_email] "roles/resourcemanager.tagViewer" = [module.net-sa-ro[0].iam_email] }, - # network security stage 2 service accounts - var.fast_stage_2.network_security.enabled != true ? {} : { - "roles/serviceusage.serviceUsageAdmin" = [ - module.nsec-sa-rw[0].iam_email - ] - (var.custom_roles["network_firewall_policies_admin"]) = [ - module.nsec-sa-rw[0].iam_email - ] - "roles/compute.orgFirewallPolicyUser" = [ - module.nsec-sa-ro[0].iam_email - ] - "roles/serviceusage.serviceUsageConsumer" = [ - module.nsec-sa-ro[0].iam_email - ] - }, # security stage 2 service accounts var.fast_stage_2.security.enabled != true ? {} : { "roles/serviceusage.serviceUsageAdmin" = [ @@ -169,9 +154,10 @@ module "net-sa-rw" { prefix = var.prefix service_account_create = var.root_node == null iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["networking"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "networking" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -191,9 +177,10 @@ module "net-sa-ro" { display_name = "Terraform resman networking service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["networking"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "networking" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-2-project-factory.tf b/fast/stages/1-resman/stage-2-project-factory.tf index 8b40daf74..91500bdca 100644 --- a/fast/stages/1-resman/stage-2-project-factory.tf +++ b/fast/stages/1-resman/stage-2-project-factory.tf @@ -26,9 +26,10 @@ module "pf-sa-rw" { display_name = "Terraform resman project factory main service account." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["project_factory"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "project-factory" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -48,9 +49,10 @@ module "pf-sa-ro" { display_name = "Terraform resman project factory main service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["project_factory"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "project-factory" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-2-security.tf b/fast/stages/1-resman/stage-2-security.tf index 88e1fa713..0c335ab2d 100644 --- a/fast/stages/1-resman/stage-2-security.tf +++ b/fast/stages/1-resman/stage-2-security.tf @@ -143,9 +143,10 @@ module "sec-sa-rw" { prefix = var.prefix service_account_create = var.root_node == null iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-rw["security"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-rw[k].iam_email if v.stage == "security" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] @@ -165,9 +166,10 @@ module "sec-sa-ro" { display_name = "Terraform resman security service account (read-only)." prefix = var.prefix iam = { - "roles/iam.serviceAccountTokenCreator" = compact([ - try(module.cicd-sa-ro["security"].iam_email, null) - ]) + "roles/iam.serviceAccountTokenCreator" = [ + for k, v in local.cicd_repositories : + module.cicd-sa-ro[k].iam_email if v.stage == "security" + ] } iam_project_roles = { (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] diff --git a/fast/stages/1-resman/stage-cicd.tf b/fast/stages/1-resman/stage-cicd.tf index 1ed22407a..58de5f8d9 100644 --- a/fast/stages/1-resman/stage-cicd.tf +++ b/fast/stages/1-resman/stage-cicd.tf @@ -14,24 +14,36 @@ * limitations under the License. */ +# tfdoc:file:description CI/CD locals and resources. + locals { - # intermediate normalization of repository configurations _cicd_configs = merge( - # stage 2s + # stage 2 { - for k, v in var.fast_stage_2 : - k => merge(v.cicd_config, { - env = "prod", short_name = v.short_name, lvl = 2 - }) - if v.cicd_config != null + for k, v in var.fast_stage_2 : k => merge(v.cicd_config, { + env = "prod" + level = 2 + stage = replace(k, "_", "-") + short_name = v.short_name + }) if v.cicd_config != null }, - # stage 3s + # stage 3 { - for k, v in local.stage3 : - k => merge(v.cicd_config, { - env = v.environment, short_name = coalesce(v.short_name, k), lvl = 3 - }) - if v.cicd_config != null + for k, v in local.stage3 : k => merge(v.cicd_config, { + env = v.environment + level = 3 + short_name = coalesce(v.short_name, k) + stage = replace(k, "_", "-") + }) if v.cicd_config != null + }, + # addons + { + for k, v in var.fast_addon : k => merge(v.cicd_config, { + env = "prod" + level = 2 + short_name = k + stage = substr(v.parent_stage, 2, -1) + }) if v.cicd_config != null } ) # finalize configurations and filter by valid identity provider and type @@ -41,18 +53,16 @@ locals { fileexists("${path.module}/templates/workflow-${v.repository.type}.yaml") ) } - # lists of input files for each stage - cicd_workflow_files = { - stage_2 = [ - "0-bootstrap.auto.tfvars.json", - "1-resman.auto.tfvars.json", - "0-globals.auto.tfvars.json" - ] - stage_3 = [ - for k, v in local._cicd_configs : - "2-${k}.auto.tfvars" if v.lvl == 2 - ] - } + cicd_workflow_providers = merge( + { + for k, v in local.cicd_repositories : + k => "${v.level}-${k}-providers.tf" + }, + { + for k, v in local.cicd_repositories : + "${k}-r" => "${v.level}-${k}-r-providers.tf" + } + ) } module "cicd-sa-rw" { @@ -63,9 +73,9 @@ module "cicd-sa-rw" { name = each.value.short_name }) display_name = ( - "CI/CD ${each.value.lvl}-${each.value.short_name} ${each.value.env} service account." + "CI/CD ${each.value.level}-${each.value.short_name} ${each.value.env} service account." ) - prefix = "${var.prefix}-${each.value.env}" + prefix = "${var.prefix}-${var.environments[each.value.env].short_name}" iam = { "roles/iam.workloadIdentityUser" = [ each.value.repository.branch == null @@ -98,9 +108,9 @@ module "cicd-sa-ro" { name = each.value.short_name }) display_name = ( - "CI/CD ${each.value.lvl}-${each.value.short_name} ${each.value.env} service account (read-only)." + "CI/CD ${each.value.level}-${each.value.short_name} ${each.value.env} service account (read-only)." ) - prefix = "${var.prefix}-${each.value.env}" + prefix = "${var.prefix}-${var.environments[each.value.env].short_name}" iam = { "roles/iam.workloadIdentityUser" = [ format( diff --git a/fast/stages/1-resman/variables-addons.tf b/fast/stages/1-resman/variables-addons.tf new file mode 100644 index 000000000..1c521a386 --- /dev/null +++ b/fast/stages/1-resman/variables-addons.tf @@ -0,0 +1,51 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "fast_addon" { + description = "FAST addons configurations for stages 2. Keys are used as short names for the add-on resources." + type = map(object({ + parent_stage = string + cicd_config = optional(object({ + identity_provider = string + repository = object({ + name = string + branch = optional(string) + type = optional(string, "github") + }) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.fast_addon : contains( + ["2-networking", "2-project-factory", "2-security"], + v.parent_stage + ) + ]) + error_message = "Resman-defined addons only support '2-networking', '2-project-factory' and '2-security' stages." + } + validation { + condition = alltrue([ + for k, v in var.fast_addon : + v.cicd_config == null || contains( + ["github", "gitlab"], + coalesce(try(v.cicd_config.repository.type, null), "-") + ) + ]) + error_message = "Invalid CI/CD repository type." + } +} diff --git a/fast/stages/1-resman/variables-fast.tf b/fast/stages/1-resman/variables-fast.tf index a35f5eca4..439139b84 100644 --- a/fast/stages/1-resman/variables-fast.tf +++ b/fast/stages/1-resman/variables-fast.tf @@ -33,6 +33,7 @@ variable "automation" { principal_repo = string })) service_accounts = object({ + resman = string resman-r = string }) }) @@ -73,6 +74,7 @@ variable "environments" { description = "Environment names." type = map(object({ name = string + short_name = string tag_name = string is_default = optional(bool, false) })) diff --git a/fast/stages/1-resman/variables-stages.tf b/fast/stages/1-resman/variables-stages.tf index c46bdb977..cecb50551 100644 --- a/fast/stages/1-resman/variables-stages.tf +++ b/fast/stages/1-resman/variables-stages.tf @@ -35,18 +35,6 @@ variable "fast_stage_2" { parent_id = optional(string) }), {}) }), {}) - network_security = optional(object({ - enabled = optional(bool, false) - short_name = optional(string, "nsec") - cicd_config = optional(object({ - identity_provider = string - repository = object({ - name = string - branch = optional(string) - type = optional(string, "github") - }) - })) - }), {}) project_factory = optional(object({ enabled = optional(bool, true) short_name = optional(string, "pf") diff --git a/fast/stages/1-tenant-factory/templates/workflow-github.yaml b/fast/stages/1-tenant-factory/templates/workflow-github.yaml deleted file mode 100644 index d8185e2c7..000000000 --- a/fast/stages/1-tenant-factory/templates/workflow-github.yaml +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: "FAST ${stage_name} stage" - -on: - pull_request: - branches: - - main - types: - - closed - - opened - - synchronize - -env: - FAST_SERVICE_ACCOUNT: ${service_accounts.apply} - FAST_SERVICE_ACCOUNT_PLAN: ${service_accounts.plan} - FAST_WIF_PROVIDER: ${identity_provider} - SSH_AUTH_SOCK: /tmp/ssh_agent.sock - TF_PROVIDERS_FILE: ${tf_providers_files.apply} - TF_PROVIDERS_FILE_PLAN: ${tf_providers_files.plan} - TF_VERSION: 1.7.4 - -jobs: - fast-pr: - # Skip PRs which are closed without being merged. - if: >- - github.event.action == 'closed' && - github.event.pull_request.merged == true || - github.event.action == 'opened' || - github.event.action == 'synchronize' - permissions: - contents: read - id-token: write - issues: write - pull-requests: write - runs-on: ubuntu-latest - steps: - - id: checkout - name: Checkout repository - uses: actions/checkout@v4 - - # set up SSH key authentication to the modules repository - - - id: ssh-config - name: Configure SSH authentication - run: | - ssh-agent -a "$SSH_AUTH_SOCK" > /dev/null - ssh-add - <<< "$${{ secrets.CICD_MODULES_KEY }}" - - # set up step variables for plan / apply - - - id: vars-plan - if: github.event.pull_request.merged != true && success() - name: Set up plan variables - run: | - echo "plan_opts=-lock=false" >> "$GITHUB_ENV" - echo "provider_file=$${{env.TF_PROVIDERS_FILE_PLAN}}" >> "$GITHUB_ENV" - echo "service_account=$${{env.FAST_SERVICE_ACCOUNT_PLAN}}" >> "$GITHUB_ENV" - - - id: vars-apply - if: github.event.pull_request.merged == true && success() - name: Set up apply variables - run: | - echo "provider_file=$${{env.TF_PROVIDERS_FILE}}" >> "$GITHUB_ENV" - echo "service_account=$${{env.FAST_SERVICE_ACCOUNT}}" >> "$GITHUB_ENV" - - # set up authentication via Workload identity Federation and gcloud - - - id: gcp-auth - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 - with: - workload_identity_provider: $${{env.FAST_WIF_PROVIDER}} - service_account: $${{env.service_account}} - access_token_lifetime: 900s - - - id: gcp-sdk - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2 - with: - install_components: alpha - - # copy provider file - - - id: tf-config-provider - name: Copy Terraform provider file - run: | - gcloud storage cp -r \ - "gs://${outputs_bucket}/providers/$${{env.provider_file}}" ./ - %{~ for f in tf_var_files ~} - gcloud storage cp -r \ - "gs://${outputs_bucket}/tfvars/${f}" ./ - %{~ endfor ~} - - - id: tf-setup - name: Set up Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: $${{env.TF_VERSION}} - - # run Terraform init/validate/plan - - - id: tf-init - name: Terraform init - continue-on-error: true - run: | - terraform init -no-color - - - id: tf-validate - continue-on-error: true - name: Terraform validate - run: terraform validate -no-color - - - id: tf-plan - name: Terraform plan - continue-on-error: true - run: | - terraform plan -input=false -out ../plan.out -no-color $${{env.plan_opts}} - - - id: tf-apply - if: github.event.pull_request.merged == true && success() - name: Terraform apply - continue-on-error: true - run: | - terraform apply -input=false -auto-approve -no-color ../plan.out - - # PR comment with Terraform result from previous steps - # length is checked and trimmed for length so as to stay within the limit - - - id: pr-comment - name: Post comment to Pull Request - continue-on-error: true - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' - env: - PLAN: $${{steps.tf-plan.outputs.stdout}}\n$${{steps.tf-plan.outputs.stderr}} - with: - script: | - const output = `### Terraform Initialization \`$${{steps.tf-init.outcome}}\` - - ### Terraform Validation \`$${{steps.tf-validate.outcome}}\` - -

Validation Output - - \`\`\`\n - $${{steps.tf-validate.outputs.stdout}} - \`\`\` - -
- - ### Terraform Plan \`$${{steps.tf-plan.outcome}}\` - -
Show Plan - - \`\`\`\n - $${process.env.PLAN.split('\n').filter(l => l.match(/^([A-Z\s].*|)$$/)).join('\n')} - \`\`\` - -
- - ### Terraform Apply \`$${{steps.tf-apply.outcome}}\` - - *Pusher: @$${{github.actor}}, Action: \`$${{github.event_name}}\`, Working Directory: \`$${{env.tf_actions_working_dir}}\`, Workflow: \`$${{github.workflow}}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - - id: pr-short-comment - name: Post comment to Pull Request (abbreviated) - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' && steps.pr-comment.outcome != 'success' - with: - script: | - const output = `### Terraform Initialization \`$${{steps.tf-init.outcome}}\` - - ### Terraform Validation \`$${{steps.tf-validate.outcome}}\` - - ### Terraform Plan \`$${{steps.tf-plan.outcome}}\` - - Plan output is in the action log. - - ### Terraform Apply \`$${{steps.tf-apply.outcome}}\` - - *Pusher: @$${{github.actor}}, Action: \`$${{github.event_name}}\`, Working Directory: \`$${{env.tf_actions_working_dir}}\`, Workflow: \`$${{github.workflow}}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) - - # exit on error from previous steps - - - id: check-init - name: Check init failure - if: steps.tf-init.outcome != 'success' - run: exit 1 - - - id: check-validate - name: Check validate failure - if: steps.tf-validate.outcome != 'success' - run: exit 1 - - - id: check-plan - name: Check plan failure - if: steps.tf-plan.outcome != 'success' - run: exit 1 - - - id: check-apply - name: Check apply failure - if: github.event.pull_request.merged == true && steps.tf-apply.outcome != 'success' - run: exit 1 diff --git a/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml b/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml deleted file mode 100644 index 150340835..000000000 --- a/fast/stages/1-tenant-factory/templates/workflow-gitlab.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -variables: - GOOGLE_CREDENTIALS: cicd-sa-credentials.json - FAST_OUTPUTS_BUCKET: ${outputs_bucket} - FAST_WIF_PROVIDER: ${identity_provider} - SSH_AUTH_SOCK: /tmp/ssh_agent.sock - %{~ if tf_var_files != [] ~} - TF_VAR_FILES: ${join("\n ", tf_var_files)} - %{~ endif ~} - -workflow: - rules: - # merge / apply - - if: $CI_PIPELINE_SOURCE == 'push' && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH - variables: - COMMAND: apply - FAST_SERVICE_ACCOUNT: ${service_accounts.apply} - TF_PROVIDERS_FILE: ${tf_providers_files.apply} - # pr / plan - - if: $CI_PIPELINE_SOURCE == 'merge_request_event' - variables: - COMMAND: plan - FAST_SERVICE_ACCOUNT: ${service_accounts.plan} - TF_PROVIDERS_FILE: ${tf_providers_files.plan} - -stages: - - gcp-setup - - tf-plan-apply - -# TODO: document project-level deploy key used to fetch modules - -gcp-setup: - stage: gcp-setup - image: - name: google/cloud-sdk:slim - artifacts: - paths: - - cicd-sa-credentials.json - - providers.tf - %{~ for f in tf_var_files ~} - - ${f} - %{~ endfor ~} - id_tokens: - GITLAB_TOKEN: - aud: - %{~ for aud in audiences ~} - - ${aud} - %{~ endfor ~} - before_script: - - echo "$GITLAB_TOKEN" > token.txt - script: - - | - gcloud iam workload-identity-pools create-cred-config \ - $FAST_WIF_PROVIDER \ - --service-account=$FAST_SERVICE_ACCOUNT \ - --service-account-token-lifetime-seconds=900 \ - --output-file=$GOOGLE_CREDENTIALS \ - --credential-source-file=token.txt - - gcloud config set auth/credential_file_override $GOOGLE_CREDENTIALS - - gcloud storage cp -r "gs://$FAST_OUTPUTS_BUCKET/providers/$TF_PROVIDERS_FILE" ./providers.tf - %{~ for f in tf_var_files ~} - - gcloud storage cp gs://$FAST_OUTPUTS_BUCKET/tfvars/${f} ./ - %{~ endfor ~} - - -tf-plan-apply: - stage: tf-plan-apply - dependencies: - - gcp-setup - id_tokens: - GITLAB_TOKEN: - aud: - %{~ for aud in audiences ~} - - ${aud} - %{~ endfor ~} - image: - name: hashicorp/terraform - entrypoint: - - "/usr/bin/env" - variables: - SSH_AUTH_SOCK: /tmp/ssh-agent.sock - script: - - | - ssh-agent -a $SSH_AUTH_SOCK - echo "$CICD_MODULES_KEY" | ssh-add - - mkdir -p ~/.ssh - ssh-keyscan -H 'gitlab.com' >> ~/.ssh/known_hosts - ssh-keyscan gitlab.com | sort -u - ~/.ssh/known_hosts -o ~/.ssh/known_hosts - - echo "$GITLAB_TOKEN" > token.txt - - terraform init - - terraform validate - - "if [ $COMMAND == 'plan' ]; then terraform plan -input=false -no-color -lock=false; fi" - - "if [ $COMMAND == 'apply' ]; then terraform apply -input=false -no-color -auto-approve; fi" diff --git a/fast/stages/2-network-security/.fast-stage.env b/fast/stages/2-network-security/.fast-stage.env deleted file mode 100644 index 6c51deaae..000000000 --- a/fast/stages/2-network-security/.fast-stage.env +++ /dev/null @@ -1,5 +0,0 @@ -FAST_STAGE_DESCRIPTION="network securoty (optional)" -FAST_STAGE_LEVEL=2 -FAST_STAGE_NAME=network-security -FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman" -FAST_STAGE_OPTIONAL="2-networking 2-security" \ No newline at end of file diff --git a/fast/stages/2-network-security/README.md b/fast/stages/2-network-security/README.md deleted file mode 100644 index ebad67615..000000000 --- a/fast/stages/2-network-security/README.md +++ /dev/null @@ -1,199 +0,0 @@ -# Network Security - -This stage enables NGFW Enterprise in the dev `dev` and `prod` VPCs. This includes: - -- security profiles -- security profile groups -- NGFW endpoints -- NGFW endpoint associations -- global network firewall policies and some recommended firewall policy rules - -The following diagram is a high level reference of the resources created and managed here (excludes projects and VPCs): - -

- Network security NGFW diagram -

- - -- [Design overview and choices](#design-overview-and-choices) -- [How to run this stage](#how-to-run-this-stage) - - [Provider and Terraform variables](#provider-and-terraform-variables) - - [Impersonating the automation service account](#impersonating-the-automation-service-account) - - [Variable configuration](#variable-configuration) - - [Running the stage](#running-the-stage) -- [Customizations](#customizations) - - [Firewall policy rules factories](#firewall-policy-rules-factories) - - [NGFW Enterprise configuration](#ngfw-enterprise-configuration) -- [Files](#files) -- [Variables](#variables) -- [Outputs](#outputs) - - -## Design overview and choices - -- We create one security profile (and security profile group) per environment in the spoke VPCs only. That's usually where inspection is needed, as it's where workloads run. -- By default, we create NGFW Enterprise endpoints in three zones in the default, primary region (europe-west1). You can adapt this, depending on where your workloads run, using the dedicated variable. -- We install default firewall policy rules in each spoke, so that we allow and inspect all traffic going to the Internet and we allow egress towards RFC-1918 addresses. In ingress, you'll need to add your own rules. We provided some examples that need to be adapted to your topology (number of regions, subnets). -- We use global network firewall policies, as legacy VPC firewall rules are not compatible with NGFW Enterprise. These policies coexist with the legacy VPC firewall rules that we create in the netwroking stage. -- For your convenience, firewall policy rules leverage factories, so that you can define firewall policy rules using yaml files. The path of these files is configurable. Look in the [Customization](#customizations) section for more details. -- NGFW Enterprise endpoints are org-level resources that need to reference a quota project for billing purposes. By default, we create a dedicated `xxx-net-ngfw-0` quota project. Anyway, you can choose to leverage an existing project. Look in the [Customization](#customizations) section for more details. -- Firewall endpoint associations in this stage can reference TLS inspection policies created in the [2-security stage](../2-security/README.md). More info in the customization section of this document. -- While TLS inspection policies are created in the [2-security stage](../2-security/README.md), FAST still allows the service accounts of this stage and the `gcp-network-admins` group to create and manage them anywhere in the organization. - -## How to run this stage - -This stage is meant to be executed after any [networking](../2-networking-a-simple) stage has run and it leverages dedicated automation service accounts and a bucket created in the [resman](../1-resman) stage. - -It's to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the bootstrap and resman stages for the roles needed. - -Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. - -### Provider and Terraform variables - -As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. - -The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. - -```bash -../fast-links.sh ~/fast-config - -# File linking commands for network securoty (optional) stage - -# provider file -ln -s ~/fast-config/fast-test-00/providers/2-network-security-providers.tf ./ - -# input files from other stages -ln -s ~/fast-config/fast-test-00/tfvars/0-globals.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/tfvars/0-bootstrap.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/tfvars/1-resman.auto.tfvars.json ./ - -# conventional place for stage tfvars (manually created) -ln -s ~/fast-config/fast-test-00/2-network-security.auto.tfvars ./ - -# optional files -ln -s ~/fast-config/fast-test-00/2-networking.auto.tfvars.json ./ -ln -s ~/fast-config/fast-test-00/2-security.auto.tfvars.json ./ -``` - -```bash -../fast-links.sh gs://xxx-prod-iac-core-outputs-0 - -# File linking commands for network securoty (optional) stage - -# provider file -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/providers/2-network-security-providers.tf ./ - -# input files from other stages -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-bootstrap.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ - -# conventional place for stage tfvars (manually created) -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-network-security.auto.tfvars ./ - -# optional files -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-networking.auto.tfvars.json ./ -gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-security.auto.tfvars.json ./ -``` - -### Impersonating the automation service account - -The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. - -### Variable configuration - -Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: - -- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above -- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-bootstrap.auto.tfvars.json`, `1-resman.auto.tfvars.json` and `2-networking.auto.tfvars.json` files linked or copied above -- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file - -The latter set is explained in the [Customization](#customizations) sections below, and the full list can be found in the [Variables](#variables) table at the bottom of this document. - -Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../0-bootstrap/README.md#output-files-and-cross-stage-variables) for more details: - -```tfvars -outputs_location = "~/fast-config" -``` - -### Running the stage - -Once provider and variable values are in place and the correct user is configured, the stage can be run: - -```bash -terraform init -terraform apply -``` - -## Customizations - -You can optionally customize a few options adding a `terraform.tfvars` file to this stage. - -### Firewall policy rules factories - -By default, firewall policy rules yaml files are contained in the `data` folder within this module. Anyway, you can customize this location. - -### NGFW Enterprise configuration - -You can decide the zones where to deploy the NGFW Enterprise endpoints. These are set by default to `europe-west1-b`, `europe-west1-c` and `europe-west1-d`. - -```tfvars -ngfw_enterprise_config = { - endpoint_zones = [ - "us-east4-a", - "us-east4-b", - "australia-southeast1-b", - "australia-southeast1-c" - ] -} -``` - -Instead of creating a dedicated NGFW Enterprise billing/quota project, you can choose to leverage an existing project. These can even be one of your existing networking projects. -You'll need to make sure your network security service account can activate the `networksecurity.googleapis.com` on that project (for example, assigning the `roles/serviceusage.serviceUsageAdmin` role). - -```tfvars -ngfw_enterprise_config = { - quota_project_id = "your-quota-project-id" -} -``` - -You can optionally enable TLS inspection in stage [2-security](../2-security/README.md). -Ingesting outputs from [stage 2-security](../2-security/README.md), this stage will configure TLS inspection in NGFW Enterprise and will reference the CAs and the trust-configs you created in [stage 2-security](../2-security/README.md). -Make sure the CAs and the trusted configs created for NGFW Enterprise in the [2-security stage](../2-security/README.md) match the region where you defined your zonal firewall endpoints. - - - -## Files - -| name | description | modules | resources | -|---|---|---|---| -| [main.tf](./main.tf) | Next-Generation Firewall Enterprise configuration. | project | google_network_security_firewall_endpoint | -| [net-dev.tf](./net-dev.tf) | Security components for dev spoke VPC. | net-firewall-policy | google_network_security_firewall_endpoint_association · google_network_security_security_profile · google_network_security_security_profile_group | -| [net-prod.tf](./net-prod.tf) | Security components for prod spoke VPC. | net-firewall-policy | google_network_security_firewall_endpoint_association · google_network_security_security_profile · google_network_security_security_profile_group | -| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | -| [variables-fast.tf](./variables-fast.tf) | FAST stage interface. | | | -| [variables.tf](./variables.tf) | Module variables. | | | - -## Variables - -| name | description | type | required | default | producer | -|---|---|:---:|:---:|:---:|:---:| -| [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables-fast.tf#L40) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables-fast.tf#L82) | Organization details. | object({…}) | ✓ | | 00-globals | -| [prefix](variables-fast.tf#L92) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [vpc_self_links](variables-fast.tf#L102) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | -| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | -| [host_project_ids](variables-fast.tf#L51) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | -| [ngfw_enterprise_config](variables.tf#L35) | NGFW Enterprise configuration. | object({…}) | | {…} | | -| [ngfw_tls_configs](variables-fast.tf#L62) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | -| [outputs_location](variables.tf#L51) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | - -## Outputs - -| name | description | sensitive | consumers | -|---|---|:---:|---| -| [ngfw_enterprise_endpoint_ids](outputs.tf#L69) | The NGFW Enterprise endpoint ids. | | | -| [ngfw_enterprise_endpoints_quota_project](outputs.tf#L74) | The NGFW Enterprise endpoints quota project. | | | - diff --git a/fast/stages/2-network-security/data/cidrs.yaml b/fast/stages/2-network-security/data/cidrs.yaml deleted file mode 100644 index 3591e95a0..000000000 --- a/fast/stages/2-network-security/data/cidrs.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# skip boilerplate check ---- -# Terraform will be unable to decode this file if it does not contain valid YAML -# You can retain `---` (start of the document) to indicate an empty document. - -healthchecks: - - 35.191.0.0/16 - - 130.211.0.0/22 - - 209.85.152.0/22 - - 209.85.204.0/22 - -rfc1918: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - -onprem_probes: - - 10.255.255.254/32 diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml deleted file mode 100644 index b193b872c..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/dev/egress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# skip boilerplate check ---- -# start of document (---) avoids errors if the file only contains comments - -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -egress-allow-rfc1918: - description: "Allow all hosts to RFC-1918" - priority: 2147483546 - match: - destination_ranges: - - rfc1918 - action: allow - -egress-inspect-internet: - description: "Inspect egress traffic from all dev hosts to Internet" - priority: 2147483547 - match: - destination_ranges: - - "0.0.0.0/0" - action: "apply_security_profile_group" - security_profile_group: "dev" - # Uncomment the line below to enable TLS inspection for this egress rule - # tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml deleted file mode 100644 index 7feca48e6..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/dev/ingress.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# skip boilerplate check ---- - -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -# Sample NGFW Enterprise ingress rules to uncomment and customize as needed - -# ingress-allow-inspect-cross: -# description: "Allow and inspect cross-env traffic from prod." -# priority: 1 -# match: -# source_ranges: -# - prod (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "dev" -# tls_inspect: true - -# ingress-allow-inspect-intra: -# description: "Allow and inspect same-env (intra-vpc) traffic." -# priority: 2 -# match: -# source_ranges: -# - dev (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "dev" -# tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml deleted file mode 100644 index 527428a19..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/prod/egress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# skip boilerplate check ---- -egress-allow-rfc1918: - description: "Allow all hosts to RFC-1918" - priority: 2147483546 - match: - destination_ranges: - - rfc1918 - action: "allow" - -egress-inspect-internet: - description: "Inspect egress traffic from all prod hosts to Internet" - priority: 2147483547 - match: - destination_ranges: - - "0.0.0.0/0" - action: "apply_security_profile_group" - security_profile_group: "prod" - # Uncomment the line below to enable TLS inspection for this egress rule - # tls_inspect: true diff --git a/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml b/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml deleted file mode 100644 index 96f8d3ee3..000000000 --- a/fast/stages/2-network-security/data/firewall-policy-rules/prod/ingress.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# skip boilerplate check ---- -# yaml-language-server: $schema=../../../schemas/firewall-policy-rules.schema.json - -# Sample NGFW Enterprise ingress rules to uncomment and customize as needed - -# ingress-allow-inspect-cross: -# description: "Allow and inspect cross-env traffic." -# priority: 1 -# match: -# source_ranges: -# - dev (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "prod" -# tls_inspect: true - -# ingress-allow-inspect-intra: -# description: "Allow and inspect intra-VPC traffic." -# priority: 2 -# match: -# source_ranges: -# - prod (to be defined) -# action: "apply_security_profile_group" -# security_profile_group: "prod" -# tls_inspect: true diff --git a/fast/stages/2-network-security/diagram.png b/fast/stages/2-network-security/diagram.png deleted file mode 100644 index 9fb05873ae50935f52ba1c040ea3578813bb864b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24228 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 \ No newline at end of file diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf deleted file mode 100644 index 946dba43f..000000000 --- a/fast/stages/2-network-security/main.tf +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Next-Generation Firewall Enterprise configuration. - -locals { - create_quota_project = ( - var.ngfw_enterprise_config.quota_project_id == null - ? true - : false - ) - vpc_ids = { - for k, v in var.vpc_self_links - : k => replace(v, "https://www.googleapis.com/compute/v1/", "") - } -} - -# Dedicated quota project for ngfw enterprise endpoints -module "ngfw-quota-project" { - source = "../../../modules/project" - name = ( - local.create_quota_project - ? "net-ngfw-0" - : var.ngfw_enterprise_config.quota_project_id - ) - billing_account = ( - local.create_quota_project - ? var.billing_account.id - : null - ) - parent = ( - local.create_quota_project - ? var.folder_ids.networking - : null - ) - prefix = ( - local.create_quota_project - ? var.prefix - : null - ) - project_create = ( - local.create_quota_project - ? true - : false - ) - services = ["networksecurity.googleapis.com"] -} - -resource "google_network_security_firewall_endpoint" "firewall_endpoint" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-ngfw-endpoint-${each.key}" - parent = "organizations/${var.organization.id}" - location = each.value - billing_project_id = module.ngfw-quota-project.id -} diff --git a/fast/stages/2-network-security/net-dev.tf b/fast/stages/2-network-security/net-dev.tf deleted file mode 100644 index b068a8f6c..000000000 --- a/fast/stages/2-network-security/net-dev.tf +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Security components for dev spoke VPC. - -moved { - from = google_network_security_security_profile.dev_sec_profile - to = google_network_security_security_profile.dev -} - -resource "google_network_security_security_profile" "dev" { - name = "${var.prefix}-dev-sp-0" - type = "THREAT_PREVENTION" - parent = "organizations/${var.organization.id}" - location = "global" -} - -moved { - from = google_network_security_security_profile_group.dev_sec_profile_group - to = google_network_security_security_profile_group.dev -} - -resource "google_network_security_security_profile_group" "dev" { - name = "${var.prefix}-dev-spg-0" - parent = "organizations/${var.organization.id}" - location = "global" - description = "Dev security profile group." - threat_prevention_profile = try( - google_network_security_security_profile.dev.id, null - ) -} - -moved { - from = google_network_security_firewall_endpoint_association.dev_fw_ep_association - to = google_network_security_firewall_endpoint_association.dev -} - -resource "google_network_security_firewall_endpoint_association" "dev" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-dev-epa-${each.key}" - parent = "projects/${try(var.host_project_ids.dev-spoke-0, null)}" - location = each.value - firewall_endpoint = ( - google_network_security_firewall_endpoint.firewall_endpoint[each.key].id - ) - network = try(local.vpc_ids.dev-spoke-0, null) - # If TLS inspection is enabled, link the regional TLS inspection policy - tls_inspection_policy = ( - var.ngfw_tls_configs.tls_enabled - # TODO: make this try less verbose and more readable - ? try( - var.ngfw_tls_configs.tls_ip_ids_by_region.dev[substr(each.value, 0, length(each.value) - 2)], - null - ) - : null - ) -} - -module "dev-spoke-firewall-policy" { - source = "../../../modules/net-firewall-policy" - name = "${var.prefix}-dev-fw-policy" - parent_id = try(var.host_project_ids.dev-spoke-0, null) - region = "global" - security_profile_group_ids = { - dev = local.security_profile_group_ids.dev - } - attachments = { - dev-spoke = try(var.vpc_self_links.dev-spoke-0, null) - } - factories_config = { - cidr_file_path = var.factories_config.cidrs - egress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.dev}/egress.yaml" - ) - ingress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.dev}/ingress.yaml" - ) - } -} diff --git a/fast/stages/2-network-security/net-prod.tf b/fast/stages/2-network-security/net-prod.tf deleted file mode 100644 index 74d18ce1b..000000000 --- a/fast/stages/2-network-security/net-prod.tf +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description Security components for prod spoke VPC. - -moved { - from = google_network_security_security_profile.prod_sec_profile - to = google_network_security_security_profile.prod -} - -resource "google_network_security_security_profile" "prod" { - name = "${var.prefix}-prod-sp-0" - type = "THREAT_PREVENTION" - parent = "organizations/${var.organization.id}" - location = "global" -} - -moved { - from = google_network_security_security_profile_group.prod_sec_profile_group - to = google_network_security_security_profile_group.prod -} - -resource "google_network_security_security_profile_group" "prod" { - name = "${var.prefix}-prod-spg-0" - parent = "organizations/${var.organization.id}" - location = "global" - description = "prod security profile group." - threat_prevention_profile = try( - google_network_security_security_profile.prod.id, null - ) -} - -moved { - from = google_network_security_firewall_endpoint_association.prod_fw_ep_association - to = google_network_security_firewall_endpoint_association.prod -} - -resource "google_network_security_firewall_endpoint_association" "prod" { - for_each = toset(var.ngfw_enterprise_config.endpoint_zones) - name = "${var.prefix}-prod-epa-${each.key}" - parent = "projects/${try(var.host_project_ids.prod-spoke-0, null)}" - location = each.value - firewall_endpoint = ( - google_network_security_firewall_endpoint.firewall_endpoint[each.key].id - ) - network = try(local.vpc_ids.prod-spoke-0, null) - # If TLS inspection is enabled, link the regional TLS inspection policy - tls_inspection_policy = ( - var.ngfw_tls_configs.tls_enabled - # TODO: make this try less verbose and more readable - ? try( - var.ngfw_tls_configs.tls_ip_ids_by_region.prod[substr(each.value, 0, length(each.value) - 2)], - null - ) - : null - ) -} - -module "prod-spoke-firewall-policy" { - source = "../../../modules/net-firewall-policy" - name = "${var.prefix}-prod-fw-policy" - parent_id = try(var.host_project_ids.prod-spoke-0, null) - region = "global" - security_profile_group_ids = { - prod = local.security_profile_group_ids.prod - } - attachments = { - prod-spoke = try(var.vpc_self_links.prod-spoke-0, null) - } - factories_config = { - cidr_file_path = var.factories_config.cidrs - egress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.prod}/egress.yaml" - ) - ingress_rules_file_path = ( - "${var.factories_config.firewall_policy_rules.prod}/ingress.yaml" - ) - } -} diff --git a/fast/stages/2-network-security/outputs.tf b/fast/stages/2-network-security/outputs.tf deleted file mode 100644 index acd5944e2..000000000 --- a/fast/stages/2-network-security/outputs.tf +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -locals { - security_profile_group_ids = { - dev = format( - "//networksecurity.googleapis.com/%s", - try(google_network_security_security_profile_group.dev.id, "") - ) - prod = format( - "//networksecurity.googleapis.com/%s", - try(google_network_security_security_profile_group.prod.id, "") - ) - } - tfvars = { - association_ids = { - dev = { - for k, v in google_network_security_firewall_endpoint_association.dev : - k => v.id - } - prod = { - for k, v in google_network_security_firewall_endpoint_association.prod : - k => v.id - } - } - endpoint_ids = { - for _, v in google_network_security_firewall_endpoint.firewall_endpoint - : v.location => v.id - } - firewall_policy_ids = { - dev = module.dev-spoke-firewall-policy.id - prod = module.prod-spoke-firewall-policy.id - } - security_profile_group_ids = local.security_profile_group_ids - quota_project_id = module.ngfw-quota-project.id - } -} - -# generate tfvars file for subsequent stages - -resource "local_file" "tfvars" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - file_permission = "0644" - filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/2-nsec.auto.tfvars.json" - content = jsonencode(local.tfvars) -} - -resource "google_storage_bucket_object" "tfvars" { - bucket = var.automation.outputs_bucket - name = "tfvars/2-nsec.auto.tfvars.json" - content = jsonencode(local.tfvars) -} - -# outputs - -output "ngfw_enterprise_endpoint_ids" { - description = "The NGFW Enterprise endpoint ids." - value = local.tfvars.endpoint_ids -} - -output "ngfw_enterprise_endpoints_quota_project" { - description = "The NGFW Enterprise endpoints quota project." - value = module.ngfw-quota-project.id -} diff --git a/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json b/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json deleted file mode 120000 index e37a764d2..000000000 --- a/fast/stages/2-network-security/schemas/firewall-policy-rules.schema.json +++ /dev/null @@ -1 +0,0 @@ -../../../../modules/net-firewall-policy/schemas/firewall-policy-rules.schema.json \ No newline at end of file diff --git a/fast/stages/2-network-security/variables-fast.tf b/fast/stages/2-network-security/variables-fast.tf deleted file mode 100644 index 45fb1c0fb..000000000 --- a/fast/stages/2-network-security/variables-fast.tf +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# tfdoc:file:description FAST stage interface. - -variable "automation" { - # tfdoc:variable:source 0-bootstrap - description = "Automation resources created by the bootstrap stage." - type = object({ - outputs_bucket = string - }) -} - -variable "billing_account" { - # tfdoc:variable:source 0-bootstrap - description = "Billing account id. If billing account is not part of the same org set `is_org_level` to false." - type = object({ - id = string - is_org_level = optional(bool, true) - }) - validation { - condition = var.billing_account.is_org_level != null - error_message = "Invalid `null` value for `billing_account.is_org_level`." - } -} - -variable "folder_ids" { - # tfdoc:variable:source 1-resman - description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." - type = object({ - networking = string - networking-dev = string - networking-prod = string - }) - nullable = false -} - -variable "host_project_ids" { - # tfdoc:variable:source 2-networking - description = "Host project for the shared VPC." - type = object({ - dev-spoke-0 = optional(string) - prod-spoke-0 = optional(string) - }) - nullable = false - default = {} -} - -variable "ngfw_tls_configs" { - # tfdoc:variable:source 2-security - description = "The NGFW Enterprise TLS configurations." - type = object({ - tls_enabled = optional(bool, false) - tls_ip_ids_by_region = optional(object({ - dev = optional(map(string), {}) - prod = optional(map(string), {}) - })) - }) - nullable = false - default = { - tls_enabled = false - tls_ip_ids_by_region = { - dev = {} - prod = {} - } - } -} - -variable "organization" { - # tfdoc:variable:source 00-globals - description = "Organization details." - type = object({ - domain = string - id = number - customer_id = string - }) -} - -variable "prefix" { - # tfdoc:variable:source 0-bootstrap - description = "Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants." - type = string - validation { - condition = try(length(var.prefix), 0) < 12 - error_message = "Use a maximum of 9 chars for organizations, and 11 chars for tenants." - } -} - -variable "vpc_self_links" { - # tfdoc:variable:source 2-networking - description = "Self link for the shared VPC." - type = object({ - dev-spoke-0 = string - prod-spoke-0 = string - }) - nullable = false -} diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf deleted file mode 100644 index 94f00b6fe..000000000 --- a/fast/stages/2-network-security/variables.tf +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -variable "factories_config" { - description = "Configuration for network resource factories." - type = object({ - cidrs = optional(string, "data/cidrs.yaml") - firewall_policy_rules = optional(object({ - dev = string - prod = string - })) - }) - nullable = false - default = { - firewall_policy_rules = { - dev = "data/firewall-policy-rules/dev" - prod = "data/firewall-policy-rules/prod" - } - } -} - -variable "ngfw_enterprise_config" { - description = "NGFW Enterprise configuration." - type = object({ - endpoint_zones = list(string) - quota_project_id = optional(string, null) - }) - nullable = false - default = { - endpoint_zones = [ - "europe-west1-b", - "europe-west1-c", - "europe-west1-d" - ] - } -} - -variable "outputs_location" { - description = "Path where providers and tfvars files for the following stages are written. Leave empty to disable." - type = string - default = null -} diff --git a/fast/stages/2-networking-a-simple/README.md b/fast/stages/2-networking-a-simple/README.md index 760272d98..415390d5f 100644 --- a/fast/stages/2-networking-a-simple/README.md +++ b/fast/stages/2-networking-a-simple/README.md @@ -523,11 +523,11 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | sensitive | consumers | |---|---|:---:|---| -| [cloud_dns_inbound_policy](outputs.tf#L87) | IP Addresses for Cloud DNS inbound policy. | | | -| [host_project_ids](outputs.tf#L92) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L97) | Network project numbers. | | | -| [ping_commands](outputs.tf#L102) | Ping commands for test instances to be run to check VPC reachability. | | | -| [shared_vpc_self_links](outputs.tf#L107) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L112) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L118) | External IP Addresses for the GCP VPN gateways. | | | +| [cloud_dns_inbound_policy](outputs.tf#L88) | IP Addresses for Cloud DNS inbound policy. | | | +| [host_project_ids](outputs.tf#L93) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L98) | Network project numbers. | | | +| [ping_commands](outputs.tf#L103) | Ping commands for test instances to be run to check VPC reachability. | | | +| [shared_vpc_self_links](outputs.tf#L108) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L113) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L119) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-a-simple/outputs.tf b/fast/stages/2-networking-a-simple/outputs.tf index e38837c7f..092028f1d 100644 --- a/fast/stages/2-networking-a-simple/outputs.tf +++ b/fast/stages/2-networking-a-simple/outputs.tf @@ -55,6 +55,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-networking-b-nva/README.md b/fast/stages/2-networking-b-nva/README.md index 36bddf8c0..db6db9716 100644 --- a/fast/stages/2-networking-b-nva/README.md +++ b/fast/stages/2-networking-b-nva/README.md @@ -586,10 +586,10 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | sensitive | consumers | |---|---|:---:|---| -| [host_project_ids](outputs.tf#L106) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L111) | Network project numbers. | | | -| [ping_commands](outputs.tf#L116) | Ping commands for test instances to be run to check VPC reachability. | | | -| [shared_vpc_self_links](outputs.tf#L121) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L126) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L132) | External IP Addresses for the GCP VPN gateways. | | | +| [host_project_ids](outputs.tf#L107) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L112) | Network project numbers. | | | +| [ping_commands](outputs.tf#L117) | Ping commands for test instances to be run to check VPC reachability. | | | +| [shared_vpc_self_links](outputs.tf#L122) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L127) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L133) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-b-nva/outputs.tf b/fast/stages/2-networking-b-nva/outputs.tf index a143a2415..6969be18a 100644 --- a/fast/stages/2-networking-b-nva/outputs.tf +++ b/fast/stages/2-networking-b-nva/outputs.tf @@ -67,6 +67,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-networking-c-separate-envs/README.md b/fast/stages/2-networking-c-separate-envs/README.md index af512fe80..853a0a846 100644 --- a/fast/stages/2-networking-c-separate-envs/README.md +++ b/fast/stages/2-networking-c-separate-envs/README.md @@ -381,11 +381,11 @@ Regions are defined via the `regions` variable which sets up a mapping between t | name | description | sensitive | consumers | |---|---|:---:|---| -| [dev_cloud_dns_inbound_policy](outputs.tf#L79) | IP Addresses for Cloud DNS inbound policy for the dev environment. | | | -| [host_project_ids](outputs.tf#L84) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L89) | Network project numbers. | | | -| [prod_cloud_dns_inbound_policy](outputs.tf#L94) | IP Addresses for Cloud DNS inbound policy for the prod environment. | | | -| [shared_vpc_self_links](outputs.tf#L99) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L104) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L110) | External IP Addresses for the GCP VPN gateways. | | | +| [dev_cloud_dns_inbound_policy](outputs.tf#L80) | IP Addresses for Cloud DNS inbound policy for the dev environment. | | | +| [host_project_ids](outputs.tf#L85) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L90) | Network project numbers. | | | +| [prod_cloud_dns_inbound_policy](outputs.tf#L95) | IP Addresses for Cloud DNS inbound policy for the prod environment. | | | +| [shared_vpc_self_links](outputs.tf#L100) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L105) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L111) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-c-separate-envs/outputs.tf b/fast/stages/2-networking-c-separate-envs/outputs.tf index 22a667895..d6331ba50 100644 --- a/fast/stages/2-networking-c-separate-envs/outputs.tf +++ b/fast/stages/2-networking-c-separate-envs/outputs.tf @@ -48,6 +48,7 @@ locals { tfvars = { host_project_ids = local.host_project_ids host_project_numbers = local.host_project_numbers + regions = var.regions subnet_self_links = local.subnet_self_links subnet_proxy_only_self_links = local.subnet_proxy_only_self_links subnet_psc_self_links = local.subnet_psc_self_links diff --git a/fast/stages/2-security/.fast-stage.env b/fast/stages/2-security/.fast-stage.env index d174c2162..9e157e378 100644 --- a/fast/stages/2-security/.fast-stage.env +++ b/fast/stages/2-security/.fast-stage.env @@ -2,4 +2,3 @@ FAST_STAGE_DESCRIPTION="security" FAST_STAGE_LEVEL=2 FAST_STAGE_NAME=security FAST_STAGE_DEPS="0-globals 0-bootstrap 1-resman" -FAST_STAGE_OPTIONAL="2-nsec" \ No newline at end of file diff --git a/fast/stages/2-security/README.md b/fast/stages/2-security/README.md index 318dfd68e..981f1082e 100644 --- a/fast/stages/2-security/README.md +++ b/fast/stages/2-security/README.md @@ -16,8 +16,6 @@ The following diagram illustrates the high-level design of resources managed her - [Design overview and choices](#design-overview-and-choices) - [Cloud KMS](#cloud-kms) - [Certificate Authority Service (CAS)](#certificate-authority-service-cas) - - [Trust Configs](#trust-configs) - - [NGFW Enterprise and TLS inspection support](#ngfw-enterprise-and-tls-inspection-support) - [How to run this stage](#how-to-run-this-stage) - [Provider and Terraform variables](#provider-and-terraform-variables) - [Impersonating the automation service account](#impersonating-the-automation-service-account) @@ -50,19 +48,7 @@ IAM roles on keys can be configured at the logical level for all locations where ### Certificate Authority Service (CAS) -With this stage you can leverage Certificate Authority Services (CAS) and create as many CAs you need for each environments. To create custom CAS, you can use the `cas_configs` variable. The variable comes with some defaults, useful for demos: in each environment, specifying the CA `location` should be enough for most of your test scenarios. - -### Trust Configs - -The stage lets you also create Certificate Manager trust configs. With trust configs you can trust whole CAs or specific server certificates, when you use them with other services, such as NGFW Enterprise. You can create additional trust configs for each environment with the `trust_configs` variable. At a very minimum, each trust config needs a `location` (the region) and either a `trust_stores` block or an `allowed_certificates` block. - -### NGFW Enterprise and TLS inspection support - -We deploy NGFW Enterprise in the [network security stage](../2-network-security/README.md). If you require TLS inspection, NGFW needs to interact with CAS and -optionally- Certificate Manager trust-configs. These components bind to firewall endpoint associations (created in the network security stage) with zonal TLS inspection policies. -Using this module, you can define CAS configurations and trust-configs for NGFW Enterprise. You can create them using the `cas_configs` and `trust_configs` variables. Anyway, these will need to use specific keys (defined in `ngfw_tls_configs.keys`), so that FAST knows which configurations to use for NGFW Enterprise. -You can then enable TLS inspection and customize its behavior for NGFW Enterprise, using the `ngfw_tls_configs.tls_inspection` variable. FAST will create the TLS inspection policies for you in the regions where you defined your CAs for NGFW Enterprise. -When you create your CAs and trust-configs for NGFW Enterprise, make sure their region matches the zones where you will define your firewall endpoints. -You can read more about NGFW configurations in the [Customizations section](#customizations) of this document. +With this stage you can leverage Certificate Authority Services (CAS) and create as many CAs you need for each environments. To create custom CAS, you can use the `certificate_authorities` variable. ## How to run this stage @@ -286,7 +272,7 @@ tls_inspection = { | name | description | modules | resources | |---|---|---|---| -| [certs.tf](./certs.tf) | Per-environment certificate resources. | certificate-authority-service | google_certificate_manager_trust_config | +| [cas.tf](./cas.tf) | Per-environment certificate resources. | certificate-authority-service | | | [kms.tf](./kms.tf) | Per-environment KMS. | | | | [main.tf](./main.tf) | Module-level locals and resources. | folder · project | | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | @@ -302,21 +288,19 @@ tls_inspection = { | [environments](variables-fast.tf#L47) | Environment names. | map(object({…})) | ✓ | | 0-globals | | [folder_ids](variables-fast.tf#L65) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | | [prefix](variables-fast.tf#L75) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [certificate_authorities](variables.tf#L17) | Certificate Authority Service CAs. If environments is null the CA is created in all environments. | map(object({…})) | | {} | | +| [certificate_authorities](variables.tf#L17) | Certificate Authority Service pool and CAs. If environments is null identical pools and CAs are created in all environments. | map(object({…})) | | {} | | | [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [essential_contacts](variables.tf#L91) | Email used for essential contacts, unset if null. | string | | null | | -| [kms_keys](variables.tf#L97) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | -| [outputs_location](variables.tf#L162) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [essential_contacts](variables.tf#L94) | Email used for essential contacts, unset if null. | string | | null | | +| [kms_keys](variables.tf#L100) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | +| [outputs_location](variables.tf#L138) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | | [stage_config](variables-fast.tf#L85) | FAST stage configuration. | object({…}) | | {} | 1-resman | | [tag_values](variables-fast.tf#L99) | Root-level tag values. | map(string) | | {} | 1-resman | -| [trust_configs](variables.tf#L168) | The Certificate Manager trust configs. If environments is null the trust config is created in all environments. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [cas_configs](outputs.tf#L57) | Certificate Authority Service configurations. | | | -| [kms_keys](outputs.tf#L62) | KMS key ids. | | | -| [tfvars](outputs.tf#L67) | Terraform variable files for the following stages. | ✓ | | -| [trust_config_ids](outputs.tf#L73) | Certificate Manager trust-config ids. | | | +| [certificate_authority_pools](outputs.tf#L53) | Certificate Authority Service pools and CAs. | | | +| [kms_keys](outputs.tf#L58) | KMS key ids. | | | +| [tfvars](outputs.tf#L63) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/2-security/cas.tf b/fast/stages/2-security/cas.tf new file mode 100644 index 000000000..10116e884 --- /dev/null +++ b/fast/stages/2-security/cas.tf @@ -0,0 +1,42 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Per-environment certificate resources. + +locals { + cas = flatten([ + for k, v in var.certificate_authorities : [ + for e in coalesce(v.environments, keys(var.environments)) : merge(v, { + environment = e + key = "${e}-${k}" + name = k + }) + ] + ]) +} + +module "cas" { + source = "../../../modules/certificate-authority-service" + for_each = { for k in local.cas : k.key => k } + project_id = module.project[each.value.environment].project_id + ca_configs = each.value.ca_configs + ca_pool_config = each.value.ca_pool_config + iam = each.value.iam + iam_bindings = each.value.iam_bindings + iam_bindings_additive = each.value.iam_bindings_additive + iam_by_principals = each.value.iam_by_principals + location = each.value.location +} diff --git a/fast/stages/2-security/main.tf b/fast/stages/2-security/main.tf index 50ff5625d..644f5b4ab 100644 --- a/fast/stages/2-security/main.tf +++ b/fast/stages/2-security/main.tf @@ -28,8 +28,8 @@ locals { project_services = [ "certificatemanager.googleapis.com", "cloudkms.googleapis.com", - "networkmanagement.googleapis.com", - "networksecurity.googleapis.com", + # "networkmanagement.googleapis.com", + # "networksecurity.googleapis.com", "privateca.googleapis.com", "secretmanager.googleapis.com", "stackdriver.googleapis.com" diff --git a/fast/stages/2-security/outputs.tf b/fast/stages/2-security/outputs.tf index efb6ebee6..75014ae8e 100644 --- a/fast/stages/2-security/outputs.tf +++ b/fast/stages/2-security/outputs.tf @@ -24,20 +24,16 @@ locals { ] ]) tfvars = { - cas_configs = { + certificate_authority_pools = { for k, v in module.cas : k => { - ca_pool_id = v.ca_pool_id - ca_ids = v.ca_ids - location = v.ca_pool.location + ca_ids = v.ca_ids + id = v.ca_pool_id + location = v.ca_pool.location } } kms_keys = { for k in local._output_kms_keys : k.key => k.id } - trust_configs = { - for k, v in google_certificate_manager_trust_config.default : - k => v.id - } } } @@ -54,9 +50,9 @@ resource "google_storage_bucket_object" "tfvars" { content = jsonencode(local.tfvars) } -output "cas_configs" { - description = "Certificate Authority Service configurations." - value = local.tfvars.cas_configs +output "certificate_authority_pools" { + description = "Certificate Authority Service pools and CAs." + value = local.tfvars.certificate_authority_pools } output "kms_keys" { @@ -69,8 +65,3 @@ output "tfvars" { sensitive = true value = local.tfvars } - -output "trust_config_ids" { - description = "Certificate Manager trust-config ids." - value = local.tfvars.trust_configs -} diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index c6a78f99b..2d25672c6 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -15,9 +15,14 @@ */ variable "certificate_authorities" { - description = "Certificate Authority Service CAs. If environments is null the CA is created in all environments." + description = "Certificate Authority Service pool and CAs. If environments is null identical pools and CAs are created in all environments." type = map(object({ - environments = optional(list(string)) + location = string + environments = optional(list(string)) + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(any), {}) + iam_bindings_additive = optional(map(any), {}) + iam_by_principals = optional(map(list(string)), {}) ca_configs = map(object({ deletion_protection = optional(string, true) type = optional(string, "SELF_SIGNED") @@ -49,19 +54,22 @@ variable "certificate_authorities" { server_auth = optional(bool, true) time_stamping = optional(bool, false) }), {}) - subject = optional(object({ - common_name = string - organization = string - country_code = optional(string) - locality = optional(string) - organizational_unit = optional(string) - postal_code = optional(string) - province = optional(string) - street_address = optional(string) - }), { - common_name = "test.example.com" - organization = "Test Example" - }) + subject = optional( + object({ + common_name = string + organization = string + country_code = optional(string) + locality = optional(string) + organizational_unit = optional(string) + postal_code = optional(string) + province = optional(string) + street_address = optional(string) + }), + { + common_name = "test.example.com" + organization = "Test Example" + } + ) subject_alt_name = optional(object({ dns_names = optional(list(string), null) email_addresses = optional(list(string), null) @@ -78,11 +86,6 @@ variable "certificate_authorities" { name = optional(string, null) tier = optional(string, "DEVOPS") }) - location = string - iam = optional(map(list(string)), {}) - iam_bindings = optional(map(any), {}) - iam_bindings_additive = optional(map(any), {}) - iam_by_principals = optional(map(list(string)), {}) })) nullable = false default = {} @@ -132,51 +135,8 @@ variable "kms_keys" { nullable = false } -# move to netsec -# variable "tls_inspection_policies" { -# description = "The CAS and trust configurations key names to be used for NGFW Enterprise." -# type = object({ -# keys = optional(object({ -# dev = optional(object({ -# cas = optional(list(string), ["ngfw-dev-cas-0"]) -# trust_configs = optional(list(string), ["ngfw-dev-tc-0"]) -# }), {}) -# prod = optional(object({ -# cas = optional(list(string), ["ngfw-prod-cas-0"]) -# trust_configs = optional(list(string), ["ngfw-prod-tc-0"]) -# }), {}) -# }), {}) -# tls_inspection = optional(object({ -# enabled = optional(bool, false) -# exclude_public_ca_set = optional(bool, false) -# min_tls_version = optional(string, "TLS_1_0") -# }), {}) -# }) -# nullable = false -# default = { -# dev = {} -# prod = {} -# } -# } - variable "outputs_location" { description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable." type = string default = null } - -variable "trust_configs" { - description = "The Certificate Manager trust configs. If environments is null the trust config is created in all environments." - type = map(object({ - location = string - description = optional(string) - environments = optional(list(string)) - allowlisted_certificates = optional(map(string), {}) - trust_stores = optional(map(object({ - intermediate_cas = optional(map(string), {}) - trust_anchors = optional(map(string), {}) - })), {}) - })) - nullable = false - default = {} -} diff --git a/fast/stages/README.md b/fast/stages/README.md index 6a30dcb01..c0ed1d6fb 100644 --- a/fast/stages/README.md +++ b/fast/stages/README.md @@ -17,6 +17,8 @@ To achieve this, we rely on specific GCP functionality like [delegated role gran Refer to each stage's documentation for a detailed description of its purpose, the architectural choices made in its design, and how it can be configured and wired together to terraform a whole GCP organization. The following is a brief overview of each stage. +Stages encapsulate core designs and functionality that is common in most type of GCP organization set-ups. Specialized designs or additional configurations that add specific functionality on top of stages to meet very specific use cases are defined via [add-ons](../addons/). + To destroy a previous FAST deployment follow the instructions detailed in [cleanup](CLEANUP.md). ## Organization (0 and 1) @@ -32,7 +34,7 @@ To destroy a previous FAST deployment follow the instructions detailed in [clean ## Multitenancy -Implemented as an [add-on stage 1](./1-tenant-factory/), with optional FAST compatibility for tenants. +Implemented as an [add-on stage 1](../addons/1-resman-tenants/), with optional FAST compatibility for tenants. ## Shared resources (2) @@ -44,7 +46,6 @@ Implemented as an [add-on stage 1](./1-tenant-factory/), with optional FAST comp Exports: host project ids and numbers, vpc self links - [Project Factory](./2-project-factory/) YAML-based factory to create and configure application or team-level projects. Configuration includes VPC-level settings for Shared VPC, service-level configuration for CMEK encryption via centralized keys, and service account creation for workloads and applications. This stage can be cloned if an org-wide or dedicated per-environment factories are needed. -- [Network Security](./2-network-security/) Optional stage that integrates with security and networking stages to manage a centralized [NGFW Enterprise](https://cloud.google.com/firewall/docs/about-firewalls) deployment. ## Environment-level resources (3) diff --git a/tests/fast/__init__.py b/tests/fast/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/__init__.py +++ b/tests/fast/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s1_tenant_factory/__init__.py b/tests/fast/addons/__init__.py similarity index 95% rename from tests/fast/stages/s1_tenant_factory/__init__.py rename to tests/fast/addons/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s1_tenant_factory/__init__.py +++ b/tests/fast/addons/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_network_security/tftest.yaml b/tests/fast/addons/a1_resman_tenants/__init__.py similarity index 85% rename from tests/fast/stages/s2_network_security/tftest.yaml rename to tests/fast/addons/a1_resman_tenants/__init__.py index 05246b4eb..c37e93b74 100644 --- a/tests/fast/stages/s2_network_security/tftest.yaml +++ b/tests/fast/addons/a1_resman_tenants/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,9 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -module: fast/stages/2-network-security/ - -tests: - simple: - tls: diff --git a/tests/fast/stages/s1_tenant_factory/simple.tfvars b/tests/fast/addons/a1_resman_tenants/simple.tfvars similarity index 93% rename from tests/fast/stages/s1_tenant_factory/simple.tfvars rename to tests/fast/addons/a1_resman_tenants/simple.tfvars index afac7a3e8..a1a588c70 100644 --- a/tests/fast/stages/s1_tenant_factory/simple.tfvars +++ b/tests/fast/addons/a1_resman_tenants/simple.tfvars @@ -23,6 +23,20 @@ custom_roles = { storage_viewer = "organizations/123456789012/roles/storageViewer" tenant_network_admin = "organizations/123456789012/roles/tenantNetworkAdmin" } +environments = { + dev = { + is_default = false + name = "Development" + short_name = "dev" + tag_name = "development" + } + prod = { + is_default = true + name = "Production" + short_name = "prod" + tag_name = "production" + } +} groups = { gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", diff --git a/tests/fast/stages/s1_tenant_factory/simple.yaml b/tests/fast/addons/a1_resman_tenants/simple.yaml similarity index 100% rename from tests/fast/stages/s1_tenant_factory/simple.yaml rename to tests/fast/addons/a1_resman_tenants/simple.yaml diff --git a/tests/fast/stages/s1_tenant_factory/tftest.yaml b/tests/fast/addons/a1_resman_tenants/tftest.yaml similarity index 94% rename from tests/fast/stages/s1_tenant_factory/tftest.yaml rename to tests/fast/addons/a1_resman_tenants/tftest.yaml index cfac5807d..e59f77706 100644 --- a/tests/fast/stages/s1_tenant_factory/tftest.yaml +++ b/tests/fast/addons/a1_resman_tenants/tftest.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -module: fast/stages/1-tenant-factory +module: fast/addons/1-resman-tenants tests: simple: diff --git a/tests/fast/stages/s2_network_security/__init__.py b/tests/fast/addons/a2_networking_ngfw/__init__.py similarity index 100% rename from tests/fast/stages/s2_network_security/__init__.py rename to tests/fast/addons/a2_networking_ngfw/__init__.py diff --git a/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem new file mode 100644 index 000000000..be23688f3 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +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 +6tHmve7JLOKynHcIhypua18aX64w +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem new file mode 100644 index 000000000..a846366b9 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIGmjCCBIKgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZsxCzAJBgNVBAYTAklU +MQ8wDQYDVQQIDAZNaWxhbm8xEjAQBgNVBAoMCUZhc3QgVGVzdDEVMBMGA1UECwwM +RmFzdCBUZXN0IENBMSIwIAYDVQQDDBlGYXN0IFRlc3QgSW50ZXJtZWRpYXRlIENB +MSwwKgYJKoZIhvcNAQkBFh1mYWJyaWMtZmFzdC1vd25lcnNAZ29vZ2xlLmNvbTAe +Fw0yNTAxMDUxNzU0NTNaFw0yNjAxMTUxNzU0NTNaMIGeMQswCQYDVQQGEwJJVDEP +MA0GA1UECAwGTWlsYW5vMQ8wDQYDVQQHDAZNaWxhbm8xEjAQBgNVBAoMCUZhc3Qg +VGVzdDEVMBMGA1UECwwMRmFzdCBUZXN0IENBMRQwEgYDVQQDDAtleGFtcGxlLmNv +bTEsMCoGCSqGSIb3DQEJARYdZmFicmljLWZhc3Qtb3duZXJzQGdvb2dsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBsjW38EG4Id6MqO/bQXXV +DaA4LbP+Eu9ekSQcIkddFGvC9+Jv0O8YC9t+FTL0cMPZDwne2fqPsk5skR2xTYFA +JJKsz6NfuSSpZYRiWmfZXKF/c2u2BBmYkBgv1MiJrM/Oum1t98QbIn5y2apS0wDw +aGmVJ/aT0SeR46zyPbXi9Dx5NqbZQmfdftmNNpDKLg68hZ42HsGcqBjn9dIiKdJL +98LQnBUzSxENAfrQbzii3bm274n7M1Nodnf00tX341IdFWgbuK+zIM4FinrSigv4 +LTzebF2ow7EhrA386zjEeHsTn8gVFVak/RhJgYbZI4MMoxgotH5/1maJVPVwCSSh +AgMBAAGjggHhMIIB3TAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglg +hkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRl +MB0GA1UdDgQWBBR+TcFut49DkNpPPmsa6JOLpNdsKjCB1QYDVR0jBIHNMIHKgBQ8 +GKPN8aSuhtdfzTMBY06se4nyN6GBraSBqjCBpzEdMBsGA1UEAwwURmFzdCBFeGFt +cGxlIFJvb3QgQ0ExCzAJBgNVBAYTAklUMQ8wDQYDVQQIDAZNaWxhbm8xDzANBgNV +BAcMBk1pbGFubzESMBAGA1UECgwJRmFzdCBUZXN0MRUwEwYDVQQLDAxGYXN0IFRl +c3QgQ0ExLDAqBgkqhkiG9w0BCQEWHWZhYnJpYy1mYXN0LW93bmVyc0Bnb29nbGUu +Y29tggIQADAOBgNVHQ8BAf8EBAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwNAYI +KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcDIuZXhhbXBsZS5j +b20wNgYDVR0RBC8wLYILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYINbS5l +eGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAk5aNZU32TarWOJRWaFQXCHSC +o9m5LCK+nyE1a2r4XeuacYc1wXEFJubBSRc6Ms5pSuEZw0zil4FEJXvaJNURqh+K +40CaiLPOkB6N/YiF4xuQHJ4lXHrGP/d2QkOp+jX0Ic/jwpQ27y0dcFKLjIgnEZub +LEhwIffwI+dWE6LnczcAj37c3+bmRE5euf71vaZy749dv1aDeR5CO7wOK7vsZZXV +/h6npnZrWQ6CsZ8xW69L29sKsZlx33VD+Lu6dNS+Nc2EXYC3WtGNeHrlJk1wy5AV +uebVKbsw94wb53h0NP8TNQrClWmz5mImyNzGPmNJ74l6Q508gppxWdjFqPWIfAnR +FK6dW7zkLAJcXcr3qHcG+KuwoxZFKI2NBTdJFPSNqil3RCSOY5ZaWN6ZroXGn3e3 +suGXBh3s53uLSMS5WsY+LoAZ8DPcbCB9LIvxHj2JnFaBl8xpy+Nys8+5CI3RxgMq +jq3gz3pSz8/JuXKpb4lCofSgVsWN5iycm8Hz23b2H2mlgoT4WRPGl+aLMeDwBFkW +1kPS02POvTf0n2MEUAwmbHEfH6b9cY62A9g8OrtKj72ItNyJPELRZKZZVOsyRutn +CMi5xDJ8NdOl3XN7TOwSazzhPbr7usZp4DPrJIaG5POaPHUmThSPsD1VoctaL23W +OkP+gNET6MuCRvTk0EA= +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem b/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem new file mode 100644 index 000000000..cfb44fe38 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/tests/fast/addons/a2_networking_ngfw/simple.tfvars b/tests/fast/addons/a2_networking_ngfw/simple.tfvars new file mode 100644 index 000000000..ad6a203d6 --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/simple.tfvars @@ -0,0 +1,87 @@ +_fast_debug = { + skip_datasources = true +} +automation = { + outputs_bucket = "test" +} +certificate_authorities = { + ngfw-0 = { + location = "europe-west8" + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "fast.example.com" + organization = "FAST Test" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + } +} +ngfw_config = { + name = "ngfw-0" + endpoint_zones = ["europe-west8-b"] + network_associations = { + prod = { + vpc_id = "projects/xxx-prod-net-spoke-0/global/networks/prod-spoke-0" + tls_inspection_policy = "ngfw-0" + } + } +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +project_id = "xxx-prod-net-landing-0" +security_profiles = { + ngfw-0 = { + threat_prevention_profile = { + severity_overrides = { + informational-allow = { + action = "ALLOW" + severity = "INFORMATIONAL" + } + } + threat_overrides = { + allow-280647 = { + action = "ALLOW" + threat_id = "280647" + } + } + } + } +} +tls_inspection_policies = { + ngfw-0 = { + ca_pool_id = "ngfw-0" + location = "europe-west8" + trust_config = "ngfw-0" + } +} +trust_configs = { + ngfw-0 = { + location = "europe-west8" + allowlisted_certificates = { + server-0 = "../../../tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem" + } + trust_stores = { + ludo-joonix = { + intermediate_cas = { + issuing-ca-1 = "../../../tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem" + } + trust_anchors = { + root-ca-1 = "../../../tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem" + } + } + } + } +} +vpc_self_links = { + dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" + prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" +} diff --git a/tests/fast/stages/s2_network_security/simple.yaml b/tests/fast/addons/a2_networking_ngfw/simple.yaml similarity index 56% rename from tests/fast/stages/s2_network_security/simple.yaml rename to tests/fast/addons/a2_networking_ngfw/simple.yaml index c0ff76a2d..8fa8ebc28 100644 --- a/tests/fast/stages/s2_network_security/simple.yaml +++ b/tests/fast/addons/a2_networking_ngfw/simple.yaml @@ -13,16 +13,14 @@ # limitations under the License. counts: - google_compute_network_firewall_policy: 2 - google_compute_network_firewall_policy_association: 2 - google_compute_network_firewall_policy_rule: 4 - google_network_security_firewall_endpoint: 3 - google_network_security_firewall_endpoint_association: 6 - google_network_security_security_profile: 2 - google_network_security_security_profile_group: 2 - google_project: 1 - google_project_service: 1 - google_project_service_identity: 1 + google_certificate_manager_trust_config: 1 + google_network_security_firewall_endpoint: 1 + google_network_security_firewall_endpoint_association: 1 + google_network_security_security_profile: 1 + google_network_security_security_profile_group: 1 + google_network_security_tls_inspection_policy: 1 + google_privateca_ca_pool: 1 + google_privateca_certificate_authority: 1 google_storage_bucket_object: 1 - modules: 3 - resources: 25 + modules: 1 + resources: 9 diff --git a/tests/fast/addons/a2_networking_ngfw/tftest.yaml b/tests/fast/addons/a2_networking_ngfw/tftest.yaml new file mode 100644 index 000000000..bc7491a5a --- /dev/null +++ b/tests/fast/addons/a2_networking_ngfw/tftest.yaml @@ -0,0 +1,22 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module: fast/addons/2-networking-ngfw/ + +tests: + simple: + extra_files: + - ../../../tests/fast/addons/a2_networking_ngfw/data/ca.cert.pem + - ../../../tests/fast/addons/a2_networking_ngfw/data/example.com.cert.pem + - ../../../tests/fast/addons/a2_networking_ngfw/data/intermediate.cert.pem diff --git a/tests/fast/stages/__init__.py b/tests/fast/stages/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/__init__.py +++ b/tests/fast/stages/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/__init__.py b/tests/fast/stages/s0_bootstrap/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s0_bootstrap/__init__.py +++ b/tests/fast/stages/s0_bootstrap/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/cicd.tfvars b/tests/fast/stages/s0_bootstrap/cicd.tfvars new file mode 100644 index 000000000..4e6490266 --- /dev/null +++ b/tests/fast/stages/s0_bootstrap/cicd.tfvars @@ -0,0 +1,58 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +outputs_location = "/fast-config" +prefix = "fast" +cicd_config = { + bootstrap = { + identity_provider = "gh-test" + repository = { + name = "fast/bootstrap" + type = "github" + branch = "main" + } + } + resman = { + identity_provider = "gl-test" + repository = { + name = "fast/resource_management" + type = "gitlab" + branch = "main" + } + } +} +fast_addon = { + resman-tenants = { + parent_stage = "1-resman" + cicd_config = { + identity_provider = "gh-test" + repository = { + name = "fast/tenants" + type = "github" + branch = "main" + } + } + } +} +workload_identity_providers = { + gh-test = { + attribute_condition = "attribute.repository_owner==\"fast\"" + issuer = "github" + } + gl-test = { + attribute_condition = "attribute.namespace_path==\"fast\"" + issuer = "gitlab" + } +} diff --git a/tests/fast/stages/s0_bootstrap/cicd.yaml b/tests/fast/stages/s0_bootstrap/cicd.yaml new file mode 100644 index 000000000..30259d6b7 --- /dev/null +++ b/tests/fast/stages/s0_bootstrap/cicd.yaml @@ -0,0 +1,359 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +values: + google_iam_workload_identity_pool.default[0]: + description: null + disabled: null + display_name: null + project: fast-prod-iac-core-0 + timeouts: null + workload_identity_pool_id: fast-bootstrap + google_iam_workload_identity_pool_provider.default["gh-test"]: + attribute_condition: attribute.repository_owner=="fast" + attribute_mapping: + attribute.actor: assertion.actor + attribute.fast_sub: '"repo:" + assertion.repository + ":ref:" + assertion.ref' + attribute.ref: assertion.ref + attribute.repository: assertion.repository + attribute.repository_owner: assertion.repository_owner + attribute.sub: assertion.sub + google.subject: assertion.sub + aws: [] + description: null + disabled: null + display_name: null + oidc: + - allowed_audiences: [] + issuer_uri: https://token.actions.githubusercontent.com + jwks_json: null + project: fast-prod-iac-core-0 + saml: [] + timeouts: null + workload_identity_pool_id: fast-bootstrap + workload_identity_pool_provider_id: fast-bootstrap-gh-test + x509: [] + google_iam_workload_identity_pool_provider.default["gl-test"]: + attribute_condition: attribute.namespace_path=="fast" + attribute_mapping: + attribute.environment: assertion.environment + attribute.environment_protected: assertion.environment_protected + attribute.namespace_id: assertion.namespace_id + attribute.namespace_path: assertion.namespace_path + attribute.pipeline_id: assertion.pipeline_id + attribute.pipeline_source: assertion.pipeline_source + attribute.project_id: assertion.project_id + attribute.project_path: assertion.project_path + attribute.ref: assertion.ref + attribute.ref_protected: assertion.ref_protected + attribute.ref_type: assertion.ref_type + attribute.repository: assertion.project_path + attribute.sub: assertion.sub + google.subject: assertion.sub + aws: [] + description: null + disabled: null + display_name: null + oidc: + - allowed_audiences: [] + issuer_uri: https://gitlab.com + jwks_json: null + project: fast-prod-iac-core-0 + saml: [] + timeouts: null + workload_identity_pool_id: fast-bootstrap + workload_identity_pool_provider_id: fast-bootstrap-gl-test + x509: [] + google_storage_bucket_object.workflows["0-bootstrap"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/0-bootstrap-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + google_storage_bucket_object.workflows["1-resman"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/1-resman-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + google_storage_bucket_object.workflows["1-resman-tenants"]: + bucket: fast-prod-iac-core-outputs-0 + cache_control: null + content_disposition: null + content_encoding: null + content_language: null + customer_encryption: [] + detect_md5hash: different hash + event_based_hold: null + metadata: null + name: workflows/1-resman-tenants-workflow.yaml + retention: [] + source: null + temporary_hold: null + timeouts: null + module.automation-tf-bootstrap-r-sa.google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-0r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform organization bootstrap service account (read-only). + email: fast-prod-bootstrap-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-bootstrap-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] + : condition: [] + members: + - serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-bootstrap-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: organizations/123456789012/roles/storageViewer + module.automation-tf-bootstrap-sa.google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-0 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform organization bootstrap service account. + email: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-bootstrap-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-bootstrap-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.admin + ? module.automation-tf-cicd-r-sa["bootstrap"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["bootstrap"].google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD bootstrap service account (read-only). + email: fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["bootstrap"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["bootstrap"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-r-sa["resman"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["resman"].google_service_account.service_account[0]: + account_id: fast-prod-resman-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman service account (read-only). + email: fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["resman"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account.service_account[0]: + account_id: fast-prod-resman-tenants-1r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman-tenants service account (read-only). + email: fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-r-sa["resman-tenants"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["bootstrap"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["bootstrap"].google_service_account.service_account[0]: + account_id: fast-prod-bootstrap-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD bootstrap service account. + email: fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + ? module.automation-tf-cicd-sa["bootstrap"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"] + : condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["bootstrap"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["resman"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["resman"].google_service_account.service_account[0]: + account_id: fast-prod-resman-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman service account. + email: fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-cicd-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]: + condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["resman"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + ? module.automation-tf-cicd-sa["resman-tenants"].google_project_iam_member.project-roles["fast-prod-iac-core-0-roles/logging.logWriter"] + : condition: [] + project: fast-prod-iac-core-0 + role: roles/logging.logWriter + module.automation-tf-cicd-sa["resman-tenants"].google_service_account.service_account[0]: + account_id: fast-prod-resman-tenants-1 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform CI/CD resman-tenants service account. + email: fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-cicd-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]: + condition: [] + role: roles/iam.workloadIdentityUser + ? module.automation-tf-cicd-sa["resman-tenants"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.objectViewer + module.automation-tf-resman-r-sa.google_service_account.service_account[0]: + account_id: fast-prod-resman-0r + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform stage 1 resman service account (read-only). + email: fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-resman-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + - serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-resman-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: organizations/123456789012/roles/storageViewer + module.automation-tf-resman-sa.google_service_account.service_account[0]: + account_id: fast-prod-resman-0 + create_ignore_already_exists: null + description: null + disabled: false + display_name: Terraform stage 1 resman service account. + email: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com + member: serviceAccount:fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com + project: fast-prod-iac-core-0 + timeouts: null + module.automation-tf-resman-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: + condition: [] + members: + - serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com + - serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com + role: roles/iam.serviceAccountTokenCreator + ? module.automation-tf-resman-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"] + : bucket: fast-prod-iac-core-outputs-0 + condition: [] + role: roles/storage.admin + +counts: + google_bigquery_dataset: 1 + google_bigquery_default_service_account: 3 + google_essential_contacts_contact: 3 + google_iam_workload_identity_pool: 1 + google_iam_workload_identity_pool_provider: 2 + google_logging_organization_settings: 1 + google_logging_organization_sink: 4 + google_logging_project_bucket_config: 4 + google_org_policy_policy: 24 + google_organization_iam_binding: 27 + google_organization_iam_custom_role: 13 + google_organization_iam_member: 29 + google_project: 3 + google_project_iam_audit_config: 1 + google_project_iam_binding: 19 + google_project_iam_member: 22 + google_project_service: 31 + google_project_service_identity: 7 + google_service_account: 12 + google_service_account_iam_binding: 12 + google_service_account_iam_member: 1 + google_storage_bucket: 4 + google_storage_bucket_iam_binding: 4 + google_storage_bucket_iam_member: 12 + google_storage_bucket_object: 13 + google_storage_project_service_account: 3 + google_tags_tag_key: 1 + google_tags_tag_value: 2 + local_file: 13 + modules: 26 + resources: 272 diff --git a/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars b/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars index 4ceaffb6a..d9ed7eb75 100644 --- a/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars +++ b/tests/fast/stages/s0_bootstrap/iam_by_principals.tfvars @@ -1,20 +1,20 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} organization = { domain = "fast.example.com" id = 123456789012 customer_id = "C00000000" } -billing_account = { - id = "000000-111111-222222" -} -essential_contacts = "gcp-organization-admins@fast.example.com" +outputs_location = "/fast-config" +prefix = "fast" iam_by_principals = { "user:other@fast.example.com" = ["roles/browser"] } -prefix = "fast" -org_policies_config = { - import_defaults = false -} -outputs_location = "/fast-config" -groups = { - gcp-support = "group:gcp-support@example.com" -} diff --git a/tests/fast/stages/s0_bootstrap/simple.tfvars b/tests/fast/stages/s0_bootstrap/simple.tfvars index 335283c3c..879a044e2 100644 --- a/tests/fast/stages/s0_bootstrap/simple.tfvars +++ b/tests/fast/stages/s0_bootstrap/simple.tfvars @@ -1,17 +1,17 @@ +billing_account = { + id = "000000-111111-222222" +} +essential_contacts = "gcp-organization-admins@fast.example.com" +groups = { + gcp-support = "group:gcp-support@example.com" +} +org_policies_config = { + import_defaults = false +} organization = { domain = "fast.example.com" id = 123456789012 customer_id = "C00000000" } -billing_account = { - id = "000000-111111-222222" -} -essential_contacts = "gcp-organization-admins@fast.example.com" -prefix = "fast" -org_policies_config = { - import_defaults = false -} outputs_location = "/fast-config" -groups = { - gcp-support = "group:gcp-support@example.com" -} +prefix = "fast" diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index 259d2e38b..17dc99a88 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,16 +30,16 @@ counts: google_project_service: 31 google_project_service_identity: 7 google_service_account: 6 - google_service_account_iam_binding: 2 + google_service_account_iam_binding: 6 google_service_account_iam_member: 1 google_storage_bucket: 4 google_storage_bucket_iam_binding: 4 google_storage_bucket_iam_member: 6 - google_storage_bucket_object: 10 + google_storage_bucket_object: 8 google_storage_project_service_account: 3 google_tags_tag_key: 1 google_tags_tag_value: 2 - local_file: 10 + local_file: 8 modules: 20 resources: 235 @@ -70,8 +70,47 @@ outputs: bootstrap: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com resman: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com tfvars: __missing__ + tfvars_globals: + billing_account: + force_create: + dataset: false + project: false + id: 000000-111111-222222 + is_org_level: true + no_iam: false + environments: + dev: + is_default: false + key: dev + name: Development + short_name: dev + tag_name: development + prod: + is_default: true + key: prod + name: Production + short_name: prod + tag_name: production + groups: + gcp-billing-admins: group:gcp-billing-admins@fast.example.com + gcp-devops: group:gcp-devops@fast.example.com + gcp-network-admins: group:gcp-vpc-network-admins@fast.example.com + gcp-organization-admins: group:gcp-organization-admins@fast.example.com + gcp-security-admins: group:gcp-security-admins@fast.example.com + gcp-support: group:gcp-support@example.com + locations: + bq: EU + gcs: EU + logging: global + pubsub: [] + organization: + customer_id: C00000000 + domain: fast.example.com + id: 123456789012 + prefix: fast workforce_identity_pool: pool: null workload_identity_pool: pool: null providers: {} + diff --git a/tests/fast/stages/s0_bootstrap/simple_sas.yaml b/tests/fast/stages/s0_bootstrap/simple_sas.yaml index 741885f2a..02c089ebd 100644 --- a/tests/fast/stages/s0_bootstrap/simple_sas.yaml +++ b/tests/fast/stages/s0_bootstrap/simple_sas.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s0_bootstrap/tftest.yaml b/tests/fast/stages/s0_bootstrap/tftest.yaml index dd8319456..b51af9e3f 100644 --- a/tests/fast/stages/s0_bootstrap/tftest.yaml +++ b/tests/fast/stages/s0_bootstrap/tftest.yaml @@ -14,12 +14,11 @@ # limitations under the License. module: fast/stages/0-bootstrap - tests: simple: inventory: - simple.yaml - simple_projects.yaml - simple_sas.yaml - iam_by_principals: + cicd: \ No newline at end of file diff --git a/tests/fast/stages/s1_resman/__init__.py b/tests/fast/stages/s1_resman/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s1_resman/__init__.py +++ b/tests/fast/stages/s1_resman/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s1_resman/simple.tfvars b/tests/fast/stages/s1_resman/simple.tfvars index b5297bc25..97adcfbdf 100644 --- a/tests/fast/stages/s1_resman/simple.tfvars +++ b/tests/fast/stages/s1_resman/simple.tfvars @@ -3,6 +3,20 @@ billing_account = { id = "000000-111111-222222" } +environments = { + dev = { + is_default = false + name = "Development" + short_name = "dev" + tag_name = "development" + } + prod = { + is_default = true + name = "Production" + short_name = "prod" + tag_name = "production" + } +} groups = { gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", @@ -48,6 +62,7 @@ automation = { project_id = "fast2-prod-automation" project_number = 123456 service_accounts = { + resman = "fast2-prod-resman-0@fast2-prod-iac-core-0.iam.gserviceaccount.com" resman-r = "fast2-prod-resman-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com" } } @@ -64,24 +79,17 @@ custom_roles = { service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin" storage_viewer = "organizations/123456789012/roles/storageViewer" } -environments = { - dev = { - is_default = false - name = "Development" - tag_name = "development" - } - prod = { - is_default = true - name = "Production" - tag_name = "production" - } -} logging = { project_id = "fast-prod-log-audit-0" } # stage variables +fast_addon = { + ngfw = { + parent_stage = "2-networking" + } +} fast_stage_2 = { networking = { cicd_config = { @@ -105,9 +113,6 @@ fast_stage_2 = { } } } - network_security = { - enabled = true - } } tags = { context = { @@ -138,16 +143,10 @@ tags = { } top_level_folders = { tenants = { - name = "Tenants" - automation = { - enable = false - } + name = "Tenants" iam_by_principals = {} } shared = { name = "Shared Infrastructure" - automation = { - enable = false - } } } diff --git a/tests/fast/stages/s1_resman/simple.yaml b/tests/fast/stages/s1_resman/simple.yaml index 73290e911..6be8a34e0 100644 --- a/tests/fast/stages/s1_resman/simple.yaml +++ b/tests/fast/stages/s1_resman/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -105,1672 +105,25 @@ values: : bucket: fast2-prod-iac-core-outputs condition: [] role: roles/storage.objectViewer - module.net-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-net-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.net-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-net-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.net-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-net-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.net-folder-envs["dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.net-folder-envs["dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.net-folder-envs["prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.net-folder-envs["prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.net-folder[0].google_folder.folder[0]: - deletion_protection: false - display_name: Networking - tags: null - timeouts: null - ? module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/networkFirewallPoliciesAdmin"] - : condition: [] - members: - - serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/networkFirewallPoliciesAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/projectIamViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/projectIamViewer - module.net-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/xpnServiceAdmin"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/xpnServiceAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.networkViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.networkViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.orgFirewallPolicyUser"]: - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.orgFirewallPolicyUser - module.net-folder[0].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/editor"]: - condition: [] - members: - - group:gcp-vpc-network-admins@fast.example.com - role: roles/editor - module.net-folder[0].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.net-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/serviceusage.serviceUsageAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/serviceusage.serviceUsageAdmin - module.net-folder[0].google_folder_iam_binding.authoritative["roles/serviceusage.serviceUsageConsumer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/serviceusage.serviceUsageConsumer - module.net-folder[0].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkAdmin:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkAdmin - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkAdmin:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkAdmin - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkViewer:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkViewer - module.net-folder[0].google_folder_iam_binding.bindings["organizations/123456789012/roles/gcveNetworkViewer:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/gcveNetworkViewer - module.net-folder[0].google_folder_iam_binding.bindings["pf_delegated_grant"]: - condition: - - description: Project factory delegated grant. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/compute.networkUser', - 'roles/composer.sharedVpcAgent', 'roles/container.hostServiceAgentUser', 'roles/vpcaccess.user']) - title: project factory project delegated admin - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectIamAdmin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.admin:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.admin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.admin:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.admin - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.reader:development"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'development'\n\ - )\n" - title: stage 3 development - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.reader - module.net-folder[0].google_folder_iam_binding.bindings["roles/dns.reader:production"]: - condition: - - description: null - expression: "resource.matchTag(\n '123456789012/environment',\n 'production'\n\ - )\n" - title: stage 3 production - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/dns.reader - module.net-folder[0].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.net-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.net-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-net-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman networking service account (read-only). - email: fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.net-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-1r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - ? module.net-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.net-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.net-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-net-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman networking service account. - email: fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.net-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-1@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - module.net-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.nsec-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-resman-nsec-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.nsec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-resman-nsec-0 - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.nsec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-resman-nsec-0 - condition: [] - members: - - serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - ? module.nsec-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.nsec-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-resman-nsec-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman network security main service account (read-only). - email: fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.nsec-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.nsec-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.nsec-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.nsec-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-resman-nsec-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman network security main service account. - email: fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.nsec-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.nsec-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.organization[0].google_organization_iam_member.bindings["gcve-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gcve-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gke-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["gke-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["project-factory-dev"]: - condition: [] - member: serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["project-factory-prod"]: - condition: [] - member: serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_net_billing"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_net_fw_policy_admin"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_fw_policy_user"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyUser - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_ngfw_enterprise_admin"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/ngfwEnterpriseAdmin - module.organization[0].google_organization_iam_member.bindings["sa_net_nsec_ro_ngfw_enterprise_viewer"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0r@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/ngfwEnterpriseViewer - module.organization[0].google_organization_iam_member.bindings["sa_net_xpn_admin"]: - condition: [] - member: serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.xpnAdmin - module.organization[0].google_organization_iam_member.bindings["sa_nsec_fw_policy_admin"]: - condition: [] - member: serviceAccount:fast2-resman-nsec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/compute.orgFirewallPolicyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_pf_billing"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_organization_iam_member.bindings["sa_pf_conditional_org_policy"]: - condition: - - description: Org policy tag scoped grant for project factory. - expression: 'resource.matchTag(''123456789012/context'', ''project-factory'') - - ' - title: org_policy_tag_pf_scoped - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/orgpolicy.policyAdmin - module.organization[0].google_organization_iam_member.bindings["sa_pf_costs_manager"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.costsManager - module.organization[0].google_organization_iam_member.bindings["sa_pf_ro_viewer"]: - condition: [] - member: serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: organizations/123456789012/roles/billingViewer - module.organization[0].google_organization_iam_member.bindings["sa_sec_asset_viewer"]: - condition: [] - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/cloudasset.viewer - module.organization[0].google_organization_iam_member.bindings["sa_sec_billing"]: - condition: [] - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - org_id: '123456789012' - role: roles/billing.user - module.organization[0].google_tags_tag_key.default["context"]: - description: Resource management context. - parent: organizations/123456789012 - purpose: null - purpose_data: null - short_name: context - timeouts: null - module.organization[0].google_tags_tag_key.default["environment"]: - description: Environment definition. - parent: organizations/123456789012 - purpose: null - purpose_data: null - short_name: environment - timeouts: null - module.organization[0].google_tags_tag_value.default["context/data-platform"]: - description: Managed by the Terraform organization module. - short_name: data-platform - timeouts: null - module.organization[0].google_tags_tag_value.default["context/gcve"]: - description: Managed by the Terraform organization module. - short_name: gcve - timeouts: null - module.organization[0].google_tags_tag_value.default["context/gke"]: - description: Managed by the Terraform organization module. - short_name: gke - timeouts: null - module.organization[0].google_tags_tag_value.default["context/network-security"]: - description: Managed by the Terraform organization module. - short_name: network-security - timeouts: null - module.organization[0].google_tags_tag_value.default["context/networking"]: - description: Managed by the Terraform organization module. - short_name: networking - timeouts: null - module.organization[0].google_tags_tag_value.default["context/nsec"]: - description: Managed by the Terraform organization module. - short_name: nsec - timeouts: null - module.organization[0].google_tags_tag_value.default["context/project-factory"]: - description: Managed by the Terraform organization module. - short_name: project-factory - timeouts: null - module.organization[0].google_tags_tag_value.default["context/sandbox"]: - description: Managed by the Terraform organization module. - short_name: sandbox - timeouts: null - module.organization[0].google_tags_tag_value.default["context/security"]: - description: Managed by the Terraform organization module. - short_name: security - timeouts: null - module.organization[0].google_tags_tag_value.default["context/tenants"]: - description: Managed by the Terraform organization module. - short_name: tenants - timeouts: null - module.organization[0].google_tags_tag_value.default["environment/development"]: - description: Managed by the Terraform organization module. - short_name: development - timeouts: null - module.organization[0].google_tags_tag_value.default["environment/production"]: - description: Managed by the Terraform organization module. - short_name: production - timeouts: null - module.organization[0].google_tags_tag_value_iam_binding.default["environment/development:roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - ? module.organization[0].google_tags_tag_value_iam_binding.default["environment/development:roles/resourcemanager.tagViewer"] - : condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.organization[0].google_tags_tag_value_iam_binding.default["environment/production:roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.organization[0].google_tags_tag_value_iam_binding.default["environment/production:roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.pf-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.pf-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.pf-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - ? module.pf-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.pf-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project factory main service account (read-only). - email: fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.pf-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.pf-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.pf-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.pf-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project factory main service account. - email: fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.pf-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - module.pf-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.sec-bucket[0].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-sec-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.sec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-sec-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.sec-bucket[0].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-sec-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.sec-folder[0].google_folder.folder[0]: - deletion_protection: false - display_name: Security - parent: organizations/123456789012 - tags: null - timeouts: null - module.sec-folder[0].google_folder_iam_binding.authoritative["organizations/123456789012/roles/projectIamViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/projectIamViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/cloudkms.cryptoKeyEncrypterDecrypter"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/cloudkms.cryptoKeyEncrypterDecrypter - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/cloudkms.viewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/cloudkms.viewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/editor"]: - condition: [] - members: - - group:gcp-security-admins@fast.example.com - role: roles/editor - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-net-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.sec-folder[0].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.sec-folder[0].google_folder_iam_binding.bindings["pf_delegated_grant"]: - condition: - - description: Project factory delegated grant. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: pf_delegated_grant - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectIamAdmin - module.sec-folder[0].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.sec-sa-ro[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.sec-sa-ro[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-sec-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman security service account (read-only). - email: fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-sec-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.sec-sa-ro[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-1r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - ? module.sec-sa-ro[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.sec-sa-rw[0].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.sec-sa-rw[0].google_service_account.service_account[0]: - account_id: fast2-prod-resman-sec-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman security service account. - email: fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-sec-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.sec-sa-rw[0].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-sec-1@fast2-prod-automation.iam.gserviceaccount.com - role: roles/iam.serviceAccountTokenCreator - module.sec-sa-rw[0].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"]: - bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-gcve-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gcve-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gcve-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-gcve-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gcve-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gcve-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-gcve-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gke-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-gke-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gke-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gke-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["gke-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-gke-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["gke-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["gke-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-gke-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["project-factory-dev"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["project-factory-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["project-factory-dev"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-bucket["project-factory-prod"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-pf-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.stage3-bucket["project-factory-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.stage3-bucket["project-factory-prod"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-pf-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.stage3-folder["gcve-dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gcve-dev"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gcve-dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gcve-prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gcve-prod"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gcve-prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gke-dev"].google_folder.folder[0]: - deletion_protection: false - display_name: Development - tags: null - timeouts: null - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gke-dev"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gke-dev"].google_tags_tag_binding.binding["environment"]: - timeouts: null - module.stage3-folder["gke-prod"].google_folder.folder[0]: - deletion_protection: false - display_name: Production - tags: null - timeouts: null - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/compute.xpnAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/compute.xpnAdmin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/logging.admin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/logging.admin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.stage3-folder["gke-prod"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.stage3-folder["gke-prod"].google_tags_tag_binding.binding["environment"]: - timeouts: null - ? module.stage3-sa-ro["gcve-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gcve-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gcve-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-dev service account (read-only). - email: fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gcve-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gcve-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gcve-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gcve-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gcve-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-prod service account (read-only). - email: fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gcve-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gcve-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gke-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gke-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gke-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-dev service account (read-only). - email: fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gke-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gke-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["gke-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["gke-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gke-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-prod service account (read-only). - email: fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gke-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-ro["gke-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["gke-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["project-factory-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["project-factory-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-dev service account (read-only). - email: fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-ro["project-factory-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["project-factory-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-ro["project-factory-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-ro["project-factory-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-pf-0r - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-prod service account (read-only). - email: fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-ro["project-factory-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-ro["project-factory-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-organizations/123456789012/roles/storageViewer"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: organizations/123456789012/roles/storageViewer - ? module.stage3-sa-rw["gcve-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gcve-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gcve-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-dev service account. - email: fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gcve-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gcve-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gcve-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gcve-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gcve-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gcve-prod service account. - email: fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gcve-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gcve-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gke-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gke-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-gke-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-dev service account. - email: fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gke-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gke-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["gke-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["gke-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-gke-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman gke-prod service account. - email: fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-gke-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.stage3-sa-rw["gke-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["gke-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["project-factory-dev"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["project-factory-dev"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-dev service account. - email: fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-rw["project-factory-dev"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["project-factory-dev"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.stage3-sa-rw["project-factory-prod"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.stage3-sa-rw["project-factory-prod"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-pf-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman project-factory-prod service account. - email: fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - ? module.stage3-sa-rw["project-factory-prod"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] - : condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.stage3-sa-rw["project-factory-prod"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - module.top-level-bucket["sandbox"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-dev-resman-sbox-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.top-level-bucket["sandbox"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-dev-resman-sbox-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.top-level-bucket["sandbox"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-dev-resman-sbox-0 - condition: [] - members: - - serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.top-level-bucket["tenants"].google_storage_bucket.bucket: - autoclass: [] - cors: [] - custom_placement_config: [] - default_event_based_hold: null - effective_labels: - goog-terraform-provisioned: 'true' - enable_object_retention: null - encryption: [] - force_destroy: false - hierarchical_namespace: [] - labels: null - lifecycle_rule: [] - location: EU - logging: [] - name: fast2-prod-resman-tenants-0 - project: fast2-prod-automation - requester_pays: null - retention_policy: [] - storage_class: STANDARD - terraform_labels: - goog-terraform-provisioned: 'true' - timeouts: null - uniform_bucket_level_access: true - versioning: - - enabled: true - module.top-level-bucket["tenants"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectAdmin"]: - bucket: fast2-prod-resman-tenants-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectAdmin - module.top-level-bucket["tenants"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"]: - bucket: fast2-prod-resman-tenants-0 - condition: [] - members: - - serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/storage.objectViewer - module.top-level-folder["gcve"].google_folder.folder[0]: - deletion_protection: false - display_name: GCVE - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["gcve"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["gke"].google_folder.folder[0]: - deletion_protection: false - display_name: GKE - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["gke"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["sandbox"].google_folder.folder[0]: - deletion_protection: false - display_name: Sandbox - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["teams"].google_folder.folder[0]: - deletion_protection: false - display_name: Teams - parent: organizations/123456789012 - tags: null - timeouts: null - ? module.top-level-folder["teams"].google_folder_iam_binding.authoritative["organizations/123456789012/roles/xpnServiceAdmin"] - : condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: organizations/123456789012/roles/xpnServiceAdmin - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/owner"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/owner - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderAdmin"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderAdmin - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.folderViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.folderViewer - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.projectCreator"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.projectCreator - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.tagUser"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagUser - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/resourcemanager.tagViewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/resourcemanager.tagViewer - module.top-level-folder["teams"].google_folder_iam_binding.authoritative["roles/viewer"]: - condition: [] - members: - - serviceAccount:fast2-resman-pf-0r@fast2-prod-automation.iam.gserviceaccount.com - role: roles/viewer - module.top-level-folder["teams"].google_tags_tag_binding.binding["context"]: - timeouts: null - module.top-level-folder["tenants"].google_folder.folder[0]: - deletion_protection: false - display_name: Tenants - parent: organizations/123456789012 - tags: null - timeouts: null - module.top-level-folder["tenants"].google_tags_tag_binding.binding["context"]: - timeouts: null - ? module.top-level-sa["sandbox"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.top-level-sa["sandbox"].google_service_account.service_account[0]: - account_id: fast2-dev-resman-sbox-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman sandbox folder service account. - email: fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-dev-resman-sbox-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.top-level-sa["sandbox"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.top-level-sa["sandbox"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin - ? module.top-level-sa["tenants"].google_project_iam_member.project-roles["fast2-prod-automation-roles/serviceusage.serviceUsageConsumer"] - : condition: [] - project: fast2-prod-automation - role: roles/serviceusage.serviceUsageConsumer - module.top-level-sa["tenants"].google_service_account.service_account[0]: - account_id: fast2-prod-resman-tenants-0 - create_ignore_already_exists: null - description: null - disabled: false - display_name: Terraform resman tenants folder service account. - email: fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - member: serviceAccount:fast2-prod-resman-tenants-0@fast2-prod-automation.iam.gserviceaccount.com - project: fast2-prod-automation - timeouts: null - module.top-level-sa["tenants"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]: - condition: [] - members: null - role: roles/iam.serviceAccountTokenCreator - ? module.top-level-sa["tenants"].google_storage_bucket_iam_member.bucket-roles["fast2-prod-iac-core-outputs-roles/storage.objectAdmin"] - : bucket: fast2-prod-iac-core-outputs - condition: [] - role: roles/storage.objectAdmin counts: google_folder: 14 - google_folder_iam_binding: 75 + google_folder_iam_binding: 73 google_org_policy_policy: 2 - google_organization_iam_member: 19 - google_project_iam_member: 27 - google_service_account: 27 - google_service_account_iam_binding: 27 - google_storage_bucket: 13 - google_storage_bucket_iam_binding: 26 - google_storage_bucket_iam_member: 27 - google_storage_bucket_object: 26 + google_organization_iam_member: 18 + google_project_iam_member: 23 + google_service_account: 23 + google_service_account_iam_binding: 23 + google_storage_bucket: 10 + google_storage_bucket_iam_binding: 20 + google_storage_bucket_iam_member: 23 + google_storage_bucket_object: 24 google_tags_tag_binding: 14 google_tags_tag_key: 2 - google_tags_tag_value: 13 + google_tags_tag_value: 12 google_tags_tag_value_iam_binding: 4 - modules: 55 - resources: 316 + modules: 48 + resources: 285 outputs: cicd_repositories: diff --git a/tests/fast/stages/s2_network_security/simple.tfvars b/tests/fast/stages/s2_network_security/simple.tfvars deleted file mode 100644 index 1ab02d8d2..000000000 --- a/tests/fast/stages/s2_network_security/simple.tfvars +++ /dev/null @@ -1,25 +0,0 @@ -automation = { - outputs_bucket = "test" -} -billing_account = { - id = "000000-111111-222222" -} -folder_ids = { - networking = "folders/12345678900" - networking-dev = "folders/12345678901" - networking-prod = "folders/12345678902" -} -host_project_ids = { - dev-spoke-0 = "dev-project" - prod-spoke-0 = "prod-project" -} -organization = { - domain = "fast.example.com" - id = 123456789012 - customer_id = "C00000000" -} -prefix = "fast2" -vpc_self_links = { - dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" - prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" -} diff --git a/tests/fast/stages/s2_network_security/tls.tfvars b/tests/fast/stages/s2_network_security/tls.tfvars deleted file mode 100644 index ecd91f565..000000000 --- a/tests/fast/stages/s2_network_security/tls.tfvars +++ /dev/null @@ -1,43 +0,0 @@ -automation = { - outputs_bucket = "test" -} -billing_account = { - id = "000000-111111-222222" -} -folder_ids = { - networking = "folders/12345678900" - networking-dev = "folders/12345678901" - networking-prod = "folders/12345678902" -} -host_project_ids = { - dev-spoke-0 = "dev-project" - prod-spoke-0 = "prod-project" -} -ngfw_enterprise_config = { - endpoint_zones = [ - "europe-west1-b", - "europe-west1-c", - "europe-west1-d" - ] -} -ngfw_tls_configs = { - tls_enabled = true - tls_ip_ids_by_region = { - dev = { - europe-west1 = "projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0" - } - prod = { - europe-west1 = "projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0" - } - } -} -organization = { - domain = "fast.example.com" - id = 123456789012 - customer_id = "C00000000" -} -prefix = "fast2" -vpc_self_links = { - dev-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1" - prod-spoke-0 = "https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2" -} diff --git a/tests/fast/stages/s2_network_security/tls.yaml b/tests/fast/stages/s2_network_security/tls.yaml deleted file mode 100644 index 8c6e0a93d..000000000 --- a/tests/fast/stages/s2_network_security/tls.yaml +++ /dev/null @@ -1,325 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -values: - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-b"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-b - name: fast2-ngfw-endpoint-europe-west1-b - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-c"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-c - name: fast2-ngfw-endpoint-europe-west1-c - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint.firewall_endpoint["europe-west1-d"]: - billing_project_id: fast2-net-ngfw-0 - labels: null - location: europe-west1-d - name: fast2-ngfw-endpoint-europe-west1-d - parent: organizations/123456789012 - timeouts: null - google_network_security_firewall_endpoint_association.dev["europe-west1-b"]: - disabled: false - labels: null - location: europe-west1-b - name: fast2-dev-epa-europe-west1-b - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.dev["europe-west1-c"]: - disabled: false - labels: null - location: europe-west1-c - name: fast2-dev-epa-europe-west1-c - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.dev["europe-west1-d"]: - disabled: false - labels: null - location: europe-west1-d - name: fast2-dev-epa-europe-west1-d - network: projects/123456789/networks/vpc-1 - parent: projects/dev-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/dev-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-b"]: - disabled: false - labels: null - location: europe-west1-b - name: fast2-prod-epa-europe-west1-b - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-c"]: - disabled: false - labels: null - location: europe-west1-c - name: fast2-prod-epa-europe-west1-c - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_firewall_endpoint_association.prod["europe-west1-d"]: - disabled: false - labels: null - location: europe-west1-d - name: fast2-prod-epa-europe-west1-d - network: projects/123456789/networks/vpc-2 - parent: projects/prod-project - timeouts: null - tls_inspection_policy: projects/project1/locations/europe-west1/tlsInspectionPolicies/prod-tls-ip-0 - google_network_security_security_profile.dev: - description: null - labels: null - location: global - name: fast2-dev-sp-0 - parent: organizations/123456789012 - threat_prevention_profile: [] - timeouts: null - type: THREAT_PREVENTION - google_network_security_security_profile.prod: - description: null - labels: null - location: global - name: fast2-prod-sp-0 - parent: organizations/123456789012 - threat_prevention_profile: [] - timeouts: null - type: THREAT_PREVENTION - google_network_security_security_profile_group.dev: - description: Dev security profile group. - labels: null - location: global - name: fast2-dev-spg-0 - parent: organizations/123456789012 - timeouts: null - google_network_security_security_profile_group.prod: - description: prod security profile group. - labels: null - location: global - name: fast2-prod-spg-0 - parent: organizations/123456789012 - timeouts: null - google_storage_bucket_object.tfvars: - bucket: test - cache_control: null - content_disposition: null - content_encoding: null - content_language: null - customer_encryption: [] - detect_md5hash: different hash - event_based_hold: null - metadata: null - name: tfvars/2-nsec.auto.tfvars.json - retention: [] - source: null - temporary_hold: null - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy.net-global[0]: - description: null - name: fast2-dev-fw-policy - project: dev-project - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_association.net-global["dev-spoke"]: - attachment_target: https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-1 - firewall_policy: fast2-dev-fw-policy - name: fast2-dev-fw-policy-dev-spoke - project: dev-project - timeouts: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-allow-rfc1918"]: - action: allow - description: Allow all hosts to RFC-1918 - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-dev-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483546 - project: dev-project - rule_name: egress-allow-rfc1918 - security_profile_group: null - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.dev-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-inspect-internet"]: - action: apply_security_profile_group - description: Inspect egress traffic from all dev hosts to Internet - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-dev-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 0.0.0.0/0 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483547 - project: dev-project - rule_name: egress-inspect-internet - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.ngfw-quota-project.google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - folder_id: '12345678900' - labels: null - name: fast2-net-ngfw-0 - org_id: null - project_id: fast2-net-ngfw-0 - timeouts: null - module.ngfw-quota-project.google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast2-net-ngfw-0 - service: networksecurity.googleapis.com - timeouts: null - module.ngfw-quota-project.google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast2-net-ngfw-0 - service: networksecurity.googleapis.com - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy.net-global[0]: - description: null - name: fast2-prod-fw-policy - project: prod-project - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_association.net-global["prod-spoke"]: - attachment_target: https://www.googleapis.com/compute/v1/projects/123456789/networks/vpc-2 - firewall_policy: fast2-prod-fw-policy - name: fast2-prod-fw-policy-prod-spoke - project: prod-project - timeouts: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-allow-rfc1918"]: - action: allow - description: Allow all hosts to RFC-1918 - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-prod-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483546 - project: prod-project - rule_name: egress-allow-rfc1918 - security_profile_group: null - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - module.prod-spoke-firewall-policy.google_compute_network_firewall_policy_rule.net-global["egress/egress-inspect-internet"]: - action: apply_security_profile_group - description: Inspect egress traffic from all prod hosts to Internet - direction: EGRESS - disabled: false - enable_logging: null - firewall_policy: fast2-prod-fw-policy - match: - - dest_address_groups: null - dest_fqdns: null - dest_ip_ranges: - - 0.0.0.0/0 - dest_region_codes: null - dest_threat_intelligences: null - layer4_configs: - - ip_protocol: all - ports: null - src_address_groups: null - src_fqdns: null - src_ip_ranges: null - src_region_codes: null - src_secure_tags: [] - src_threat_intelligences: null - priority: 2147483547 - project: prod-project - rule_name: egress-inspect-internet - target_secure_tags: [] - target_service_accounts: null - timeouts: null - tls_inspect: null - -counts: - google_compute_network_firewall_policy: 2 - google_compute_network_firewall_policy_association: 2 - google_compute_network_firewall_policy_rule: 4 - google_network_security_firewall_endpoint: 3 - google_network_security_firewall_endpoint_association: 6 - google_network_security_security_profile: 2 - google_network_security_security_profile_group: 2 - google_project: 1 - google_project_service: 1 - google_project_service_identity: 1 - google_storage_bucket_object: 1 - modules: 3 - resources: 25 - -outputs: - ngfw_enterprise_endpoint_ids: __missing__ - ngfw_enterprise_endpoints_quota_project: fast2-net-ngfw-0 - diff --git a/tests/fast/stages/s2_networking_a_simple/__init__.py b/tests/fast/stages/s2_networking_a_simple/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s2_networking_a_simple/__init__.py +++ b/tests/fast/stages/s2_networking_a_simple/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_networking_a_simple/ncc.yaml b/tests/fast/stages/s2_networking_a_simple/ncc.yaml index 65630a089..36d2c9c8b 100644 --- a/tests/fast/stages/s2_networking_a_simple/ncc.yaml +++ b/tests/fast/stages/s2_networking_a_simple/ncc.yaml @@ -22,7 +22,7 @@ counts: google_compute_router: 2 google_compute_router_nat: 2 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_dns_managed_zone: 9 google_dns_policy: 3 google_dns_record_set: 3 @@ -39,8 +39,7 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 24 - resources: 180 + modules: 22 + resources: 175 diff --git a/tests/fast/stages/s2_networking_a_simple/simple.yaml b/tests/fast/stages/s2_networking_a_simple/simple.yaml index eaf9c6690..ed6159b6a 100644 --- a/tests/fast/stages/s2_networking_a_simple/simple.yaml +++ b/tests/fast/stages/s2_networking_a_simple/simple.yaml @@ -27,7 +27,7 @@ counts: google_compute_router_nat: 3 google_compute_router_peer: 2 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_compute_vpn_tunnel: 2 google_dns_managed_zone: 9 google_dns_policy: 3 @@ -43,9 +43,8 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 29 + modules: 27 random_id: 3 - resources: 199 + resources: 194 diff --git a/tests/fast/stages/s2_networking_a_simple/tftest.yaml b/tests/fast/stages/s2_networking_a_simple/tftest.yaml index 9b14f136b..67b70d4a5 100644 --- a/tests/fast/stages/s2_networking_a_simple/tftest.yaml +++ b/tests/fast/stages/s2_networking_a_simple/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,11 +17,5 @@ module: fast/stages/2-networking-a-simple tests: # peering (default) is implemented in simple.tfvars ncc: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf simple: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf vpn: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf diff --git a/tests/fast/stages/s2_networking_a_simple/vpn.yaml b/tests/fast/stages/s2_networking_a_simple/vpn.yaml index d948bcb93..f6609d003 100644 --- a/tests/fast/stages/s2_networking_a_simple/vpn.yaml +++ b/tests/fast/stages/s2_networking_a_simple/vpn.yaml @@ -25,7 +25,7 @@ counts: google_compute_router_nat: 3 google_compute_router_peer: 12 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 7 + google_compute_subnetwork: 5 google_compute_vpn_tunnel: 12 google_dns_managed_zone: 9 google_dns_policy: 3 @@ -41,9 +41,8 @@ counts: google_project_iam_member: 20 google_project_service: 26 google_project_service_identity: 20 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 31 + modules: 29 random_id: 17 - resources: 244 + resources: 239 diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml index bb1a44770..054ff476c 100644 --- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml +++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml @@ -28,7 +28,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 20 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 12 + google_compute_subnetwork: 10 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 4 @@ -46,9 +46,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 39 + modules: 37 random_id: 6 - resources: 261 + resources: 256 diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml index bcd5a8555..d3c76f0fd 100644 --- a/tests/fast/stages/s2_networking_b_nva/regional.yaml +++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml @@ -32,7 +32,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 4 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 14 + google_compute_subnetwork: 12 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 6 @@ -48,9 +48,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 47 + modules: 45 random_id: 6 - resources: 269 + resources: 264 diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml index 51c09deb6..bb1699354 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.yaml +++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml @@ -32,7 +32,7 @@ counts: google_compute_router_nat: 2 google_compute_router_peer: 4 google_compute_shared_vpc_host_project: 3 - google_compute_subnetwork: 12 + google_compute_subnetwork: 10 google_compute_vpn_tunnel: 4 google_dns_managed_zone: 9 google_dns_policy: 4 @@ -48,9 +48,8 @@ counts: google_project_iam_member: 19 google_project_service: 25 google_project_service_identity: 19 - google_storage_bucket_object: 2 + google_storage_bucket_object: 1 google_tags_tag_binding: 3 - google_vpc_access_connector: 2 - modules: 43 + modules: 41 random_id: 6 - resources: 247 + resources: 242 diff --git a/tests/fast/stages/s2_networking_b_nva/tftest.yaml b/tests/fast/stages/s2_networking_b_nva/tftest.yaml index a60ec3adb..50d6b13ed 100644 --- a/tests/fast/stages/s2_networking_b_nva/tftest.yaml +++ b/tests/fast/stages/s2_networking_b_nva/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,11 +16,5 @@ module: fast/stages/2-networking-b-nva tests: simple: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf ncc-ra: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf regional: - extra_files: - - ../../plugins/2-networking-serverless-connector/*.tf \ No newline at end of file diff --git a/tests/fast/stages/s2_project_factory/tftest.yaml b/tests/fast/stages/s2_project_factory/tftest.yaml index 470b47fbc..2e7b27a19 100644 --- a/tests/fast/stages/s2_project_factory/tftest.yaml +++ b/tests/fast/stages/s2_project_factory/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s2_security/simple.tfvars b/tests/fast/stages/s2_security/simple.tfvars index dcea9e4b1..742f6c85e 100644 --- a/tests/fast/stages/s2_security/simple.tfvars +++ b/tests/fast/stages/s2_security/simple.tfvars @@ -4,6 +4,25 @@ automation = { billing_account = { id = "000000-111111-222222" } +certificate_authorities = { + ngfw-0 = { + environments = ["prod"] + ca_configs = { + ca-0 = { + deletion_protection = false + subject = { + common_name = "fast.example.com" + organization = "FAST Test" + } + } + } + ca_pool_config = { + authz_nsec_sa = true + name = "ca-pool-0" + } + location = "europe-west8" + } +} custom_roles = { project_iam_viewer = "organizations/123456789012/roles/bar" service_project_network_admin = "organizations/123456789012/roles/foo" diff --git a/tests/fast/stages/s2_security/simple.yaml b/tests/fast/stages/s2_security/simple.yaml index 23a3d6a2a..e2191cfec 100644 --- a/tests/fast/stages/s2_security/simple.yaml +++ b/tests/fast/stages/s2_security/simple.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,21 +13,84 @@ # limitations under the License. values: - google_storage_bucket_object.tfvars: - bucket: test - cache_control: null - content_disposition: null - content_encoding: null - content_language: null - customer_encryption: [] - detect_md5hash: different hash - event_based_hold: null - metadata: null - name: tfvars/2-security.auto.tfvars.json - retention: [] - source: null - temporary_hold: null + module.cas["prod-ngfw-0"].google_privateca_ca_pool.ca_pool[0]: + effective_labels: + goog-terraform-provisioned: 'true' + issuance_policy: [] + labels: null + location: europe-west8 + name: ca-pool-0 + project: fast-prod-sec-core-0 + publishing_options: [] + terraform_labels: + goog-terraform-provisioned: 'true' + tier: DEVOPS timeouts: null + module.cas["prod-ngfw-0"].google_privateca_certificate_authority.cas["ca-0"]: + certificate_authority_id: ca-0 + config: + - subject_config: + - subject: + - common_name: fast.example.com + country_code: null + locality: null + organization: FAST Test + organizational_unit: null + postal_code: null + province: null + street_address: null + subject_alt_name: [] + subject_key_id: [] + x509_config: + - additional_extensions: [] + aia_ocsp_servers: null + ca_options: + - is_ca: true + max_issuer_path_length: null + non_ca: null + zero_max_issuer_path_length: null + key_usage: + - base_key_usage: + - cert_sign: true + content_commitment: false + crl_sign: true + data_encipherment: false + decipher_only: false + digital_signature: false + encipher_only: false + key_agreement: false + key_encipherment: true + extended_key_usage: + - client_auth: false + code_signing: false + email_protection: false + ocsp_signing: false + server_auth: true + time_stamping: false + unknown_extended_key_usages: [] + name_constraints: [] + policy_ids: [] + deletion_protection: false + desired_state: null + effective_labels: + goog-terraform-provisioned: 'true' + gcs_bucket: null + ignore_active_certificates_on_deletion: false + key_spec: + - algorithm: RSA_PKCS1_2048_SHA256 + cloud_kms_key_version: null + labels: null + lifetime: 315360000s + location: europe-west8 + pem_ca_certificate: null + pool: ca-pool-0 + project: fast-prod-sec-core-0 + skip_grace_period: true + subordinate_config: [] + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + type: SELF_SIGNED module.folder.google_essential_contacts_contact.contact["gcp-security-admins@fast.example.com"]: email: gcp-security-admins@fast.example.com language_tag: en @@ -227,257 +290,26 @@ values: name: prod-global project: fast-prod-sec-core-0 timeouts: null - module.project["dev"].google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - effective_labels: - environment: dev - goog-terraform-provisioned: 'true' - folder_id: '12345678' - labels: - environment: dev - name: fast-dev-sec-core-0 - org_id: null - project_id: fast-dev-sec-core-0 - tags: null - terraform_labels: - environment: dev - goog-terraform-provisioned: 'true' - timeouts: null - module.project["dev"].google_project_iam_binding.authoritative["organizations/123456789012/roles/bar"]: - condition: [] - members: - - serviceAccount:fast2-dev-resman-gcve-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-dev-resman-pf-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-dev-sec-core-0 - role: organizations/123456789012/roles/bar - module.project["dev"].google_project_iam_binding.bindings["sa_delegated_grants"]: - condition: - - description: Development project delegated grants. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: dev_stage3_sa_delegated_grants - members: - - serviceAccount:fast2-dev-resman-gcve-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-dev-resman-pf-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-dev-sec-core-0 - role: roles/resourcemanager.projectIamAdmin - module.project["dev"].google_project_iam_member.service_agents["certificatemanager"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/certificatemanager.serviceAgent - module.project["dev"].google_project_iam_member.service_agents["cloudkms"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/cloudkms.serviceAgent - module.project["dev"].google_project_iam_member.service_agents["networkmanagement"]: - condition: [] - project: fast-dev-sec-core-0 - role: roles/networkmanagement.serviceAgent - module.project["dev"].google_project_service.project_services["certificatemanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["cloudkms.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["networkmanagement.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["privateca.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["secretmanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service.project_services["stackdriver.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-dev-sec-core-0 - service: stackdriver.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["certificatemanager.googleapis.com"]: - project: fast-dev-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["cloudkms.googleapis.com"]: - project: fast-dev-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["networkmanagement.googleapis.com"]: - project: fast-dev-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast-dev-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["privateca.googleapis.com"]: - project: fast-dev-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["dev"].google_project_service_identity.default["secretmanager.googleapis.com"]: - project: fast-dev-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["dev"].google_tags_tag_binding.binding["environment"]: - tag_value: tagValues/12345 - timeouts: null - module.project["prod"].google_project.project[0]: - auto_create_network: false - billing_account: 000000-111111-222222 - deletion_policy: DELETE - effective_labels: - environment: prod - goog-terraform-provisioned: 'true' - folder_id: '12345678' - labels: - environment: prod - name: fast-prod-sec-core-0 - org_id: null - project_id: fast-prod-sec-core-0 - tags: null - terraform_labels: - environment: prod - goog-terraform-provisioned: 'true' - timeouts: null - module.project["prod"].google_project_iam_binding.authoritative["organizations/123456789012/roles/bar"]: - condition: [] - members: - - serviceAccount:fast2-prod-resman-gcve-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-prod-sec-core-0 - role: organizations/123456789012/roles/bar - module.project["prod"].google_project_iam_binding.bindings["sa_delegated_grants"]: - condition: - - description: Production project delegated grants. - expression: api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['roles/cloudkms.cryptoKeyEncrypterDecrypter']) - title: prod_stage3_sa_delegated_grants - members: - - serviceAccount:fast2-prod-resman-gcve-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - - serviceAccount:fast2-prod-resman-pf-0@fast2-prod-iac-core-0.iam.gserviceaccount.com - project: fast-prod-sec-core-0 - role: roles/resourcemanager.projectIamAdmin - module.project["prod"].google_project_iam_member.service_agents["certificatemanager"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/certificatemanager.serviceAgent - module.project["prod"].google_project_iam_member.service_agents["cloudkms"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/cloudkms.serviceAgent - module.project["prod"].google_project_iam_member.service_agents["networkmanagement"]: - condition: [] - project: fast-prod-sec-core-0 - role: roles/networkmanagement.serviceAgent - module.project["prod"].google_project_service.project_services["certificatemanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["cloudkms.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["networkmanagement.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["networksecurity.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["privateca.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["secretmanager.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service.project_services["stackdriver.googleapis.com"]: - disable_dependent_services: false - disable_on_destroy: false - project: fast-prod-sec-core-0 - service: stackdriver.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["certificatemanager.googleapis.com"]: - project: fast-prod-sec-core-0 - service: certificatemanager.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["cloudkms.googleapis.com"]: - project: fast-prod-sec-core-0 - service: cloudkms.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["networkmanagement.googleapis.com"]: - project: fast-prod-sec-core-0 - service: networkmanagement.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["networksecurity.googleapis.com"]: - project: fast-prod-sec-core-0 - service: networksecurity.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["privateca.googleapis.com"]: - project: fast-prod-sec-core-0 - service: privateca.googleapis.com - timeouts: null - module.project["prod"].google_project_service_identity.default["secretmanager.googleapis.com"]: - project: fast-prod-sec-core-0 - service: secretmanager.googleapis.com - timeouts: null - module.project["prod"].google_tags_tag_binding.binding["environment"]: - tag_value: tagValues/12346 - timeouts: null counts: google_essential_contacts_contact: 1 google_kms_crypto_key: 8 google_kms_crypto_key_iam_binding: 8 google_kms_key_ring: 8 + google_privateca_ca_pool: 1 + google_privateca_certificate_authority: 1 google_project: 2 google_project_iam_binding: 4 - google_project_iam_member: 6 - google_project_service: 14 - google_project_service_identity: 12 + google_project_iam_member: 4 + google_project_service: 10 + google_project_service_identity: 8 google_storage_bucket_object: 1 google_tags_tag_binding: 2 - modules: 11 - resources: 66 + modules: 12 + resources: 58 outputs: - cas_configs: {} + certificate_authority_pools: __missing__ kms_keys: __missing__ tfvars: __missing__ - trust_config_ids: {} diff --git a/tests/fast/stages/s2_security/tftest.yaml b/tests/fast/stages/s2_security/tftest.yaml index 5555caac3..ba6eba583 100644 --- a/tests/fast/stages/s2_security/tftest.yaml +++ b/tests/fast/stages/s2_security/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s3_gcve_dev/__init__.py b/tests/fast/stages/s3_gcve_dev/__init__.py index 7ba50f933..c37e93b74 100644 --- a/tests/fast/stages/s3_gcve_dev/__init__.py +++ b/tests/fast/stages/s3_gcve_dev/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fast/stages/s3_gke_dev/tftest.yaml b/tests/fast/stages/s3_gke_dev/tftest.yaml index 14b8860e0..925dc10d4 100644 --- a/tests/fast/stages/s3_gke_dev/tftest.yaml +++ b/tests/fast/stages/s3_gke_dev/tftest.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/fixtures.py b/tests/fixtures.py index 701e0a5c3..c1ab300e7 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -186,7 +186,7 @@ def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None, # side of any comparison operators. # - include a descriptive error message to the assert # print(yaml.dump({'values': summary.values})) - # print(yaml.dump({'counts': summary.counts})) + # print("", yaml.dump({'counts': summary.counts})) if 'values' in inventory: try: