From c486bfc66f9814e33b410602cb557a5e4d532912 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 3 Apr 2020 14:06:48 +0200 Subject: [PATCH] Merge development branch (#44) * VPN-HA module initial commit * Added readme for net-vpn-ha module * Update readme, add simple description * Merge new modules list and environments foundation example (#30) * gke-cluster * net-vpc module and tests * add TODO to net-vpc module * add minimal README files with input/output variables to gke and net-vpc modules * BigQuery Module (#24) * Bigquery Module * Added README file * Added type hints * gke-cluster * net-vpc module and tests * add TODO to net-vpc module * add minimal README files with input/output variables to gke and net-vpc modules * BigQuery Module (#24) * Bigquery Module * Added README file * Added type hints * GCS module * net vpc module: improve secondary range outputs * net vpc module: add serve project registration * project module * move bigquery module to not-ready folder * folders module * rename project module's iam variables * slight tweak to folder module outputs * gcs module * simplify net-vpc module variables * fix module tests configurations, fix net-vpc module tests * add pydoc utility * add/update module READMEs * add/update module READMEs * add/update module READMEs * improve variable type summary generation in tfdoc * tfdoc: add support for replacing doc in README.md files * improve module READMEs * net-vpc-firewall module * add support for sensitive output attribute in tfdoc * remove empty function from tfdoc * render variable type as code in tfdoc * update module READMEs * net address module * net cloudnat module * remove redundant variable from net-cloudnat module * vpc module: add support for peering, use network name as subnet name prefix * net-vpn-static module * net-vpn-static module README * net-vpn-static module README * tfdoc: fix error on undeclared variable type * dns module * set version for all modules * kms module (untested) * change kms key self links output to map, fix gcs and kms iam variable descriptions * fix kms module * update kms module readme * simplify local iam pairs in modules * service accounts module (unfinished) * work on service accounts module * project module: add gcr service account * project module: update outputs in README * first working version of the iam service accounts module * iam service accounts module: extra checks in locals * modules/net-cloudnat: reorder variables * modules/net-vpn-dynamic: initial import (untested) * modules/net-vpn-dynamic: first working version * modules/net-vpn-dynamic: add outputs for auto-created router * modules/net-vpn-dynamic: update README * modules/net-[vpn,cloudnat]: clean up variable,s remove prefix * modules/net-vpn-dynamic: add advertisement configuration to tunnel bgp peer, refactor variables * tfdoc: add tooltips for variable types and defaults * modules: update README variables and outputs * tfdoc: improve variable default rendering * modules: update README variables and outputs * modules/net-vpc: minimal output refactoring * modules/vm-cos: initial import, base resources working, no outputs * modules/vm-cos: add variable descriptions * tfdoc: fix parsing in type and default blocks * modules/vm-cos: fix README * tfdoc: fix parsing in type and default blocks * modules/vm-cos: fix README * modules/compute-vm: initial working import (not fully tested) * modules/vm-cos: move to not-ready * tfdoc: fix variable defaults formatting * modules: update README files with tfdoc fixes * modules: add initial examples * gke-nodepool: initial import, untested * gke nodepool: add README, fix location variable, set node count default to 1 * gke cluster: fix private cluster variables * gke nodepool: fix README title * gke cluster: add output for cluster location * gke nodepool: add missing variables for project id and cluster name, remove default from location variable, fix gke version assignment * gke nodepool: update README * net-cloudnat: fix router name when creating default router * fix variables used for address and router optional creation * vpn dynamic: fix README * modules/net-vpn-dynamic: fix router name output * modules/compute-vm: remove unused variable * modules/compute-vm-cos-coredns: initial import * Update foundations modules versions (#26) * update foundations modules versions * update Terraform version to v0.12.19 in CI test configuration * backport tfdoc from Ludo's branch (#27) * Update docs using tfdoc format (#28) * update README files * set all types on variables * foundations/environments: move log filter to a variable, use org for xpn by default * foundations/environments: do not use liens by default * modules/ntp-vpc: better shared_vpc_host variable description * modules/logging-sinks: initial version * modules/logging-sinks: streamline options in sinks variable * modules/compute-vm-cos-coredns: add support for additional files * modules/folders: rename from 'folder' * modules/logging-sinks: fix circular dependencies and improve variables * modules/project: remove extra variable * modules/bigquery: new module with dataset support only * foundations/environments: refactor using local modules * modules/bigquery: better variables, README description and example * modules: fix a few READMEs Co-authored-by: Julio Castillo * modules/net-vpc: README description and examples * modules/net-vpc: tweak README description and examples * modules/net-vpc: tweak README description and examples * modules/net-vpc-firewall: change tag-based rule default ranges, improve README examples and description * modules/compute-vm: README changes * modules/compute-vm: use an object for the service account variable, update README * modules/compute-vm: update README variables table * modules/compute-vm: add TODO list to README * modules/compute-vm: add TODO list to README * modules/compute-vm: add outputs for service account * modules/net-cloudnat: README * modules/net-cloudnat: README * modules/net-cloudnat: add router_create variable * modules/compute-vm: simplify service account variables * modules/net-vpn-dynamic: fix README example, use local secret for both empty string and null * modules/net-vpn-dynamic: improve README example * modules/gke-cluster: minimal README tweaks * modules/kms: fix ephemeral keys resource name * modules/iam-service-accounts: add storage roles * modules/gke-nodepool: fix node default scopes * New project variable to prevent deletion of default network (#32) * New project variable to prevent deletion of default network This is a workaround to fix terraform-google-modules/cloud-foundation-fabric#31 while the GCP terraform provider is fixed * Add TODOs to remove workarounds in the project module * Fix Cloud Build files * modules/gke-nodepool: add monitoring scope to defaults * modules/iam-service-accounts: add support for IAM bindings onthe service accounts * playground module in sandbox, remove not ready modules * Fix ci configurations in development branch (#33) * try fixing ci confgurations * add exclusion match to ci boilerplate check * add skip boilerplate comment to compute-vm-cos-coredns template fragment * modules/gke-cluster: fix boilerplate in outputs * Simplify tests, re-enable CI * add instance group support to compute-vm, start tests refactoring * modules/compute-vm: group fixes, tests * modules/compute-vm: minimal test beautification * simplify top-level pytest fixture * modules/dns: tests and minor tweaks * fix missing boilerplate in tests * re-add requirements file to tests folder * re-enable tests in ci build configuration * Folder module tests and fixes (#38) * folder tests wip * modules/folders: tests and tweaks * update folders and compute-vm README files * modules/gcs: tests and minor tweaks * Create README.md * Update README.md * Update README.md * Update README.md * Added docker image for strongSwan * Add support for routes and tests to net-vpc module (#39) * modules/net-vpc: add routes (untested) * initial tests * modules/net-vpc: add test for flow logs * modules/net-vpc: split tests into two separate files * modules/net-vpc: routes test * modules/net-vpc: test routes * Add support for Terraform plugin cache in ci test build file (#40) * add Terraform plugin caching to test ci build configuration * fix mkdir in test build configuration * trigger test check * Refactor dynamic vpn configuration for on-prem-in-a-box module * Fix dynamic vpn for onprem-in-a-box module * Migrate Shared VPC example to local modules (#41) * wip * wip * validated, untested * modules/compute-vm: make service account email in locals resilient to destroy * modules/project: make project id output depend on iam roles * fixes * shared-vpc tweaks * update diagram * update README input output tables * modules/compute-vm: add service account IAM email output * move GKE service account roles at the project level, add GCE service account roles * update diagram and README * modules/project: add extra output for IAM-dependent project id * update modules READMEs * minor tweaks * modules/compute-vm: fix service account output * remove static address from NAT * fix container service agent binding dependency * rename shared vpc * Update README.md * Update README.md * Add static vpn gw to on-prem-in-a-box module * Refactor hub and spoke to use new modules (#42) * modules/compute-vm: saner defaults for service account scopes * hub and spoke refactor, docs still missing * complete hub and spoke * Update README.md * Add toolbox docker container, fix gw routing to the internet * Add DNS Hybrid connectivity parameters * Fix onprem dns zone for the static vpn configuration * Added readme.md for on-prem module * Add new line at the end of the files * Add boilerplate for cloudbuild config files * fix boilerplate in strongswan shell script * Update README.md * include missing file to fix merge conflict * remove missing file to fix merge conflict * include missing file to fix merge conflict (again) * remove content from spurious file used to avoid merge conflicts * Add net-vpc-peering module * Initial commit for hub-and-spoke-peering infrastructure example * Fix typos in infrastructure/ READMEs * remove stale file * use larger resolution version of hub and spoke diagram * Update README.md * Update hub-and-spoke-peerings example to use internal modules * Add initial project tests (#46) * modules/project: make prefix optional * initial project module tests * modules/project: use null for unset parent * modules/dns: backport PR6 from the CFT dns module * Add testing resources including on-prem-in-a-box to hub-and-spoke-peerings example * Fix firewall rules to allow connectivity, switch to custom route advertisement for onprem -> spokes connectivity * Move locals out of main.tf * remove ssh tag from compute-vm variable default * Add ssh tag to the test vms * Update README.md * Update README.md * Update README.md * Hub and spoke peering changes (#48) * rename hub-and-spoke-vpn * add ssh tag to shared-vpc-gke instance * rename and rework hub and spoke peering * fix test requirements * align hub and spoke peering with module contents * diagram * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * minimal fixes to onprem examples variable files * onprem example stub, missing DNS zones and private.googleapis records onprem * add missing boilerplate * Update README.md * Update README.md * infra/onprem: add test instance and minimal outputs * add DNS modules and resource * infra/onprem: diagram and initial README * minor changes to onprem module and example (#49) * update toolbox image * infra/onprem: add zone for private access, add metadata domain to onprem dns * infra/onprem: onnprem service account, add testing procedure in README * Update README.md * infra/onprem: remove extra variable * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * infra/onprem: rename forwarder address variable * Update README: Added explicit --tunnel-through-iap for gcloud compute ssh commands * Update top-level and section READMEs (#50) * top-level README WIP * rewrite top-level README * change top-level README title * remove initial quote in top-level README * Update README.md * Update README.md * Update README.md * foundations README * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * add experimental scheduled cloud function module * scheduled cloud function module: allow disabling schedule * business-units foundation example (#52) * Added folder-units module. * Business units example update (WIP) * Update all BU modules to internal ones * Refactoring business-units example, add billing and org IAM handling * update projects tests for new iam additive naming * update project README for new iam additive naming * streamline bu example and module (#53) Co-authored-by: Ludovico Magnocavallo * align net-vpn-ha interface with the other vpn modules * update module README files * Update README.md * Update README.md * Create CHANGELOG.md * Refactor COS module to be generic (#51) * Create generic COS module and update CoreDNS module to use it * Update compute-vm-cos README * Fix COS README * Update COS example * Skip boilerplate check for COS file template * Make COS module more generic and provide preset configurations * Update COS module documentation * tfdoc: add support for multiple variables files * compute-vm: split boot disk in separate variable file for cos module support * Streamline cos modules (#54) * tfdoc: fix bug in last commit * compute-vm: add support for user-data * compute-vm: restore noncos variable split * remove compute-vm-cos-coredns * compute-vm: revert to original state * cos-container/coredns * fix variables mess * cos/coredns fixes * cos/mysql * remove stale compute-vm-cos module * add test instance to cos modules * tfdoc: add support for multiple output files * cos: add initial READMEs * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * add test apply fixture * cos-coredns: tested * Update README.md * Fix typo * cos-coredns: refactor README * Update README.md * test yaml validity in cos modules tests * cos mysql tests * cos mysql: refactor and test (disk tests missing) * onprem: fix Coredns * cos mysql: additional disk working * cos modules: fix instance disks for no instance * update some modules READMEs * update some modules READMEs * Update README.md * Update README.md * add simple tests for foundations/environments * change default for org id in foundations/environments to avoid errors when none is specified * fix null/empty organization id in foundations/environments * fix errors when destroying on empty state in foundations/environments * fundations/bu: fix errors when destroying with empty state * modules/gcs: make outputs resilient on destroy with empty state * modules/folders: make outputs resilient on destroy with empty state * switch organization_id variable to long form in foundations/bu and modules/folders-unit * Update README.md * infra/shared-vpc: remove duplicate tag attribute from bastion Co-authored-by: Aleksandr Averbukh Co-authored-by: Julio Castillo Co-authored-by: Julio Castillo --- .ci/.terraformrc | 1 + .ci/cloudbuild.lint.yaml | 12 +- .ci/cloudbuild.test.yaml | 21 +- .ci/scripts/check_boilerplate.py | 6 +- CHANGELOG.md | 13 + README.md | 42 ++- docker-images/strongswan/Dockerfile | 34 ++ docker-images/strongswan/README.md | 44 +++ .../strongswan/cloudbuild.yaml | 31 +- docker-images/strongswan/entrypoint.sh | 35 ++ docker-images/strongswan/ipsec-vti.sh | 66 ++++ docker-images/toolbox/Dockerfile | 30 ++ docker-images/toolbox/README.md | 26 ++ .../toolbox/cloudbuild.yaml | 31 +- docker-images/toolbox/entrypoint.sh | 18 + foundations/README.md | 56 +-- foundations/business-units/README.md | 36 +- foundations/business-units/backend.tf.sample | 4 +- foundations/business-units/main.tf | 258 ++++++------- .../modules/business-unit-folders/README.md | 24 -- .../modules/business-unit-folders/main.tf | 44 --- .../modules/business-unit-folders/outputs.tf | 26 -- .../business-unit-folders/variables.tf | 34 -- foundations/business-units/outputs.tf | 106 +++--- foundations/business-units/providers.tf | 30 +- .../business-units/terraform.tfvars.sample | 19 + foundations/business-units/variables.tf | 117 +++--- foundations/business-units/versions.tf | 30 +- foundations/environments/README.md | 24 +- foundations/environments/locals.tf | 39 ++ foundations/environments/main.tf | 200 +++++----- foundations/environments/outputs.tf | 3 +- foundations/environments/variables.tf | 110 +++--- foundations/environments/versions.tf | 2 +- infrastructure/README.md | 34 +- .../hub-and-spoke-peering/README.md | 75 ++++ .../hub-and-spoke-peering/backend.tf.sample | 9 +- .../hub-and-spoke-peering/diagram.png | Bin 0 -> 153704 bytes infrastructure/hub-and-spoke-peering/main.tf | 280 ++++++++++++++ .../hub-and-spoke-peering/outputs.tf | 21 ++ .../hub-and-spoke-peering/variables.tf | 51 +++ .../hub-and-spoke-peering/versions.tf | 25 ++ infrastructure/hub-and-spoke-vpn/README.md | 54 +++ .../backend.tf.sample | 0 infrastructure/hub-and-spoke-vpn/diagram.png | Bin 0 -> 199253 bytes infrastructure/hub-and-spoke-vpn/main.tf | 308 ++++++++++++++++ infrastructure/hub-and-spoke-vpn/outputs.tf | 21 ++ .../provider.tf | 0 infrastructure/hub-and-spoke-vpn/variables.tf | 68 ++++ infrastructure/hub-and-spoke-vpns/README.md | 79 ---- infrastructure/hub-and-spoke-vpns/diagram.png | Bin 146632 -> 0 bytes infrastructure/hub-and-spoke-vpns/main.tf | 344 ------------------ infrastructure/hub-and-spoke-vpns/outputs.tf | 82 ----- .../terraform.tfvars.sample | 3 - .../hub-and-spoke-vpns/test-resources.tf | 158 -------- .../hub-and-spoke-vpns/variables.tf | 132 ------- .../onprem-google-access-dns/README.md | 161 ++++++++ .../onprem-google-access-dns/assets/Corefile | 21 ++ .../onprem-google-access-dns/diagram.png | Bin 0 -> 143289 bytes .../onprem-google-access-dns/main.tf | 244 +++++++++++++ .../onprem-google-access-dns/outputs.tf | 39 ++ .../onprem-google-access-dns/variables.tf | 64 ++++ .../versions.tf | 2 +- infrastructure/shared-vpc-gke/README.md | 73 ++++ .../backend.tf.sample | 0 .../diagram.gcpdraw | 21 +- infrastructure/shared-vpc-gke/diagram.png | Bin 0 -> 95353 bytes infrastructure/shared-vpc-gke/main.tf | 249 +++++++++++++ infrastructure/shared-vpc-gke/outputs.tf | 54 +++ infrastructure/shared-vpc-gke/variables.tf | 87 +++++ .../shared-vpc-gke}/versions.tf | 0 .../shared-vpc/.terraform.tfstate.lock.info | 1 - infrastructure/shared-vpc/README.md | 61 ---- infrastructure/shared-vpc/diagram.png | Bin 159686 -> 0 bytes infrastructure/shared-vpc/locals.tf | 58 --- infrastructure/shared-vpc/main.tf | 160 -------- infrastructure/shared-vpc/outputs.tf | 36 -- infrastructure/shared-vpc/test-resources.tf | 167 --------- infrastructure/shared-vpc/variables.tf | 131 ------- modules/README.md | 47 +++ .../cloud-function-scheduled/README.md | 42 +++ .../cloud-function-scheduled/main.tf | 133 +++++++ .../cloud-function-scheduled/outputs.tf | 35 ++ .../cloud-function-scheduled/variables.tf | 100 +++++ .../cloud-function-scheduled/versions.tf | 19 + modules/bigquery/README.md | 69 ++++ modules/bigquery/main.tf | 67 ++++ modules/bigquery/outputs.tf | 47 +++ modules/bigquery/variables.tf | 86 +++++ modules/bigquery/versions.tf | 19 + modules/compute-vm/README.md | 113 ++++++ modules/compute-vm/instance_group.tf | 215 +++++++++++ modules/compute-vm/main.tf | 226 ++++++++++++ modules/compute-vm/outputs.tf | 110 ++++++ modules/compute-vm/variables.tf | 231 ++++++++++++ modules/cos-container/.gitignore | 1 + modules/cos-container/README.md | 25 ++ modules/cos-container/coredns/Corefile | 6 + modules/cos-container/coredns/Corefile-hosts | 9 + modules/cos-container/coredns/README.md | 93 +++++ .../cos-container/coredns/cloud-config.yaml | 83 +++++ modules/cos-container/coredns/instance.tf | 1 + modules/cos-container/coredns/main.tf | 41 +++ .../cos-container/coredns/outputs-instance.tf | 1 + modules/cos-container/coredns/outputs.tf | 20 + .../coredns/variables-instance.tf | 1 + modules/cos-container/coredns/variables.tf | 55 +++ modules/cos-container/instance.tf | 87 +++++ modules/cos-container/mysql/.gitignore | 2 + modules/cos-container/mysql/README.md | 100 +++++ modules/cos-container/mysql/cloud-config.yaml | 117 ++++++ modules/cos-container/mysql/instance.tf | 1 + modules/cos-container/mysql/main.tf | 30 ++ .../cos-container/mysql/outputs-instance.tf | 1 + modules/cos-container/mysql/outputs.tf | 20 + .../cos-container/mysql/variables-instance.tf | 1 + modules/cos-container/mysql/variables.tf | 61 ++++ modules/cos-container/outputs-instance.tf | 24 ++ modules/cos-container/variables-instance.tf | 34 ++ modules/dns/README.md | 48 +++ modules/dns/main.tf | 129 +++++++ modules/dns/outputs.tf | 40 ++ modules/dns/variables.tf | 97 +++++ modules/dns/versions.tf | 19 + modules/folders-unit/README.md | 56 +++ modules/folders-unit/locals.tf | 63 ++++ modules/folders-unit/main.tf | 109 ++++++ modules/folders-unit/outputs.tf | 59 +++ modules/folders-unit/variables.tf | 122 +++++++ modules/folders-unit/versions.tf | 19 + modules/folders/README.md | 45 +++ modules/folders/main.tf | 50 +++ modules/folders/outputs.tf | 63 ++++ modules/folders/variables.tf | 38 ++ modules/folders/versions.tf | 19 + modules/gcs/README.md | 54 +++ modules/gcs/main.tf | 62 ++++ modules/gcs/outputs.tf | 63 ++++ modules/gcs/variables.tf | 79 ++++ modules/gcs/versions.tf | 19 + modules/gke-cluster/README.md | 77 ++++ modules/gke-cluster/main.tf | 197 ++++++++++ modules/gke-cluster/outputs.tf | 41 +++ modules/gke-cluster/variables.tf | 236 ++++++++++++ modules/gke-cluster/versions.tf | 19 + modules/gke-nodepool/README.md | 56 +++ modules/gke-nodepool/main.tf | 111 ++++++ modules/gke-nodepool/outputs.tf | 20 + modules/gke-nodepool/variables.tf | 198 ++++++++++ modules/gke-nodepool/versions.tf | 19 + modules/iam-service-accounts/README.md | 59 +++ modules/iam-service-accounts/main.tf | 155 ++++++++ modules/iam-service-accounts/outputs.tf | 66 ++++ modules/iam-service-accounts/variables.tf | 80 ++++ modules/iam-service-accounts/versions.tf | 19 + modules/kms/README.md | 83 +++++ modules/kms/main.tf | 73 ++++ modules/kms/outputs.tf | 45 +++ modules/kms/variables.tf | 70 ++++ modules/kms/versions.tf | 19 + modules/logging-sinks/README.md | 31 ++ modules/logging-sinks/main.tf | 97 +++++ modules/logging-sinks/outputs.tf | 30 ++ modules/logging-sinks/variables.tf | 54 +++ modules/logging-sinks/versions.tf | 19 + modules/net-address/README.md | 35 ++ modules/net-address/main.tf | 44 +++ modules/net-address/outputs.tf | 48 +++ modules/net-address/variables.tf | 65 ++++ modules/net-address/versions.tf | 19 + modules/net-cloudnat/README.md | 44 +++ modules/net-cloudnat/main.tf | 57 +++ modules/net-cloudnat/outputs.tf | 40 ++ modules/net-cloudnat/variables.tf | 98 +++++ modules/net-cloudnat/versions.tf | 19 + modules/net-vpc-firewall/README.md | 77 ++++ modules/net-vpc-firewall/main.tf | 145 ++++++++ modules/net-vpc-firewall/outputs.tf | 56 +++ modules/net-vpc-firewall/variables.tf | 74 ++++ modules/net-vpc-firewall/versions.tf | 19 + modules/net-vpc-peering/README.md | 65 ++++ modules/net-vpc-peering/main.tf | 50 +++ modules/net-vpc-peering/outputs.tf | 30 ++ modules/net-vpc-peering/variables.tf | 49 +++ modules/net-vpc-peering/versions.tf | 19 + modules/net-vpc/README.md | 141 +++++++ modules/net-vpc/main.tf | 207 +++++++++++ modules/net-vpc/outputs.tf | 80 ++++ modules/net-vpc/variables.tf | 137 +++++++ modules/net-vpc/versions.tf | 19 + modules/net-vpn-dynamic/README.md | 72 ++++ modules/net-vpn-dynamic/main.tf | 182 +++++++++ modules/net-vpn-dynamic/outputs.tf | 75 ++++ modules/net-vpn-dynamic/variables.tf | 104 ++++++ modules/net-vpn-dynamic/versions.tf | 19 + modules/net-vpn-ha/README.md | 159 ++++++++ modules/net-vpn-ha/main.tf | 169 +++++++++ modules/net-vpn-ha/outputs.tf | 80 ++++ modules/net-vpn-ha/variables.tf | 111 ++++++ modules/net-vpn-ha/versions.tf | 19 + modules/net-vpn-static/README.md | 52 +++ modules/net-vpn-static/main.tf | 101 +++++ modules/net-vpn-static/outputs.tf | 65 ++++ modules/net-vpn-static/variables.tf | 73 ++++ modules/net-vpn-static/versions.tf | 19 + modules/on-prem-in-a-box/README.md | 131 +++++++ modules/on-prem-in-a-box/assets/Corefile | 11 + .../assets/dynamic-vpn-gw-cloud-init.yaml | 319 ++++++++++++++++ .../assets/static-vpn-gw-cloud-init.yaml | 225 ++++++++++++ modules/on-prem-in-a-box/main.tf | 130 +++++++ modules/on-prem-in-a-box/outputs.tf | 43 +++ modules/on-prem-in-a-box/variables.tf | 118 ++++++ modules/project/README.md | 59 +++ modules/project/main.tf | 126 +++++++ modules/project/outputs.tf | 74 ++++ modules/project/variables.tf | 109 ++++++ modules/project/versions.tf | 19 + tests/__init__.py | 7 +- tests/conftest.py | 44 ++- tests/foundations/__init__.py | 7 +- .../business_units/terraform.tfvars | 9 - .../business_units/test_folders.py | 42 --- .../business_units/test_outputs.py | 48 --- .../business_units/test_projects.py | 46 --- .../business_units/test_service_accounts.py | 34 -- tests/foundations/environments/__init__.py | 7 +- tests/foundations/environments/conftest.py | 27 -- .../foundations/environments/fixture/main.tf | 30 ++ .../environments/fixture/variables.tf | 76 ++++ .../foundations/environments/terraform.tfvars | 7 - .../foundations/environments/test_outputs.py | 47 --- tests/foundations/environments/test_plan.py | 53 +++ .../foundations/environments/test_projects.py | 44 --- .../environments/test_service_accounts.py | 34 -- .../hub_and_spoke_vpns/conftest.py | 27 -- .../hub_and_spoke_vpns/terraform.tfvars | 3 - .../hub_and_spoke_vpns/test_cloud_routers.py | 38 -- .../hub_and_spoke_vpns/test_firewall.py | 37 -- .../hub_and_spoke_vpns/test_outputs.py | 34 -- .../hub_and_spoke_vpns/test_vpns.py | 35 -- .../shared_vpc/terraform.tfvars | 4 - .../infrastructure/shared_vpc/test_outputs.py | 29 -- .../shared_vpc/test_svpc_resources.py | 62 ---- tests/modules/__init__.py | 15 + .../compute_vm}/__init__.py | 4 +- tests/modules/compute_vm/fixture/main.tf | 34 ++ tests/modules/compute_vm/fixture/variables.tf | 40 ++ tests/modules/compute_vm/test_plan.py | 46 +++ tests/modules/compute_vm/test_plan_group.py | 44 +++ .../cos_container_coredns}/__init__.py | 4 +- .../cos_container_coredns/fixture/main.tf | 24 ++ .../cos_container_coredns/fixture/outputs.tf | 19 + .../fixture/variables.tf | 50 +++ .../cos_container_coredns/test_apply.py | 34 ++ .../cos_container_mysql}/__init__.py | 6 +- .../cos_container_mysql/fixture/main.tf | 26 ++ .../cos_container_mysql/fixture/outputs.tf | 19 + .../cos_container_mysql/fixture/variables.tf | 54 +++ .../modules/cos_container_mysql/test_apply.py | 55 +++ tests/modules/dns/__init__.py | 13 + tests/modules/dns/fixture/main.tf | 29 ++ tests/modules/dns/fixture/variables.tf | 43 +++ tests/modules/dns/test_plan.py | 80 ++++ tests/modules/folders/__init__.py | 13 + tests/modules/folders/fixture/main.tf | 23 ++ tests/modules/folders/fixture/variables.tf | 25 ++ tests/modules/folders/test_plan.py | 49 +++ tests/modules/gcs/__init__.py | 13 + tests/modules/gcs/fixture/main.tf | 28 ++ tests/modules/gcs/fixture/variables.tf | 55 +++ tests/modules/gcs/test_plan.py | 81 +++++ tests/modules/net-vpc/__init__.py | 13 + tests/modules/net-vpc/fixture/main.tf | 34 ++ tests/modules/net-vpc/fixture/variables.tf | 124 +++++++ tests/modules/net-vpc/test_plan.py | 90 +++++ tests/modules/net-vpc/test_plan_subnets.py | 79 ++++ tests/modules/project/__init__.py | 13 + tests/modules/project/fixture/main.tf | 35 ++ tests/modules/project/fixture/variables.tf | 85 +++++ tests/modules/project/test_plan.py | 42 +++ tests/requirements.txt | 6 +- tools/tfdoc/tfdoc.py | 17 +- 282 files changed, 14289 insertions(+), 2752 deletions(-) create mode 100644 .ci/.terraformrc create mode 100644 CHANGELOG.md create mode 100644 docker-images/strongswan/Dockerfile create mode 100644 docker-images/strongswan/README.md rename tests/infrastructure/shared_vpc/conftest.py => docker-images/strongswan/cloudbuild.yaml (53%) create mode 100644 docker-images/strongswan/entrypoint.sh create mode 100644 docker-images/strongswan/ipsec-vti.sh create mode 100644 docker-images/toolbox/Dockerfile create mode 100644 docker-images/toolbox/README.md rename tests/foundations/business_units/conftest.py => docker-images/toolbox/cloudbuild.yaml (53%) create mode 100644 docker-images/toolbox/entrypoint.sh delete mode 100644 foundations/business-units/modules/business-unit-folders/README.md delete mode 100644 foundations/business-units/modules/business-unit-folders/main.tf delete mode 100644 foundations/business-units/modules/business-unit-folders/outputs.tf delete mode 100644 foundations/business-units/modules/business-unit-folders/variables.tf create mode 100644 foundations/business-units/terraform.tfvars.sample create mode 100644 foundations/environments/locals.tf create mode 100644 infrastructure/hub-and-spoke-peering/README.md rename tests/infrastructure/__init__.py => infrastructure/hub-and-spoke-peering/backend.tf.sample (86%) create mode 100644 infrastructure/hub-and-spoke-peering/diagram.png create mode 100644 infrastructure/hub-and-spoke-peering/main.tf create mode 100644 infrastructure/hub-and-spoke-peering/outputs.tf create mode 100644 infrastructure/hub-and-spoke-peering/variables.tf create mode 100644 infrastructure/hub-and-spoke-peering/versions.tf create mode 100644 infrastructure/hub-and-spoke-vpn/README.md rename infrastructure/{hub-and-spoke-vpns => hub-and-spoke-vpn}/backend.tf.sample (100%) create mode 100644 infrastructure/hub-and-spoke-vpn/diagram.png create mode 100644 infrastructure/hub-and-spoke-vpn/main.tf create mode 100644 infrastructure/hub-and-spoke-vpn/outputs.tf rename infrastructure/{hub-and-spoke-vpns => hub-and-spoke-vpn}/provider.tf (100%) create mode 100644 infrastructure/hub-and-spoke-vpn/variables.tf delete mode 100644 infrastructure/hub-and-spoke-vpns/README.md delete mode 100644 infrastructure/hub-and-spoke-vpns/diagram.png delete mode 100644 infrastructure/hub-and-spoke-vpns/main.tf delete mode 100644 infrastructure/hub-and-spoke-vpns/outputs.tf delete mode 100644 infrastructure/hub-and-spoke-vpns/terraform.tfvars.sample delete mode 100644 infrastructure/hub-and-spoke-vpns/test-resources.tf delete mode 100644 infrastructure/hub-and-spoke-vpns/variables.tf create mode 100644 infrastructure/onprem-google-access-dns/README.md create mode 100644 infrastructure/onprem-google-access-dns/assets/Corefile create mode 100644 infrastructure/onprem-google-access-dns/diagram.png create mode 100644 infrastructure/onprem-google-access-dns/main.tf create mode 100644 infrastructure/onprem-google-access-dns/outputs.tf create mode 100644 infrastructure/onprem-google-access-dns/variables.tf rename infrastructure/{shared-vpc => onprem-google-access-dns}/versions.tf (95%) create mode 100644 infrastructure/shared-vpc-gke/README.md rename infrastructure/{shared-vpc => shared-vpc-gke}/backend.tf.sample (100%) rename infrastructure/{shared-vpc => shared-vpc-gke}/diagram.gcpdraw (72%) create mode 100644 infrastructure/shared-vpc-gke/diagram.png create mode 100644 infrastructure/shared-vpc-gke/main.tf create mode 100644 infrastructure/shared-vpc-gke/outputs.tf create mode 100644 infrastructure/shared-vpc-gke/variables.tf rename {foundations/business-units/modules/business-unit-folders => infrastructure/shared-vpc-gke}/versions.tf (100%) delete mode 100644 infrastructure/shared-vpc/.terraform.tfstate.lock.info delete mode 100644 infrastructure/shared-vpc/README.md delete mode 100644 infrastructure/shared-vpc/diagram.png delete mode 100644 infrastructure/shared-vpc/locals.tf delete mode 100644 infrastructure/shared-vpc/main.tf delete mode 100644 infrastructure/shared-vpc/outputs.tf delete mode 100644 infrastructure/shared-vpc/test-resources.tf delete mode 100644 infrastructure/shared-vpc/variables.tf create mode 100644 modules/README.md create mode 100644 modules/__experimental/cloud-function-scheduled/README.md create mode 100644 modules/__experimental/cloud-function-scheduled/main.tf create mode 100644 modules/__experimental/cloud-function-scheduled/outputs.tf create mode 100644 modules/__experimental/cloud-function-scheduled/variables.tf create mode 100644 modules/__experimental/cloud-function-scheduled/versions.tf create mode 100644 modules/bigquery/README.md create mode 100644 modules/bigquery/main.tf create mode 100644 modules/bigquery/outputs.tf create mode 100644 modules/bigquery/variables.tf create mode 100644 modules/bigquery/versions.tf create mode 100644 modules/compute-vm/README.md create mode 100644 modules/compute-vm/instance_group.tf create mode 100644 modules/compute-vm/main.tf create mode 100644 modules/compute-vm/outputs.tf create mode 100644 modules/compute-vm/variables.tf create mode 100644 modules/cos-container/.gitignore create mode 100644 modules/cos-container/README.md create mode 100644 modules/cos-container/coredns/Corefile create mode 100644 modules/cos-container/coredns/Corefile-hosts create mode 100644 modules/cos-container/coredns/README.md create mode 100644 modules/cos-container/coredns/cloud-config.yaml create mode 120000 modules/cos-container/coredns/instance.tf create mode 100644 modules/cos-container/coredns/main.tf create mode 120000 modules/cos-container/coredns/outputs-instance.tf create mode 100644 modules/cos-container/coredns/outputs.tf create mode 120000 modules/cos-container/coredns/variables-instance.tf create mode 100644 modules/cos-container/coredns/variables.tf create mode 100644 modules/cos-container/instance.tf create mode 100644 modules/cos-container/mysql/.gitignore create mode 100644 modules/cos-container/mysql/README.md create mode 100644 modules/cos-container/mysql/cloud-config.yaml create mode 120000 modules/cos-container/mysql/instance.tf create mode 100644 modules/cos-container/mysql/main.tf create mode 120000 modules/cos-container/mysql/outputs-instance.tf create mode 100644 modules/cos-container/mysql/outputs.tf create mode 120000 modules/cos-container/mysql/variables-instance.tf create mode 100644 modules/cos-container/mysql/variables.tf create mode 100644 modules/cos-container/outputs-instance.tf create mode 100644 modules/cos-container/variables-instance.tf create mode 100644 modules/dns/README.md create mode 100644 modules/dns/main.tf create mode 100644 modules/dns/outputs.tf create mode 100644 modules/dns/variables.tf create mode 100644 modules/dns/versions.tf create mode 100644 modules/folders-unit/README.md create mode 100644 modules/folders-unit/locals.tf create mode 100644 modules/folders-unit/main.tf create mode 100644 modules/folders-unit/outputs.tf create mode 100644 modules/folders-unit/variables.tf create mode 100644 modules/folders-unit/versions.tf create mode 100644 modules/folders/README.md create mode 100644 modules/folders/main.tf create mode 100644 modules/folders/outputs.tf create mode 100644 modules/folders/variables.tf create mode 100644 modules/folders/versions.tf create mode 100644 modules/gcs/README.md create mode 100644 modules/gcs/main.tf create mode 100644 modules/gcs/outputs.tf create mode 100644 modules/gcs/variables.tf create mode 100644 modules/gcs/versions.tf create mode 100644 modules/gke-cluster/README.md create mode 100644 modules/gke-cluster/main.tf create mode 100644 modules/gke-cluster/outputs.tf create mode 100644 modules/gke-cluster/variables.tf create mode 100644 modules/gke-cluster/versions.tf create mode 100644 modules/gke-nodepool/README.md create mode 100644 modules/gke-nodepool/main.tf create mode 100644 modules/gke-nodepool/outputs.tf create mode 100644 modules/gke-nodepool/variables.tf create mode 100644 modules/gke-nodepool/versions.tf create mode 100644 modules/iam-service-accounts/README.md create mode 100644 modules/iam-service-accounts/main.tf create mode 100644 modules/iam-service-accounts/outputs.tf create mode 100644 modules/iam-service-accounts/variables.tf create mode 100644 modules/iam-service-accounts/versions.tf create mode 100644 modules/kms/README.md create mode 100644 modules/kms/main.tf create mode 100644 modules/kms/outputs.tf create mode 100644 modules/kms/variables.tf create mode 100644 modules/kms/versions.tf create mode 100644 modules/logging-sinks/README.md create mode 100644 modules/logging-sinks/main.tf create mode 100644 modules/logging-sinks/outputs.tf create mode 100644 modules/logging-sinks/variables.tf create mode 100644 modules/logging-sinks/versions.tf create mode 100644 modules/net-address/README.md create mode 100644 modules/net-address/main.tf create mode 100644 modules/net-address/outputs.tf create mode 100644 modules/net-address/variables.tf create mode 100644 modules/net-address/versions.tf create mode 100644 modules/net-cloudnat/README.md create mode 100644 modules/net-cloudnat/main.tf create mode 100644 modules/net-cloudnat/outputs.tf create mode 100644 modules/net-cloudnat/variables.tf create mode 100644 modules/net-cloudnat/versions.tf create mode 100644 modules/net-vpc-firewall/README.md create mode 100644 modules/net-vpc-firewall/main.tf create mode 100644 modules/net-vpc-firewall/outputs.tf create mode 100644 modules/net-vpc-firewall/variables.tf create mode 100644 modules/net-vpc-firewall/versions.tf create mode 100644 modules/net-vpc-peering/README.md create mode 100644 modules/net-vpc-peering/main.tf create mode 100644 modules/net-vpc-peering/outputs.tf create mode 100644 modules/net-vpc-peering/variables.tf create mode 100644 modules/net-vpc-peering/versions.tf create mode 100644 modules/net-vpc/README.md create mode 100644 modules/net-vpc/main.tf create mode 100644 modules/net-vpc/outputs.tf create mode 100644 modules/net-vpc/variables.tf create mode 100644 modules/net-vpc/versions.tf create mode 100644 modules/net-vpn-dynamic/README.md create mode 100644 modules/net-vpn-dynamic/main.tf create mode 100644 modules/net-vpn-dynamic/outputs.tf create mode 100644 modules/net-vpn-dynamic/variables.tf create mode 100644 modules/net-vpn-dynamic/versions.tf create mode 100644 modules/net-vpn-ha/README.md create mode 100644 modules/net-vpn-ha/main.tf create mode 100644 modules/net-vpn-ha/outputs.tf create mode 100644 modules/net-vpn-ha/variables.tf create mode 100644 modules/net-vpn-ha/versions.tf create mode 100644 modules/net-vpn-static/README.md create mode 100644 modules/net-vpn-static/main.tf create mode 100644 modules/net-vpn-static/outputs.tf create mode 100644 modules/net-vpn-static/variables.tf create mode 100644 modules/net-vpn-static/versions.tf create mode 100644 modules/on-prem-in-a-box/README.md create mode 100644 modules/on-prem-in-a-box/assets/Corefile create mode 100644 modules/on-prem-in-a-box/assets/dynamic-vpn-gw-cloud-init.yaml create mode 100644 modules/on-prem-in-a-box/assets/static-vpn-gw-cloud-init.yaml create mode 100644 modules/on-prem-in-a-box/main.tf create mode 100644 modules/on-prem-in-a-box/outputs.tf create mode 100644 modules/on-prem-in-a-box/variables.tf create mode 100644 modules/project/README.md create mode 100644 modules/project/main.tf create mode 100644 modules/project/outputs.tf create mode 100644 modules/project/variables.tf create mode 100644 modules/project/versions.tf delete mode 100644 tests/foundations/business_units/terraform.tfvars delete mode 100644 tests/foundations/business_units/test_folders.py delete mode 100644 tests/foundations/business_units/test_outputs.py delete mode 100644 tests/foundations/business_units/test_projects.py delete mode 100644 tests/foundations/business_units/test_service_accounts.py delete mode 100644 tests/foundations/environments/conftest.py create mode 100644 tests/foundations/environments/fixture/main.tf create mode 100644 tests/foundations/environments/fixture/variables.tf delete mode 100644 tests/foundations/environments/terraform.tfvars delete mode 100644 tests/foundations/environments/test_outputs.py create mode 100644 tests/foundations/environments/test_plan.py delete mode 100644 tests/foundations/environments/test_projects.py delete mode 100644 tests/foundations/environments/test_service_accounts.py delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/conftest.py delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/terraform.tfvars delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/test_cloud_routers.py delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/test_firewall.py delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/test_outputs.py delete mode 100644 tests/infrastructure/hub_and_spoke_vpns/test_vpns.py delete mode 100644 tests/infrastructure/shared_vpc/terraform.tfvars delete mode 100644 tests/infrastructure/shared_vpc/test_outputs.py delete mode 100644 tests/infrastructure/shared_vpc/test_svpc_resources.py create mode 100644 tests/modules/__init__.py rename tests/{infrastructure/shared_vpc => modules/compute_vm}/__init__.py (86%) create mode 100644 tests/modules/compute_vm/fixture/main.tf create mode 100644 tests/modules/compute_vm/fixture/variables.tf create mode 100644 tests/modules/compute_vm/test_plan.py create mode 100644 tests/modules/compute_vm/test_plan_group.py rename tests/{infrastructure/hub_and_spoke_vpns => modules/cos_container_coredns}/__init__.py (86%) create mode 100644 tests/modules/cos_container_coredns/fixture/main.tf create mode 100644 tests/modules/cos_container_coredns/fixture/outputs.tf create mode 100644 tests/modules/cos_container_coredns/fixture/variables.tf create mode 100644 tests/modules/cos_container_coredns/test_apply.py rename tests/{foundations/business_units => modules/cos_container_mysql}/__init__.py (85%) create mode 100644 tests/modules/cos_container_mysql/fixture/main.tf create mode 100644 tests/modules/cos_container_mysql/fixture/outputs.tf create mode 100644 tests/modules/cos_container_mysql/fixture/variables.tf create mode 100644 tests/modules/cos_container_mysql/test_apply.py create mode 100644 tests/modules/dns/__init__.py create mode 100644 tests/modules/dns/fixture/main.tf create mode 100644 tests/modules/dns/fixture/variables.tf create mode 100644 tests/modules/dns/test_plan.py create mode 100644 tests/modules/folders/__init__.py create mode 100644 tests/modules/folders/fixture/main.tf create mode 100644 tests/modules/folders/fixture/variables.tf create mode 100644 tests/modules/folders/test_plan.py create mode 100644 tests/modules/gcs/__init__.py create mode 100644 tests/modules/gcs/fixture/main.tf create mode 100644 tests/modules/gcs/fixture/variables.tf create mode 100644 tests/modules/gcs/test_plan.py create mode 100644 tests/modules/net-vpc/__init__.py create mode 100644 tests/modules/net-vpc/fixture/main.tf create mode 100644 tests/modules/net-vpc/fixture/variables.tf create mode 100644 tests/modules/net-vpc/test_plan.py create mode 100644 tests/modules/net-vpc/test_plan_subnets.py create mode 100644 tests/modules/project/__init__.py create mode 100644 tests/modules/project/fixture/main.tf create mode 100644 tests/modules/project/fixture/variables.tf create mode 100644 tests/modules/project/test_plan.py diff --git a/.ci/.terraformrc b/.ci/.terraformrc new file mode 100644 index 0000000000..6c09c56287 --- /dev/null +++ b/.ci/.terraformrc @@ -0,0 +1 @@ +plugin_cache_dir = "/workspace/.terraform.d/plugin-cache" \ No newline at end of file diff --git a/.ci/cloudbuild.lint.yaml b/.ci/cloudbuild.lint.yaml index c88a13b34c..9fb35a96e4 100644 --- a/.ci/cloudbuild.lint.yaml +++ b/.ci/cloudbuild.lint.yaml @@ -14,12 +14,12 @@ steps: - - name: "python:3-alpine" - id: "boilerplate" + - name: python:3-alpine + id: boilerplate args: ["/workspace/.ci/scripts/check_boilerplate.py", "/workspace"] - - name: "wata727/tflint" - id: "lint" + - name: wata727/tflint + id: lint args: ["/workspace"] tags: - - "ci" - - "lint" + - ci + - lint diff --git a/.ci/cloudbuild.test.yaml b/.ci/cloudbuild.test.yaml index 24de3dfad4..d248a3cb26 100644 --- a/.ci/cloudbuild.test.yaml +++ b/.ci/cloudbuild.test.yaml @@ -23,28 +23,21 @@ steps: wget https://releases.hashicorp.com/terraform/${_TERRAFORM_VERSION}/terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && unzip terraform_${_TERRAFORM_VERSION}_linux_amd64.zip -d /builder/home/.local/bin && rm terraform_${_TERRAFORM_VERSION}_linux_amd64.zip && - chmod 755 /builder/home/.local/bin/terraform - # TODO(ludoo): split into two triggers with different filters + chmod 755 /builder/home/.local/bin/terraform && + mkdir -p /workspace/.terraform.d/plugin-cache + # TODO(ludoo): add a step that detects change files and sets tests to run - name: python:3-alpine - id: test-foundations + id: test-modules entrypoint: pytest args: - -v - - tests/foundations + - tests/modules env: - PATH=/usr/local/bin:/usr/bin:/bin:/builder/home/.local/bin - - name: python:3-alpine - id: test-infrastructure - entrypoint: pytest - args: - - -v - - tests/infrastructure - env: - - PATH=/usr/local/bin:/usr/bin:/bin:/builder/home/.local/bin - - PYTHONDONTWRITEBYTECODE=true + - TF_CLI_CONFIG_FILE=/workspace/.ci/.terraformrc substitutions: - _TERRAFORM_VERSION: 0.12.19 + _TERRAFORM_VERSION: 0.12.20 tags: - "ci" diff --git a/.ci/scripts/check_boilerplate.py b/.ci/scripts/check_boilerplate.py index 762d67d76e..a3bfa03e76 100755 --- a/.ci/scripts/check_boilerplate.py +++ b/.ci/scripts/check_boilerplate.py @@ -21,6 +21,7 @@ _EXCLUDE_DIRS = ('.git', '.terraform') +_EXCLUDE_RE = re.compile(r'# skip boilerplate check') _MATCH_FILES = ( 'Dockerfile', '.py', '.sh', '.tf', '.yaml', '.yml' ) @@ -40,8 +41,11 @@ def main(dir): for fname in files: if fname in _MATCH_FILES or os.path.splitext(fname)[1] in _MATCH_FILES: fpath = os.path.abspath(os.path.join(root, fname)) + content = open(fpath).read() + if _EXCLUDE_RE.search(content): + continue try: - if not _MATCH_RE.search(open(fpath).read()): + if not _MATCH_RE.search(content): errors.append(fpath) except (IOError, OSError): warnings.append(fpath) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..abf38dd2b0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +## [1.0.0] - 2020-03-27 + +- merge development branch with suite of new modules and end-to-end examples + + +[Unreleased]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/terraform-google-modules/cloud-foundation-fabric/compare/v0.1...v1.0 diff --git a/README.md b/README.md index 57644e9e13..7ce72a3317 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,41 @@ -# Cloud Foundation Toolkit - Fabric +# Terraform Examples and Modules for Google Cloud -Cloud Foundation Fabric provides end-to-end Terraform code examples on GCP, which are meant for prototyping and as minimal samples to aid in designing real-world infrastructures. As such, these samples are meant to be adapted and updated for your different use cases, and often do not implement GCP security best practices for production use. +This repository provides **end-to-end examples** and a **suite of Terraform modules** for Google Cloud, which support different use cases: -All the examples leverage composition, combining different Cloud Foundation Toolkit modules to realize an integrated design. Additional modules can be combined in to tailor the examples to specific needs, and to implement additional best practices. You can check the [full list of Cloud Foundation Toolkit modules here](https://github.com/terraform-google-modules). +- starter kits used to bootstrap real-word cloud foundations and infrastructure +- reference examples used to deep dive on network patterns or product features +- composable modules that support quick prototyping and testing +- a comprehensive source of lean modules that lend themselves well to changes -The examples are organized into two main sections: GCP foundational design, and infrastructure design +The whole repository is meant to be cloned as a single unit, and then forked into separate owned repositories to seed production usage, or used as-is and periodically updated as a complete toolkit for prototyping. -## Foundational examples +Both the examples and modules require some measure of Terraform skills to be used effectively. If you are looking for a feature-rich black box to manage project or product creation with minimal specific skills, you might be better served by the [Cloud Foundation Toolkit](https://registry.terraform.io/modules/terraform-google-modules) suite of modules. -Foundational examples deal with organization-level management of GCP resources, and take care of folder hierarchy, initial automation requirements (service accounts, GCS buckets), and high level best practices like audit log exports and organization policies. +## End-to-end examples -They are simplified versions of real-life use cases, and put a particular emphasis on separation of duties at the environment or tenant level, and decoupling high level permissions from the day to day running of infrastructure automation. More details and the actual examples are available in the [foundations folder](foundations). +The examples in this repository are split in two main sections: **foundational examples** that bootstrap the organizational hierarchy and automation prerequisites, and **infrastructure scenarios** that implement core networking patterns or features. -## Infrastructure examples +Currently available examples: -Infrastructure examples showcase typical networking configurations on GCP, and are meant to illustrate how to automate them with Terraform, and to offer an easy way of testing different scenarios. Like the foundational examples, they are simplified versions of real-life use cases. More details and the actual examples are available in the [infrastructure folder](infrastructure). +- **foundations** - [single level hierarchy](./foundations/environments/) (environments), [multiple level hierarchy](./foundations/business-units/) (business units + environments) +- **infrastructure** - [hub and spoke via peering](./infrastructure/hub-and-spoke-peering/), [hub and spoke via VPN](./infrastructure/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./infrastructure/onprem-google-access-dns/), [Shared VPC with GKE support](./infrastructure/shared-vpc-gke/) + +For more information see the README files in the [foundations](./foundations/) and [infrastructure](./infrastructure/) folders. + +## Modules + +The suite of modules in this repository are designed for rapid composition and reuse, and to be reasonably simple and readable so that they can be forked and changed where use of third party code and sources is not allowed. + +All modules share a similar interface where each module tries to stay close to the underlying provider resources, support IAM together with resource creation and modification, offer the option of creating multiple resources where it makes sense (eg not for projects), and be completely free of side-effects (eg no external commands). + +The current list of modules supports most of the core foundational and networking components used to design end-to-end infrastructure, with more modules in active development for specialized compute, security, and data scenarios. + +Currently available modules: + +- **foundational** - [folders](./modules/folders), [log sinks](./modules/logging-sinks), [project](./modules/project), [service accounts](./modules/iam-service-accounts) +- **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), VPN ([static](./modules/net-vpn-static), [dynamic](./modules/net-vpn-dynamic), [HA](./modules/net-vpn-ha)), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns) +- **compute** - [VM/VM group](./modules/compute-vm), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [COS container](./modules/compute-vm-cos-coredns) +- **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery) +- **other** - [KMS](./modules/kms), [on-premises in Docker](./modules/on-prem-in-a-box) + +For more information and usage examples see each module's README file. diff --git a/docker-images/strongswan/Dockerfile b/docker-images/strongswan/Dockerfile new file mode 100644 index 0000000000..a804c9203e --- /dev/null +++ b/docker-images/strongswan/Dockerfile @@ -0,0 +1,34 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine:latest + +RUN set -xe \ + && apk add --no-cache strongswan bash sudo + +COPY entrypoint.sh /entrypoint.sh +RUN chmod 0755 /entrypoint.sh + +COPY ipsec-vti.sh /var/lib/strongswan/ipsec-vti.sh +RUN chmod 0755 /var/lib/strongswan/ipsec-vti.sh + +RUN echo 'ipsec ALL=NOPASSWD:SETENV:/usr/sbin/ipsec,/sbin/ip,/sbin/sysctl' > /etc/sudoers.d/ipsec +RUN chmod 0440 /etc/sudoers.d/ipsec + +ENV VPN_DEVICE=eth0 +ENV LAN_NETWORKS=192.168.0.0/24 + +EXPOSE 500/udp 4500/udp + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker-images/strongswan/README.md b/docker-images/strongswan/README.md new file mode 100644 index 0000000000..42225416b2 --- /dev/null +++ b/docker-images/strongswan/README.md @@ -0,0 +1,44 @@ + +# StrongSwan docker container + +### [strongSwan](https://www.strongswan.org/) is an OpenSource IPsec-based VPN Solution + +### Docker compose example +```yaml +version: "3" +services: + vpn: + image: gcr.io/pso-cft-fabric/strongswan:latest + networks: + default: + ipv4_address: 192.168.0.2 + cap_add: + - NET_ADMIN + ports: + - "500:500/udp" + - "4500:4500/udp" + - "179:179/tcp" + privileged: true + volumes: + - "/lib/modules:/lib/modules:ro" + - "/etc/localtime:/etc/localtime:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro" + - "/var/lib/docker-compose/onprem/ipsec/vti.conf:/etc/strongswan.d/vti.conf:ro" + bird: + image: pierky/bird + network_mode: service:vpn + cap_add: + - NET_ADMIN + - NET_BROADCAST + - NET_RAW + privileged: true + volumes: + - "/var/lib/docker-compose/onprem/bird/bird.conf:/etc/bird/bird.conf:ro" + +``` + +### Build +```bash +gcloud builds submit . --config=cloudbuild.yaml +``` diff --git a/tests/infrastructure/shared_vpc/conftest.py b/docker-images/strongswan/cloudbuild.yaml similarity index 53% rename from tests/infrastructure/shared_vpc/conftest.py rename to docker-images/strongswan/cloudbuild.yaml index cb648e15af..793f9497c2 100644 --- a/tests/infrastructure/shared_vpc/conftest.py +++ b/docker-images/strongswan/cloudbuild.yaml @@ -1,10 +1,11 @@ -# Copyright 2019 Google LLC + +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -12,16 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -"Plan fixture." - -import os - -import pytest - - -_TFDIR = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[-3:-1]) - +# In this directory, run the following command to build this builder. +# $ gcloud builds submit . --config=cloudbuild.yaml -@pytest.fixture(scope='package') -def plan(plan): - return plan(_TFDIR) +steps: +- name: 'gcr.io/cloud-builders/docker' + args: + - build + - --tag=gcr.io/$PROJECT_ID/strongswan + - --tag=gcr.io/$PROJECT_ID/strongswan:latest + - . + +images: + - 'gcr.io/$PROJECT_ID/strongswan:latest' + +timeout: 1200s diff --git a/docker-images/strongswan/entrypoint.sh b/docker-images/strongswan/entrypoint.sh new file mode 100644 index 0000000000..afda6f36a7 --- /dev/null +++ b/docker-images/strongswan/entrypoint.sh @@ -0,0 +1,35 @@ +#!/bin/sh -e + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Enable IP forwarding +sysctl -w net.ipv4.ip_forward=1 + +# Stop ipsec when terminating +_stop_ipsec() { + echo "Shutting down strongSwan/ipsec..." + ipsec stop +} +trap _stop_ipsec SIGTERM + +# Making the containter to work as a default gateway for LAN_NETWORKS +iptables -t nat -A POSTROUTING -s ${LAN_NETWORKS} -o ${VPN_DEVICE} -m policy --dir out --pol ipsec -j ACCEPT +iptables -t nat -A POSTROUTING -s ${LAN_NETWORKS} -o ${VPN_DEVICE} -j MASQUERADE + +# Start ipsec +echo "Starting up strongSwan/ipsec..." +ipsec start --nofork "$@" & +child=$! +wait "$child" diff --git a/docker-images/strongswan/ipsec-vti.sh b/docker-images/strongswan/ipsec-vti.sh new file mode 100644 index 0000000000..e686d0c1a1 --- /dev/null +++ b/docker-images/strongswan/ipsec-vti.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# originally published at +# https://cloud.google.com/community/tutorials/using-cloud-vpn-with-strongswan + +set -o nounset +set -o errexit + +IP=$(which ip) + +PLUTO_MARK_OUT_ARR=(${PLUTO_MARK_OUT//// }) +PLUTO_MARK_IN_ARR=(${PLUTO_MARK_IN//// }) + +VTI_TUNNEL_ID=${1} +VTI_REMOTE=${2} +VTI_LOCAL=${3} + +LOCAL_IF="${PLUTO_INTERFACE}" +VTI_IF="vti${VTI_TUNNEL_ID}" +# GCP's MTU is 1460 +GCP_MTU="1460" +# ipsec overhead is 73 bytes, we need to compute new mtu. +VTI_MTU=$((GCP_MTU-73)) + +case "${PLUTO_VERB}" in + up-client) + sudo ${IP} link add ${VTI_IF} type vti local ${PLUTO_ME} remote ${PLUTO_PEER} okey ${PLUTO_MARK_OUT_ARR[0]} ikey ${PLUTO_MARK_IN_ARR[0]} + sudo ${IP} addr add ${VTI_LOCAL} remote ${VTI_REMOTE} dev "${VTI_IF}" + sudo ${IP} link set ${VTI_IF} up mtu ${VTI_MTU} + + # Disable IPSEC Policy + sudo /sbin/sysctl -w net.ipv4.conf.${VTI_IF}.disable_policy=1 + + # Enable loosy source validation, if possible. Otherwise disable validation. + sudo /sbin/sysctl -w net.ipv4.conf.${VTI_IF}.rp_filter=2 || sysctl -w net.ipv4.conf.${VTI_IF}.rp_filter=0 + + # If you would like to use VTI for policy-based you shoud take care of routing by yourselv, e.x. + if [[ "${PLUTO_PEER_CLIENT}" != "0.0.0.0/0" ]]; then + ${IP} r add "${PLUTO_PEER_CLIENT}" dev "${VTI_IF}" + fi + ;; + down-client) + sudo ${IP} tunnel del "${VTI_IF}" + ;; +esac + +# Enable IPv4 forwarding +sudo /sbin/sysctl -w net.ipv4.ip_forward=1 + +# Disable IPSEC Encryption on local net +sudo /sbin/sysctl -w net.ipv4.conf.${LOCAL_IF}.disable_xfrm=1 +sudo /sbin/sysctl -w net.ipv4.conf.${LOCAL_IF}.disable_policy=1 diff --git a/docker-images/toolbox/Dockerfile b/docker-images/toolbox/Dockerfile new file mode 100644 index 0000000000..31bc1a6cb7 --- /dev/null +++ b/docker-images/toolbox/Dockerfile @@ -0,0 +1,30 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +FROM google/cloud-sdk:alpine + +COPY entrypoint.sh /entrypoint.sh +RUN chmod 0755 /entrypoint.sh + +RUN apk update && \ + apk add bash curl bind-tools busybox-extras netcat-openbsd && \ + rm /var/cache/apk/* + +RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl && \ + chmod 755 kubectl && mv kubectl /usr/local/bin/ + +CMD ["/bin/bash"] + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker-images/toolbox/README.md b/docker-images/toolbox/README.md new file mode 100644 index 0000000000..6e2f70c241 --- /dev/null +++ b/docker-images/toolbox/README.md @@ -0,0 +1,26 @@ + +# ToolBox docker container + +Lightweight container with some basic console tools used for testing and probing. + +## Building + +```bash +gcloud builds submit . --config=cloudbuild.yaml +``` + +## Docker compose + +```yaml +version: "3" +services: + vpn: + image: gcr.io/pso-cft-fabric/toolbox:latest + networks: + default: + ipv4_address: 192.168.0.5 + cap_add: + - NET_ADMIN + privileged: true + +``` diff --git a/tests/foundations/business_units/conftest.py b/docker-images/toolbox/cloudbuild.yaml similarity index 53% rename from tests/foundations/business_units/conftest.py rename to docker-images/toolbox/cloudbuild.yaml index cb648e15af..6d123129ae 100644 --- a/tests/foundations/business_units/conftest.py +++ b/docker-images/toolbox/cloudbuild.yaml @@ -1,10 +1,11 @@ -# Copyright 2019 Google LLC + +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -12,16 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -"Plan fixture." - -import os - -import pytest - - -_TFDIR = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[-3:-1]) - +# In this directory, run the following command to build this builder. +# $ gcloud builds submit . --config=cloudbuild.yaml -@pytest.fixture(scope='package') -def plan(plan): - return plan(_TFDIR) +steps: +- name: 'gcr.io/cloud-builders/docker' + args: + - build + - --tag=gcr.io/$PROJECT_ID/toolbox + - --tag=gcr.io/$PROJECT_ID/toolbox:latest + - . + +images: + - 'gcr.io/$PROJECT_ID/toolbox:latest' + +timeout: 1200s diff --git a/docker-images/toolbox/entrypoint.sh b/docker-images/toolbox/entrypoint.sh new file mode 100644 index 0000000000..31f37caea3 --- /dev/null +++ b/docker-images/toolbox/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh -e + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo "Entering sleep..." +trap : TERM INT; (while true; do sleep 1000; done) & wait diff --git a/foundations/README.md b/foundations/README.md index b8799d9c8d..097cd486aa 100644 --- a/foundations/README.md +++ b/foundations/README.md @@ -1,40 +1,48 @@ -# Organization-level bootstrap samples +# Cloud foundation examples -This set of Terraform root modules is designed with two main purposes in mind: automating the organizational layout, and bootstrapping the initial resources and the corresponding IAM roles, which will then be used to automate the actual infrastructure. +The examples in this folder deal with cloud foundations: the set of resources used to **create the organizational hierarchy** (folders and specific IAM roles), **implement top-level initial best practices** (audit log exports, policies) and **bootstrap infrastructure automation** (GCS buckets, service accounts and IAM roles). -Despite being fairly generic, these modules closely match some of the initial automation stages we have implemented in actual customer engagements, and are purposely kept simple to offer a good starting point for further customizations. +The examples are derived from actual production use cases, and are meant to be used as-is, or extended to create more complex hierarchies. The guiding principles they implement are: -There are several advantages in using an initial stage like the ones provided here: +- divide the hierarchy in separate partitions along environment/organization boundaries, to enforce separation of duties and decouple organization admin permissions from the day-to-day running of infrastructure +- keep top-level Terraform code minimal and encapsulate complexity in modules, to ensure readability and allow using code as high level documentation -- automate and parameterize creation of the organizational layout, documenting it through code -- use a single declarative tool to create and manage both prerequisites and the actual infrastructure, eliminating the need for manual commands, scripts or external tools -- enforce separation of duties at the environment (or tenant in multi-tenant architectures) level, by automating creation of per-environment Terraform service accounts, their IAM roles, and GCS buckets to hold state -- decouple and document the use of organization-level permissions from the day to day management of the actual infrastructure, by assigning a minimum but sufficient set of high level IAM roles to Terraform service accounts in an initial stage -- provide a sane place for the creation and management of shared resources that are not tied to a specific environment +## Examples -## Operational considerations +### Environment Hierarchy + + This [example](./environments/) implements a simple one-level oganizational layout, which is commonly used to bootstrap small infrastructures, or in situations where lower level folders are managed with separate, more granular Terraform setups. -This specific type of preliminary automation stage is usually fairly static, only changing when a new environment or shared resource is added or modified, and lends itself well to being applied manually by organization administrators. One secondary advantage of running this initial stage manually, is eliminating the need to create and manage automation credentials that embed sensitive permissions scoped at the organization or root folder level. +One authoritative service account, one bucket and one folder are created for each environment, together with top-level shared resources. This example's simplicity makes it a good starting point to understand and prototype foundational design. -### IAM roles +
-This type of automation stage needs very specific IAM roles on the root node (organization or folder), and additional roles at the organization level if the generated service accounts for automation need to be able to create and manage Shared VPC. The needed roles are: +### Business Unit / Environment Hierarchy -- on the root node Project Creator, Folder Administrator, Logging Administrator -- on the billing account or organization Billing Account Administrator -- on the organization Organization Administrator, if Shared VPC needs to be managed by the automation service accounts + This [example](./business-units/) implements a two-level organizational layout, with a first level usually mapped to business units, and a second level implementing identical environments (prod, test, etc.) under each first-level folder. -### State +This approach maps well to medium sized infrastructures, and can be used as a starting point for more complex scenarios. Separate Terraform stages are then usually implemented for each business unit, implementing fine-grained project and service account creation for individual application teams. +
-This type of stage creates the prerequisites for Terraform automation including the GCS bucket used for its own remote state, so some care needs to be used when running it for the first time, when its GCS bucket has not yet been created. +## Operational considerations -After the first successful `terraform apply`, copy the `backend.tf.sample` file -to `backend.tf`, then set the bucket name to the one shown in the `bootstrap_tf_gcs_bucket` output in the new file. Once that is done, run `terraform apply` again to transfer local state to the remote GCS bucket. From then on, state will be remote. +These examples are always used manually, as they require very high-level permissions and are updated infrequently. -### Things to be aware of +The IAM roles needed are: -Using `count` in Terraform resources has the [well-known limitation](https://github.com/hashicorp/terraform/issues/18767) that changing the variable controlling `count` results in the potential unwanted deletion of resources. +- Project Creator, Folder Administrator, Logging Administrator on the root node (org or folder) +- Billing Account Administrator on the billing account or org +- Organization Administrator if Shared VPC roles have to be granted to the automation service accounts created for each scope -These samples use `count` on the `environments` list variable to manage multiples for key resources like service accounts, GCS buckets, and IAM roles. Environment names are usually stable, but care must still be taken in defining the initial list so that names are final, and names for temporary environments (if any are needed) are last so they don't trigger recreation of resources based on the following elements in the list. +State is local on the first run, then it should be moved to the GCS bucket created by the examples for this specific purpose: -This issue will be addressed in a future release of these examples, by replacing `count` with the new `foreach` construct [introduced in Terraform 0.12.6](https://twitter.com/mitchellh/status/1156661893789966336?lang=en) that uses key-based indexing. +```bash +# first apply +terraform apply +# create backend file +cp backend.tf.sample backend.tf +# edit backend.tf and use bootstrap_tf_gcs_bucket output for GCS bucket name +vi backend.tf +# once done, move local state to GCS bucket +terraform init +``` diff --git a/foundations/business-units/README.md b/foundations/business-units/README.md index 684579bc06..7c74045893 100644 --- a/foundations/business-units/README.md +++ b/foundations/business-units/README.md @@ -2,7 +2,7 @@ This sample creates an organizational layout with two folder levels, where the first level is usually mapped to one business unit or team (infra, data, analytics) and the second level represents enviroments (prod, test). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and the correct roles on those to enforce separation of duties at the environment level. -This layout is well suited for medium-sized infrastructures managed by different sets of teams, and especially where the foundational infrastructure needs to be managed centrally, as the top-level automation service accounts for each environment allow cross-team management of the base resources (projects, IAM, etc.). +This layout is well suited for medium-sized infrastructures managed by different sets of teams, and in cases where the core infrastructure is managed centrally, as the top-level automation service accounts for each environment allow cross-team management of the base resources (projects, IAM, etc.). ![High-level diagram](diagram.png "High-level diagram") @@ -19,7 +19,7 @@ This sample creates several distinct groups of resources: - one project in the shared folder to set up and host centralized audit log exports - one project in the shared folder to hold services used across environments like GCS, GCR, KMS, Cloud Build, etc. -The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging the full array of [Cloud Foundation Toolkit modules](https://github.com/terraform-google-modules), especially in the shared services project. +The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging other [modules from our bundle](../../modules/), or from other sources like the [CFT suite](https://github.com/terraform-google-modules). ## Shared services @@ -31,38 +31,28 @@ This sample uses a top-level folder to encapsulate projects that host resources | name | description | type | required | default | |---|---|:---: |:---:|:---:| | billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| business_unit_1_name | Business unit 1 short name. | string | ✓ | | -| business_unit_2_name | Business unit 2 short name. | string | ✓ | | -| business_unit_3_name | Business unit 3 short name. | string | ✓ | | -| environments | Environment short names. | list(string) | ✓ | | | organization_id | Organization id. | string | ✓ | | | prefix | Prefix used for resources that need unique names. | string | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | -| *gcs_location* | GCS bucket location. | string | | EU | -| *generate_service_account_keys* | Generate and store service account keys in the state file. | bool | | false | +| *audit_filter* | Audit log filter used for the log sink. | string | | ... | +| *environments* | Environment short names. | map(string) | | ... | +| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | +| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *iam_shared_owners* | Shared services project owners, in IAM format. | list(string) | | [] | +| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | | *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *shared_bindings_members* | List of comma-delimited IAM-format members for the additional shared project bindings. | list(string) | | [] | -| *shared_bindings_roles* | List of roles for additional shared project bindings. | list(string) | | [] | -| *terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | ## Outputs | name | description | sensitive | |---|---|:---:| -| audit_logs_bq_dataset | Bigquery dataset for the audit logs export. | | | audit_logs_project | Project that holds the audit logs export resources. | | | bootstrap_tf_gcs_bucket | GCS bucket used for the bootstrap Terraform state. | | -| business_unit_1_environment_folders_ids | Business unit 1 environment folders. | | -| business_unit_1_folder_id | Business unit 1 top-level folder ID. | | -| business_unit_2_environment_folders_ids | Business unit 2 environment folders. | | -| business_unit_2_folder_id | Business unit 2 top-level folder ID. | | -| business_unit_3_environment_folders_ids | Business unit 3 environment folders. | | -| business_unit_3_folder_id | Business unit 3 top-level folder ID. | | -| environment_service_account_keys | Service account keys used to run each environment Terraform modules. | ✓ | -| environment_service_accounts | Service accounts used to run each environment Terraform modules. | | -| environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | | -| shared_folder_id | Shared folder ID. | | +| bu_bi | Business Intelligence attributes. | | +| bu_bi_sa_keys | Business Intelligence service account keys. | ✓ | +| bu_ml | Machine Learning attributes. | | +| bu_ml_sa_keys | Machine Learning service account keys. | ✓ | +| shared_folder_id | Shared folder id. | | | shared_resources_project | Project that holdes resources shared across business units. | | | terraform_project | Project that holds the base Terraform resources. | | diff --git a/foundations/business-units/backend.tf.sample b/foundations/business-units/backend.tf.sample index 19ccfaf66c..d63060c66e 100644 --- a/foundations/business-units/backend.tf.sample +++ b/foundations/business-units/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,4 +19,4 @@ terraform { # run apply again to transfer state bucket = "" } -} \ No newline at end of file +} diff --git a/foundations/business-units/main.tf b/foundations/business-units/main.tf index 2123126170..76b01d144a 100644 --- a/foundations/business-units/main.tf +++ b/foundations/business-units/main.tf @@ -1,130 +1,86 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# TODO(averbukh): simplify log-sink parameters once https://github.com/terraform-google-modules/terraform-google-log-export/issues/28 is done. - -locals { - parent_numeric_id = element(split("/", var.root_node), 1) - log_sink_parent_resource_type = element(split("/", var.root_node), 0) == "organizations" ? "organization" : "folder" - log_sink_name = element(split("/", var.root_node), 0) == "organizations" ? "logs-audit-org-${local.parent_numeric_id}" : "logs-audit-folder-${local.parent_numeric_id}" -} +/** + * Copyright 2020 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. + */ ############################################################################### -# Shared resources folder # +# Terraform top-level resources # ############################################################################### +# Shared folder + module "shared-folder" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = var.root_node - names = ["shared"] + source = "../../modules/folders" + parent = var.root_node + names = ["shared"] } -############################################################################### -# Terraform top-level resources # -############################################################################### - # Terraform project -module "project-tf" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix - name = "terraform" - lien_reason = "terraform" - owners = var.terraform_owners - activate_apis = var.project_services -} - -# Per environment service accounts - -module "service-accounts-tf-environments" { - source = "terraform-google-modules/service-accounts/google" - version = "2.0.2" - project_id = module.project-tf.project_id - org_id = var.organization_id - billing_account_id = var.billing_account_id - prefix = var.prefix - names = var.environments - grant_billing_role = true - generate_keys = var.generate_service_account_keys +module "tf-project" { + source = "../../modules/project" + name = "terraform" + parent = module.shared-folder.id + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { "roles/owner" = var.iam_terraform_owners } + iam_additive_roles = ["roles/owner"] + services = var.project_services } # Bootstrap Terraform state GCS bucket -module "gcs-tf-bootstrap" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" +module "tf-gcs-bootstrap" { + source = "../../modules/gcs" + project_id = module.tf-project.project_id names = ["tf-bootstrap"] - location = var.gcs_location -} - -# Per environment Terraform state GCS buckets - -module "gcs-tf-environments" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" - names = var.environments - location = var.gcs_location - set_admin_roles = true - bucket_admins = zipmap( - var.environments, - module.service-accounts-tf-environments.iam_emails_list - ) + prefix = "${var.prefix}-tf" + location = var.gcs_defaults.location } ############################################################################### -# Business units # +# Business units # ############################################################################### -# Business unit 1 - -module "business-unit-1-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_1_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - -} - -# Business unit 2 - -module "business-unit-2-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_2_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - +module "bu-business-intelligence" { + source = "../../modules/folders-unit" + name = "Business Intelligence" + short_name = "bi" + automation_project_id = module.tf-project.project_id + billing_account_id = var.billing_account_id + environments = var.environments + gcs_defaults = var.gcs_defaults + organization_id = var.organization_id + root_node = var.root_node + # extra variables from the folders-unit module can be used here to grant + # IAM roles to the bu users, configure the automation service accounts, etc. + # iam_roles = ["viewer"] + # iam_members = { viewer = ["user:user@example.com"] } } -# Business unit 3 - -module "business-unit-3-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_3_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - +module "bu-machine-learning" { + source = "../../modules/folders-unit" + name = "Machine Learning" + short_name = "ml" + automation_project_id = module.tf-project.project_id + billing_account_id = var.billing_account_id + environments = var.environments + gcs_defaults = var.gcs_defaults + organization_id = var.organization_id + root_node = var.root_node + # extra variables from the folders-unit module can be used here to grant + # IAM roles to the bu users, configure the automation service accounts, etc. } ############################################################################### @@ -133,42 +89,50 @@ module "business-unit-3-folders" { # Audit logs project -module "project-audit" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix +module "audit-project" { + source = "../../modules/project" name = "audit" - lien_reason = "audit" - viewers = var.audit_viewers - activate_apis = concat(var.project_services, [ + parent = var.root_node + prefix = var.prefix + billing_account = var.billing_account_id + iam_members = { + "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] + "roles/viewer" = var.iam_audit_viewers + } + iam_roles = [ + "roles/bigquery.dataEditor", + "roles/viewer" + ] + services = concat(var.project_services, [ "bigquery.googleapis.com", ]) } -# Audit logs destination on BigQuery - -module "bq-audit-export" { - source = "terraform-google-modules/log-export/google//modules/bigquery" - version = "3.2.0" - project_id = module.project-audit.project_id - dataset_name = "${replace(local.log_sink_name, "-", "_")}" - log_sink_writer_identity = module.log-sink-audit.writer_identity +# audit logs dataset and sink + +module "audit-datasets" { + source = "../../modules/bigquery" + project_id = module.audit-project.project_id + datasets = { + audit_export = { + name = "Audit logs export." + description = "Terraform managed." + location = "EU" + labels = null + options = null + } + } } -# Audit log sink for root node - -module "log-sink-audit" { - source = "terraform-google-modules/log-export/google" - version = "3.2.0" - filter = "logName: \"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName: \"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" - log_sink_name = local.log_sink_name - parent_resource_type = local.log_sink_parent_resource_type - parent_resource_id = local.parent_numeric_id - include_children = "true" - unique_writer_identity = "true" - destination_uri = "${module.bq-audit-export.destination_uri}" +module "audit-log-sinks" { + source = "../../modules/logging-sinks" + parent = var.root_node + destinations = { + audit-logs = "bigquery.googleapis.com/projects/${module.audit-project.project_id}/datasets/${try(module.audit-datasets.names[0], "")}" + } + sinks = { + audit-logs = var.audit_filter + } } ############################################################################### @@ -177,17 +141,19 @@ module "log-sink-audit" { # Shared resources project -module "project-shared-resources" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix - name = "shared" - lien_reason = "shared" - activate_apis = var.project_services - extra_bindings_roles = var.shared_bindings_roles - extra_bindings_members = var.shared_bindings_members +module "shared-project" { + source = "../../modules/project" + name = "shared" + parent = module.shared-folder.id + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { + "roles/owner" = var.iam_shared_owners + } + iam_additive_roles = [ + "roles/owner" + ] + services = var.project_services } # Add further modules here for resources that are common to all business units diff --git a/foundations/business-units/modules/business-unit-folders/README.md b/foundations/business-units/modules/business-unit-folders/README.md deleted file mode 100644 index 89081edb45..0000000000 --- a/foundations/business-units/modules/business-unit-folders/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Two-level folders tree - -This module is a simple wrapper for the [Cloud Foundation Folder module](https://github.com/terraform-google-modules/terraform-google-folders), that manages one folder and one child folder under it, for each name passed in the `environments` variable. It is meant to be used for organizational layouts where a predefined number of folders representing environments, are created as child folders for business units or teams. - -For details on how the IAM variables work, please refer to the [Cloud Foundation Folder module](https://github.com/terraform-google-modules/terraform-google-folders). - - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| business\_unit\_folder\_name | Business Unit Folder name. | string | n/a | yes | -| environments | Environment short names. | list(string) | n/a | yes | -| per\_folder\_admins | List of IAM-style members per folder who will get extended permissions. | list(string) | `` | no | -| root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| business\_unit\_folder\_id | Business Unit Folder ID. | -| environment\_folders\_ids | Environment folders IDs. | - - diff --git a/foundations/business-units/modules/business-unit-folders/main.tf b/foundations/business-units/modules/business-unit-folders/main.tf deleted file mode 100644 index 0b48b5444c..0000000000 --- a/foundations/business-units/modules/business-unit-folders/main.tf +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -############################################################################### -# Business Unit Folder # -############################################################################### - -module "business-unit-folder" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = var.root_node - names = [var.business_unit_folder_name] -} - -############################################################################### -# Environment Folders # -############################################################################### - -module "environment-folders" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = module.business-unit-folder.id - names = var.environments - set_roles = true - per_folder_admins = var.per_folder_admins - folder_admin_roles = [ - "roles/resourcemanager.folderViewer", - "roles/resourcemanager.projectCreator", - "roles/owner", - "roles/compute.networkAdmin", - "roles/compute.xpnAdmin" - ] -} diff --git a/foundations/business-units/modules/business-unit-folders/outputs.tf b/foundations/business-units/modules/business-unit-folders/outputs.tf deleted file mode 100644 index 78e4b5d856..0000000000 --- a/foundations/business-units/modules/business-unit-folders/outputs.tf +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -output "business_unit_folder_id" { - description = "Business Unit Folder ID." - value = module.business-unit-folder.id -} - -output "environment_folders_ids" { - description = "Environment folders IDs." - value = module.environment-folders.ids -} - -# Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. diff --git a/foundations/business-units/modules/business-unit-folders/variables.tf b/foundations/business-units/modules/business-unit-folders/variables.tf deleted file mode 100644 index 936eda6879..0000000000 --- a/foundations/business-units/modules/business-unit-folders/variables.tf +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -variable "business_unit_folder_name" { - description = "Business Unit Folder name." - type = string -} - -variable "environments" { - description = "Environment short names." - type = list(string) -} - -variable "per_folder_admins" { - type = list(string) - description = "List of IAM-style members per folder who will get extended permissions." - default = [] -} - -variable "root_node" { - description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." - type = string -} diff --git a/foundations/business-units/outputs.tf b/foundations/business-units/outputs.tf index b82a102aa0..51d5c21343 100644 --- a/foundations/business-units/outputs.tf +++ b/foundations/business-units/outputs.tf @@ -1,91 +1,75 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ output "terraform_project" { description = "Project that holds the base Terraform resources." - value = module.project-tf.project_id + value = module.tf-project.project_id } output "bootstrap_tf_gcs_bucket" { description = "GCS bucket used for the bootstrap Terraform state." - value = module.gcs-tf-bootstrap.name -} - -output "environment_tf_gcs_buckets" { - description = "GCS buckets used for each environment Terraform state." - value = module.gcs-tf-environments.names -} - -output "environment_service_account_keys" { - description = "Service account keys used to run each environment Terraform modules." - sensitive = true - value = module.service-accounts-tf-environments.keys -} - -output "environment_service_accounts" { - description = "Service accounts used to run each environment Terraform modules." - value = module.service-accounts-tf-environments.emails + value = module.tf-gcs-bootstrap.name } output "shared_folder_id" { - description = "Shared folder ID." + description = "Shared folder id." value = module.shared-folder.id } -output "business_unit_1_folder_id" { - description = "Business unit 1 top-level folder ID." - value = module.business-unit-1-folders.business_unit_folder_id -} - -output "business_unit_1_environment_folders_ids" { - description = "Business unit 1 environment folders." - value = module.business-unit-1-folders.environment_folders_ids -} -output "business_unit_2_folder_id" { - description = "Business unit 2 top-level folder ID." - value = module.business-unit-2-folders.business_unit_folder_id +output "bu_machine_learning" { + description = "Machine Learning attributes." + value = { + unit_folder = module.bu-machine-learning.unit_folder, + env_gcs_buckets = module.bu-machine-learning.env_gcs_buckets + env_folders = module.bu-machine-learning.env_folders + env_service_accounts = module.bu-machine-learning.env_service_accounts + } } -output "business_unit_2_environment_folders_ids" { - description = "Business unit 2 environment folders." - value = module.business-unit-2-folders.environment_folders_ids -} - -output "business_unit_3_folder_id" { - description = "Business unit 3 top-level folder ID." - value = module.business-unit-3-folders.business_unit_folder_id +output "bu_machine_learning_keys" { + description = "Machine Learning service account keys." + sensitive = true + value = module.bu-machine-learning.env_sa_keys } -output "business_unit_3_environment_folders_ids" { - description = "Business unit 3 environment folders." - value = module.business-unit-3-folders.environment_folders_ids +output "bu_business_intelligence" { + description = "Business Intelligence attributes." + value = { + unit_folder = module.bu-business-intelligence.unit_folder, + env_gcs_buckets = module.bu-business-intelligence.env_gcs_buckets + env_folders = module.bu-business-intelligence.env_folders + env_service_accounts = module.bu-business-intelligence.env_service_accounts + } } -output "audit_logs_bq_dataset" { - description = "Bigquery dataset for the audit logs export." - value = module.bq-audit-export.resource_name +output "bu_business_intelligence_keys" { + description = "Business Intelligence service account keys." + sensitive = true + value = module.bu-business-intelligence.env_sa_keys } output "audit_logs_project" { description = "Project that holds the audit logs export resources." - value = module.project-audit.project_id + value = module.audit-project.project_id } output "shared_resources_project" { description = "Project that holdes resources shared across business units." - value = module.project-shared-resources.project_id + value = module.shared-project.project_id } # Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. \ No newline at end of file +# resources, like GCR, GCS buckets, KMS, etc. diff --git a/foundations/business-units/providers.tf b/foundations/business-units/providers.tf index b166f75dc4..d57e94c5de 100644 --- a/foundations/business-units/providers.tf +++ b/foundations/business-units/providers.tf @@ -1,15 +1,17 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 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. + */ -provider "google" {} \ No newline at end of file +provider "google" {} diff --git a/foundations/business-units/terraform.tfvars.sample b/foundations/business-units/terraform.tfvars.sample new file mode 100644 index 0000000000..5545bf3f57 --- /dev/null +++ b/foundations/business-units/terraform.tfvars.sample @@ -0,0 +1,19 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +billing_account_id = "014617-19UCBC-AF02D9" +organization_id= "500001140800" +prefix = "xyz" +root_node = "folders/9572793983696" +generate_keys = true diff --git a/foundations/business-units/variables.tf b/foundations/business-units/variables.tf index 3dbecffd1a..393d76352b 100644 --- a/foundations/business-units/variables.tf +++ b/foundations/business-units/variables.tf @@ -1,21 +1,27 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -variable "audit_viewers" { - description = "Audit project viewers, in IAM format." - type = list(string) - default = [] +variable "audit_filter" { + description = "Audit log filter used for the log sink." + type = string + default = <string | ✓ | | +| billing_account_id | Billing account id used as to create projects. | string | ✓ | | | environments | Environment short names. | list(string) | ✓ | | -| organization_id | Organization id. | string | ✓ | | +| organization_id | Organization id in organizations/nnnnnnnn format. | string | ✓ | | | prefix | Prefix used for resources that need unique names. | string | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *audit_filter* | Audit log filter used for the log sink. | string | | ... | | *gcs_location* | GCS bucket location. | string | | EU | -| *generate_service_account_keys* | Generate and store service account keys in the state file. | bool | | false | -| *grant_xpn_folder_roles* | Grant roles needed for Shared VPC creation to service accounts at the environment folder level. | bool | | true | -| *grant_xpn_org_roles* | Grant roles needed for Shared VPC creation to service accounts at the organization level. | bool | | false | +| *iam_assets_editors* | Shared assets project editors, in IAM format. | list(string) | | [] | +| *iam_assets_owners* | Shared assets project owners, in IAM format. | list(string) | | [] | +| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *iam_billing_config* | Control granting billing user role to service accounts. Target the billing account by default. | object({...}) | | ... | +| *iam_folder_roles* | List of roles granted to each service account on its respective folder (excluding XPN roles). | list(string) | | ... | +| *iam_sharedsvc_owners* | Shared services project owners, in IAM format. | list(string) | | [] | +| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | +| *iam_xpn_config* | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({...}) | | ... | | *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *shared_bindings_members* | List of comma-delimited IAM-format members for the additional shared project bindings. | list(string) | | [] | -| *shared_bindings_roles* | List of roles for additional shared project bindings. | list(string) | | [] | -| *terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | +| *service_account_keys* | Generate and store service account keys in the state file. | bool | | true | ## Outputs @@ -59,5 +62,4 @@ If no shared services are needed, the shared service project module can of cours | environment_service_accounts | Service accounts used to run each environment Terraform modules. | | | environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | | | shared_resources_project | Project that holdes resources shared across environments. | | -| terraform_project | Project that holds the base Terraform resources. | | diff --git a/foundations/environments/locals.tf b/foundations/environments/locals.tf new file mode 100644 index 0000000000..4c803cb7e7 --- /dev/null +++ b/foundations/environments/locals.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + folder_roles = concat(var.iam_folder_roles, local.sa_xpn_folder_role) + organization_id = element(split("/", var.organization_id), 1) + sa_billing_account_role = ( + var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_billing_org_role = ( + ! var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_xpn_folder_role = ( + local.sa_xpn_target_org ? [] : ["roles/compute.xpnAdmin"] + ) + sa_xpn_org_roles = ( + local.sa_xpn_target_org + ? ["roles/compute.xpnAdmin", "roles/resourcemanager.organizationViewer"] + : ["roles/resourcemanager.organizationViewer"] + ) + sa_xpn_target_org = ( + var.iam_xpn_config.target_org + || + substr(var.root_node, 0, 13) == "organizations" + ) +} diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 1d8da9229b..043e0004e7 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -18,80 +18,84 @@ # Terraform project -module "project-tf" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = var.root_node - billing_account = var.billing_account_id - prefix = var.prefix - name = "terraform" - # lien_reason = "terraform" - owners = var.terraform_owners - activate_apis = var.project_services +module "tf-project" { + source = "../../modules/project" + name = "terraform" + parent = var.root_node + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { "roles/owner" = var.iam_terraform_owners } + iam_additive_roles = ["roles/owner"] + services = var.project_services } # per-environment service accounts -module "service-accounts-tf-environments" { - source = "terraform-google-modules/service-accounts/google" - version = "2.0.2" - project_id = module.project-tf.project_id - org_id = var.organization_id - billing_account_id = var.billing_account_id - prefix = var.prefix - names = var.environments - grant_billing_role = true - grant_xpn_roles = var.grant_xpn_org_roles - generate_keys = var.generate_service_account_keys +module "tf-service-accounts" { + source = "../../modules/iam-service-accounts" + project_id = module.tf-project.project_id + names = var.environments + prefix = var.prefix + iam_billing_roles = { + (var.billing_account_id) = ( + var.iam_billing_config.grant ? local.sa_billing_account_role : [] + ) + } + # folder roles are set in the folders module using authoritative bindings + iam_organization_roles = { + (local.organization_id) = concat( + var.iam_billing_config.grant ? local.sa_billing_org_role : [], + var.iam_xpn_config.grant ? local.sa_xpn_org_roles : [] + ) + } + generate_keys = var.service_account_keys } # bootstrap Terraform state GCS bucket -module "gcs-tf-bootstrap" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" +module "tf-gcs-bootstrap" { + source = "../../modules/gcs" + project_id = module.tf-project.project_id names = ["tf-bootstrap"] + prefix = "${var.prefix}-tf" location = var.gcs_location } # per-environment Terraform state GCS buckets -module "gcs-tf-environments" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" - names = var.environments - location = var.gcs_location - set_admin_roles = true - bucket_admins = zipmap( - var.environments, - module.service-accounts-tf-environments.iam_emails_list - ) +module "tf-gcs-environments" { + source = "../../modules/gcs" + project_id = module.tf-project.project_id + names = var.environments + prefix = "${var.prefix}-tf" + location = var.gcs_location + iam_roles = { + for name in var.environments : (name) => ["roles/storage.objectAdmin"] + } + iam_members = { + for name in var.environments : (name) => { + "roles/storage.objectAdmin" = [module.tf-service-accounts.iam_emails[name]] + } + } } ############################################################################### # Top-level folders # ############################################################################### -module "folders-top-level" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = var.root_node - names = var.environments - set_roles = true - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - folder_admin_roles = compact( - [ - "roles/compute.networkAdmin", - "roles/owner", - "roles/resourcemanager.folderViewer", - "roles/resourcemanager.projectCreator", - var.grant_xpn_folder_roles ? "roles/compute.xpnAdmin" : "" - ] - ) +module "environment-folders" { + source = "../../modules/folders" + parent = var.root_node + names = var.environments + iam_roles = { + for name in var.environments : (name) => local.folder_roles + } + iam_members = { + for name in var.environments : (name) => { + for role in local.folder_roles : + (role) => [module.tf-service-accounts.iam_emails[name]] + } + } } ############################################################################### @@ -100,43 +104,50 @@ module "folders-top-level" { # audit logs project -module "project-audit" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" +module "audit-project" { + source = "../../modules/project" + name = "audit" parent = var.root_node - billing_account = var.billing_account_id prefix = var.prefix - name = "audit" - # lien_reason = "audit" - activate_apis = concat(var.project_services, [ + billing_account = var.billing_account_id + iam_members = { + "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] + "roles/viewer" = var.iam_audit_viewers + } + iam_roles = [ + "roles/bigquery.dataEditor", + "roles/viewer" + ] + services = concat(var.project_services, [ "bigquery.googleapis.com", ]) - viewers = var.audit_viewers } -# audit logs destination on BigQuery - -module "bq-audit-export" { - source = "terraform-google-modules/log-export/google//modules/bigquery" - version = "3.2.0" - project_id = module.project-audit.project_id - dataset_name = "logs_audit_${replace(var.environments[0], "-", "_")}" - log_sink_writer_identity = module.log-sink-audit.writer_identity +# audit logs dataset and sink + +module "audit-datasets" { + source = "../../modules/bigquery" + project_id = module.audit-project.project_id + datasets = { + audit_export = { + name = "Audit logs export." + description = "Terraform managed." + location = "EU" + labels = null + options = null + } + } } -# audit log sink -# set the organization as parent to export audit logs for all environments - -module "log-sink-audit" { - source = "terraform-google-modules/log-export/google" - version = "3.2.0" - filter = var.audit_filter - log_sink_name = "logs-audit-${var.environments[0]}" - parent_resource_type = "folder" - parent_resource_id = split("/", module.folders-top-level.ids_list[0])[1] - include_children = "true" - unique_writer_identity = "true" - destination_uri = "${module.bq-audit-export.destination_uri}" +module "audit-log-sinks" { + source = "../../modules/logging-sinks" + parent = var.root_node + destinations = { + audit-logs = "bigquery.googleapis.com/projects/${module.audit-project.project_id}/datasets/${try(module.audit-datasets.names[0], "")}" + } + sinks = { + audit-logs = var.audit_filter + } } ############################################################################### @@ -146,16 +157,19 @@ module "log-sink-audit" { # shared resources project # see the README file for additional options on managing shared services -module "project-shared-resources" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = var.root_node - billing_account = var.billing_account_id - prefix = var.prefix - name = "shared" - activate_apis = var.project_services - extra_bindings_roles = var.shared_bindings_roles - extra_bindings_members = var.shared_bindings_members +module "sharedsvc-project" { + source = "../../modules/project" + name = "sharedsvc" + parent = var.root_node + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { + "roles/owner" = var.iam_sharedsvc_owners + } + iam_additive_roles = [ + "roles/owner" + ] + services = var.project_services } # Add further modules here for resources that are common to all environments diff --git a/foundations/environments/outputs.tf b/foundations/environments/outputs.tf index cca71987d0..f083d1c8a0 100644 --- a/foundations/environments/outputs.tf +++ b/foundations/environments/outputs.tf @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -output "terraform_project" { +/* output "terraform_project" { description = "Project that holds the base Terraform resources." value = module.project-tf.project_id } @@ -60,3 +60,4 @@ output "shared_resources_project" { # Add further outputs here for the additional modules that manage shared # resources, like GCR, GCS buckets, KMS, etc. + */ diff --git a/foundations/environments/variables.tf b/foundations/environments/variables.tf index 720b6333a7..482e5da4c3 100644 --- a/foundations/environments/variables.tf +++ b/foundations/environments/variables.tf @@ -22,14 +22,8 @@ variable "audit_filter" { END } -variable "audit_viewers" { - description = "Audit project viewers, in IAM format." - type = list(string) - default = [] -} - variable "billing_account_id" { - description = "Billing account id used as default for new projects." + description = "Billing account id used as to create projects." type = string } @@ -38,32 +32,79 @@ variable "environments" { type = list(string) } -variable "generate_service_account_keys" { - description = "Generate and store service account keys in the state file." - type = bool - default = false -} - variable "gcs_location" { description = "GCS bucket location." type = string default = "EU" } -variable "grant_xpn_org_roles" { - description = "Grant roles needed for Shared VPC creation to service accounts at the organization level." - type = bool - default = true +variable "iam_assets_editors" { + description = "Shared assets project editors, in IAM format." + type = list(string) + default = [] } -variable "grant_xpn_folder_roles" { - description = "Grant roles needed for Shared VPC creation to service accounts at the environment folder level." - type = bool - default = false +variable "iam_assets_owners" { + description = "Shared assets project owners, in IAM format." + type = list(string) + default = [] +} + +variable "iam_audit_viewers" { + description = "Audit project viewers, in IAM format." + type = list(string) + default = [] +} + +variable "iam_billing_config" { + description = "Control granting billing user role to service accounts. Target the billing account by default." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "iam_folder_roles" { + description = "List of roles granted to each service account on its respective folder (excluding XPN roles)." + type = list(string) + default = [ + "roles/compute.networkAdmin", + "roles/owner", + "roles/resourcemanager.folderViewer", + "roles/resourcemanager.projectCreator", + ] +} + +variable "iam_sharedsvc_owners" { + description = "Shared services project owners, in IAM format." + type = list(string) + default = [] +} + +variable "iam_terraform_owners" { + description = "Terraform project owners, in IAM format." + type = list(string) + default = [] +} + +variable "iam_xpn_config" { + description = "Control granting Shared VPC creation roles to service accounts. Target the root node by default." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = true + } } variable "organization_id" { - description = "Organization id." + description = "Organization id in organizations/nnnnnnnn format." type = string } @@ -77,25 +118,6 @@ variable "root_node" { type = string } -variable "shared_bindings_members" { - description = "List of comma-delimited IAM-format members for the additional shared project bindings." - # example: ["user:a@example.com,b@example.com", "user:c@example.com"] - type = list(string) - default = [] -} -variable "shared_bindings_roles" { - description = "List of roles for additional shared project bindings." - # example: ["roles/storage.objectViewer", "roles/storage.admin"] - type = list(string) - default = [] -} - -variable "terraform_owners" { - description = "Terraform project owners, in IAM format." - type = list(string) - default = [] -} - variable "project_services" { description = "Service APIs enabled by default in new projects." type = list(string) @@ -104,3 +126,9 @@ variable "project_services" { "stackdriver.googleapis.com", ] } + +variable "service_account_keys" { + description = "Generate and store service account keys in the state file." + type = bool + default = true +} diff --git a/foundations/environments/versions.tf b/foundations/environments/versions.tf index 4eb1500c5a..92787679e0 100644 --- a/foundations/environments/versions.tf +++ b/foundations/environments/versions.tf @@ -13,5 +13,5 @@ # limitations under the License. terraform { - required_version = ">= 0.12" + required_version = ">= 0.12.6" } diff --git a/infrastructure/README.md b/infrastructure/README.md index e393e1108b..7c91c87dd7 100644 --- a/infrastructure/README.md +++ b/infrastructure/README.md @@ -1,3 +1,33 @@ -# Infrastructure samples +# Networking and infrastructure examples -These examples showcase typical networking configurations on GCP derived from real-world use cases, and are meant to illustrate how to automate them with Terraform, and to offer an easy way of testing different scenarios. We have a long list of examples we plan on adding, so check back here often. \ No newline at end of file +The examples in this folder implement **typical network topologies** like hub and spoke, or **end-to-end scenarios** that allow testing specific features like on-premises DNS policies and Private Google Access. + +They are meant to be used as minimal but complete strting points to create actual infrastructure, and as playgrounds to experiment with specific Google Cloud features. + +## Examples + +### Hub and Spoke via Peering + + This [example](./hub-and-spoke-peering/) implements a hub and spoke topology via VPC peering, a common design where a landing zone VPC (hub) is conncted to on-premises, and then peered with satellite VPCs (spokes) to further partition the infrastructure. + +The sample highlights the lack of transitivity in peering: the absence of connectivity between spokes, and the need create workarounds for private service access to managed services. One such workarund is shown for private GKE, allowing access from hub and all spokes to GKE masters via a dedicated VPN. +
+ +### Hub and Spoke via Dynamic VPN + + This [example](./hub-and-spoke-vpn/) implements a hub and spoke topology via dynamic VPN tunnels, a common design where peering cannot be used due to limitations on the number of spokes or connectivity to managed services. + +The example shows how to implement spoke transitivity via BGP advertisements, how to expose hub DNS zones to spokes via DNS peering, and allows easy testing of different VPN and BGP configurations. +
+ +### DNS and Private Access for On-premises + + This [example](./onprem-google-access-dns/) uses an emulated on-premises environment running in Docker containers inside a GCE instance, to allow testing specific features like DNS policies, DNS forwarding zones across VPN, and Private Access for On-premises hosts. + +The emulated on-premises environment can be used to test access to different services from outside Google Cloud, by implementing a VPN connection and BGP to Google CLoud via Strongswan and Bird. +
+ +### Shared VPC with GKE and per-subnet support + This [example](./shared-vpc-gke/) shows how to configure a Shared VPC, including the specific IAM configurations needed for GKE, and to give different level of access to the VPC subnets to different identities. + +It is meant to be used as a starting point for most Shared VPC configurations, and to be integrated to the above examples where Shared VPC is needed in more complex network topologies. diff --git a/infrastructure/hub-and-spoke-peering/README.md b/infrastructure/hub-and-spoke-peering/README.md new file mode 100644 index 0000000000..6e4cb87cdc --- /dev/null +++ b/infrastructure/hub-and-spoke-peering/README.md @@ -0,0 +1,75 @@ +# Hub and Spoke via VPC Peering + +This example creates a simple **Hub and Spoke** setup, where the VPC network connects satellite locations (spokes) through a single intermediary location (hub) via [VPC Peering](https://cloud.google.com/vpc/docs/vpc-peering). + +The example shows some of the limitations that need to be taken into account when using VPC Peering, mostly due to the lack of transivity between peerings: + +- no mesh networking between the spokes +- complex support for managed services hosted in tenant VPCs connected via peering (Cloud SQL, GKE, etc.) + +One possible solution to the managed service limitation above is presented here, using a static VPN to establish connectivity to the GKE masters in the tenant project ([courtesy of @drebes](https://github.com/drebes/tf-samples/blob/master/gke-master-from-hub/main.tf#L10)). Other solutions typically involve the use of proxies, as [described in this GKE article](https://cloud.google.com/solutions/creating-kubernetes-engine-private-clusters-with-net-proxies). + +One other topic that needs to be considered when using peering is the limit of 25 peerings in each peering group, which constrains the scalability of design like the one presented here. + +The example has been purposefully kept simple to show how to use and wire the VPC modules together, and so that it can be used as a basis for more complex scenarios. This is the high level diagram: + +![High-level diagram](diagram.png "High-level diagram") + +## Managed resources and services + +This sample creates several distinct groups of resources: + +- one VPC each for hub and each spoke +- one set of firewall rules for each VPC +- one Cloud NAT configuration for each spoke +- one test instance for each spoke +- one GKE cluster with a single nodepool in spoke 2 +- one service account for the GCE instances +- one service account for the GKE nodes +- one static VPN gateway in hub and spoke 2 with a single tunnel each + +## Testing GKE access from spoke 1 + +As mentioned above, a VPN tunnel is used as a workaround to avoid the peering transitivity issue that would prevent any VPC other than spoke 2 to connect to the GKE master. + +To test cluster access, first log on to the spoke 2 instance and confirm cluster and IAM roles are set up correctly: + +```bash +gcloud container clusters get-credentials cluster-1 --zone europe-west1-b +kubectl get all +``` + +The next step is to edit the peering towards the GKE master tenant VPC, and enable export routes. The peering has a name like `gke-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-peer`, you can edit it in the Cloud Console from the *VPC network peering* page or using `gcloud`: + +``` +gcloud compute networks peerings list +# find the gke-xxxxxxxxxxxxxxxxxxxx-xxxx-xxxx-peer in the spoke-2 network +gcloud compute networks peerings update [peering name from above] \ + --network spoke-2 --export-custom-routes +``` + +Then connect via SSH to the spoke 1 instance and run the same commands you ran on the spoke 2 instance above, you should be able to run `kubectl` commands against the cluster. To test the default situation with no supporting VPN, just comment out the two VPN modules in `main.tf` and run `terraform apply` to bring down the VPN gateways and tunnels. GKE should only become accessible from spoke 2. + +## Operational considerations + +A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project. + +The VPN used to connect the GKE masters VPC does not account for HA, upgrading to use HA VPN is reasonably simple by using the relevant [module](../../modules/net-vpn-ha). + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id for all resources. | string | ✓ | | +| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | +| *ip_secondary_ranges* | Secondary IP CIDR ranges. | map(string) | | ... | +| *private_service_ranges* | Private service IP CIDR ranges. | map(string) | | ... | +| *region* | VPC regions. | string | | europe-west1 | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| vms | GCE VMs. | | + diff --git a/tests/infrastructure/__init__.py b/infrastructure/hub-and-spoke-peering/backend.tf.sample similarity index 86% rename from tests/infrastructure/__init__.py rename to infrastructure/hub-and-spoke-peering/backend.tf.sample index 086a24e64e..a540c7cd10 100644 --- a/tests/infrastructure/__init__.py +++ b/infrastructure/hub-and-spoke-peering/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# Copyright 2020 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,3 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + + +terraform { + backend "gcs" { + bucket = "" + } +} diff --git a/infrastructure/hub-and-spoke-peering/diagram.png b/infrastructure/hub-and-spoke-peering/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..0c598fe56eb548065c4766b17209061b9480bbce GIT binary patch literal 153704 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

#FkAC${zN~kek z25KK@jg>CkCL;J77(c>urMpI3fCRw)J9OPlIOOso#m3#l6HN*M*^tXi9QJg$Ss#7z zcYL&wEh%lc2J%+%@Ea9r#0~-F$?GdaN=w=1hz|7;dyH?Mhf3PJMn!ppnT(frDQo^+eN8`#|z^C?hrX+C6XKe>CX@ zf^%t{Ks*V%?G;L#;kg(4Q2o?~)b&XvBvYMcm{uSOQJ4lHUNCw$Nxo@MU~6J7!cw+V zqb=arE{n>GMRb|7SI#bVH$k7N^&y2EMj==9KmaCwa`}k6Ag2;Hc176u?mPu-`Q;Bl z+d)?vOA)Ji-nAlAqfk%Q#a1sW@8G&M8Z<{<^~?kn_%+86uC#p63uRQN*PCI8<8d

&){Eru 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 a-PD{<_U%h?y5CC!{^^}C#*}C|Uj7H9(_J9| literal 0 HcmV?d00001 diff --git a/infrastructure/hub-and-spoke-peering/main.tf b/infrastructure/hub-and-spoke-peering/main.tf new file mode 100644 index 0000000000..9f4b875734 --- /dev/null +++ b/infrastructure/hub-and-spoke-peering/main.tf @@ -0,0 +1,280 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +locals { + vm-instances = concat( + module.vm-spoke-1.instances, + module.vm-spoke-2.instances + ) + vm-startup-script = join("\n", [ + "#! /bin/bash", + "apt-get update && apt-get install -y bash-completion dnsutils kubectl" + ]) +} + +################################################################################ +# Hub networking # +################################################################################ + +module "vpc-hub" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "hub" + subnets = { + default = { + ip_cidr_range = var.ip_ranges.hub + region = var.region + secondary_ip_range = {} + } + } +} + +module "vpc-hub-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-hub.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +################################################################################ +# Spoke 1 networking # +################################################################################ + +module "vpc-spoke-1" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "spoke-1" + subnets = { + default = { + ip_cidr_range = var.ip_ranges.spoke-1 + region = var.region + secondary_ip_range = {} + } + } +} + +module "vpc-spoke-1-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-spoke-1.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "nat-spoke-1" { + source = "../../modules/net-cloudnat" + project_id = var.project_id + region = module.vpc-spoke-1.subnet_regions.default + name = "spoke-1" + router_name = "spoke-1" + router_network = module.vpc-spoke-1.self_link +} + +module "hub-to-spoke-1-peering" { + source = "../../modules/net-vpc-peering" + local_network = module.vpc-hub.self_link + peer_network = module.vpc-spoke-1.self_link + export_local_custom_routes = true + export_peer_custom_routes = false +} + +################################################################################ +# Spoke 2 networking # +################################################################################ + +module "vpc-spoke-2" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "spoke-2" + subnets = { + default = { + ip_cidr_range = var.ip_ranges.spoke-2 + region = var.region + secondary_ip_range = { + pods = var.ip_secondary_ranges.spoke-2-pods + services = var.ip_secondary_ranges.spoke-2-services + } + } + } +} + +module "vpc-spoke-2-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-spoke-2.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "nat-spoke-2" { + source = "../../modules/net-cloudnat" + project_id = var.project_id + region = module.vpc-spoke-2.subnet_regions.default + name = "spoke-2" + router_name = "spoke-2" + router_network = module.vpc-spoke-2.self_link +} + +module "hub-to-spoke-2-peering" { + source = "../../modules/net-vpc-peering" + local_network = module.vpc-hub.self_link + peer_network = module.vpc-spoke-2.self_link + export_local_custom_routes = true + export_peer_custom_routes = false + module_depends_on = [module.hub-to-spoke-1-peering.complete] +} + +################################################################################ +# Test VMs # +################################################################################ + +module "vm-spoke-1" { + source = "../../modules/compute-vm" + project_id = var.project_id + region = module.vpc-spoke-1.subnet_regions.default + zone = "${module.vpc-spoke-1.subnet_regions.default}-b" + name = "spoke-1-test" + network_interfaces = [{ + network = module.vpc-spoke-1.self_link, + subnetwork = module.vpc-spoke-1.subnet_self_links.default, + nat = false, + addresses = null + }] + metadata = { startup-script = local.vm-startup-script } + service_account = module.service-account-gce.email + service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + tags = ["ssh"] +} + +module "vm-spoke-2" { + source = "../../modules/compute-vm" + project_id = var.project_id + region = module.vpc-spoke-2.subnet_regions.default + zone = "${module.vpc-spoke-2.subnet_regions.default}-b" + name = "spoke-2-test" + network_interfaces = [{ + network = module.vpc-spoke-2.self_link, + subnetwork = module.vpc-spoke-2.subnet_self_links.default, + nat = false, + addresses = null + }] + metadata = { startup-script = local.vm-startup-script } + service_account = module.service-account-gce.email + service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + tags = ["ssh"] +} + +module "service-account-gce" { + source = "../../modules/iam-service-accounts" + project_id = var.project_id + names = ["gce-test"] + iam_project_roles = { + (var.project_id) = [ + "roles/container.developer", + "roles/logging.logWriter", + "roles/monitoring.metricWriter", + ] + } +} + +################################################################################ +# GKE # +################################################################################ + +module "cluster-1" { + source = "../../modules/gke-cluster" + name = "cluster-1" + project_id = var.project_id + location = "${module.vpc-spoke-2.subnet_regions.default}-b" + network = module.vpc-spoke-2.self_link + subnetwork = module.vpc-spoke-2.subnet_self_links.default + secondary_range_pods = "pods" + secondary_range_services = "services" + default_max_pods_per_node = 32 + labels = { + environment = "test" + } + master_authorized_ranges = { + for name, range in var.ip_ranges : name => range + } + private_cluster_config = { + enable_private_nodes = true + enable_private_endpoint = true + master_ipv4_cidr_block = var.private_service_ranges.spoke-2-cluster-1 + } +} + +module "cluster-1-nodepool-1" { + source = "../../modules/gke-nodepool" + name = "nodepool-1" + project_id = var.project_id + location = module.cluster-1.location + cluster_name = module.cluster-1.name + node_config_service_account = module.service-account-gke-node.email +} + +# roles assigned via this module use non-authoritative IAM bindings at the +# project level, with no risk of conflicts with pre-existing roles + +module "service-account-gke-node" { + source = "../../modules/iam-service-accounts" + project_id = var.project_id + names = ["gke-node"] + iam_project_roles = { + (var.project_id) = [ + "roles/logging.logWriter", "roles/monitoring.metricWriter", + ] + } +} + +################################################################################ +# GKE peering VPN # +################################################################################ + +module "vpn-hub" { + source = "../../modules/net-vpn-static" + project_id = var.project_id + region = var.region + network = module.vpc-hub.name + name = "hub" + remote_ranges = values(var.private_service_ranges) + tunnels = { + spoke-2 = { + ike_version = 2 + peer_ip = module.vpn-spoke-2.address + shared_secret = "" + traffic_selectors = { local = ["0.0.0.0/0"], remote = null } + } + } +} + +module "vpn-spoke-2" { + source = "../../modules/net-vpn-static" + project_id = var.project_id + region = var.region + network = module.vpc-spoke-2.name + name = "spoke-2" + # use an aggregate of the remote ranges, so as to be less specific than the + # routes exchanged via peering + remote_ranges = ["10.0.0.0/8"] + tunnels = { + spoke-2 = { + ike_version = 2 + peer_ip = module.vpn-hub.address + shared_secret = module.vpn-hub.random_secret + traffic_selectors = { local = ["0.0.0.0/0"], remote = null } + } + } +} diff --git a/infrastructure/hub-and-spoke-peering/outputs.tf b/infrastructure/hub-and-spoke-peering/outputs.tf new file mode 100644 index 0000000000..933d03cf3e --- /dev/null +++ b/infrastructure/hub-and-spoke-peering/outputs.tf @@ -0,0 +1,21 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +output "vms" { + description = "GCE VMs." + value = { + for instance in local.vm-instances : + instance.name => instance.network_interface.0.network_ip + } +} diff --git a/infrastructure/hub-and-spoke-peering/variables.tf b/infrastructure/hub-and-spoke-peering/variables.tf new file mode 100644 index 0000000000..3f1375b2e6 --- /dev/null +++ b/infrastructure/hub-and-spoke-peering/variables.tf @@ -0,0 +1,51 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +variable "ip_ranges" { + description = "IP CIDR ranges." + type = map(string) + default = { + hub = "10.0.0.0/24" + spoke-1 = "10.0.16.0/24" + spoke-2 = "10.0.32.0/24" + } +} + +variable "ip_secondary_ranges" { + description = "Secondary IP CIDR ranges." + type = map(string) + default = { + spoke-2-pods = "10.128.0.0/18" + spoke-2-services = "172.16.0.0/24" + } +} + +variable "private_service_ranges" { + description = "Private service IP CIDR ranges." + type = map(string) + default = { + spoke-2-cluster-1 = "192.168.0.0/28" + } +} + +variable "project_id" { + description = "Project id for all resources." + type = string +} + +variable "region" { + description = "VPC region." + type = string + default = "europe-west1" +} diff --git a/infrastructure/hub-and-spoke-peering/versions.tf b/infrastructure/hub-and-spoke-peering/versions.tf new file mode 100644 index 0000000000..6eed2e875e --- /dev/null +++ b/infrastructure/hub-and-spoke-peering/versions.tf @@ -0,0 +1,25 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +terraform { + required_version = ">= 0.12.16" +} + +provider "google" { + version = "~> 3.3" +} + +provider "google-beta" { + version = "~> 3.3" +} diff --git a/infrastructure/hub-and-spoke-vpn/README.md b/infrastructure/hub-and-spoke-vpn/README.md new file mode 100644 index 0000000000..2e39e68cbe --- /dev/null +++ b/infrastructure/hub-and-spoke-vpn/README.md @@ -0,0 +1,54 @@ +# Hub and Spoke via VPN + +This example creates a simple **Hub and Spoke VPN** setup, where the VPC network connects satellite locations (spokes) through a single intermediary location (hub) via [IPsec VPN](https://cloud.google.com/vpn/docs/concepts/overview), optionally providing full-mesh networking via [custom route advertisements](https://cloud.google.com/router/docs/how-to/advertising-overview). + +The example has been purposefully kept simple to show how to use and wire the VPC and VPN modules together, and so that it can be used as a basis for more complex scenarios. This is the high level diagram: + +![High-level diagram](diagram.png "High-level diagram") + +## Managed resources and services + +This sample creates several distinct groups of resources: + +- one VPC for each hub and each spoke +- one set of firewall rules for each VPC +- one VPN gateway, one tunnel and one Cloud Router for each spoke +- two VPN gateways, two tunnels and two Cloud Routers for the hub (one for each spoke) +- one DNS private zone in the hub +- one DNS peering zone in each spoke +- one Cloud NAT configuration for each spoke +- one test instance for each spoke + +## Operational considerations + +A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project. The provided project needs a valid billing account and the Compute and DNS APIs enabled. You can easily create such a project with the [project module](../../modules/project) or with the following commands: + +``` shell +MY_PROJECT_ID="" +gcloud projects create $MY_PROJECT_ID +gcloud alpha billing projects link --billing-account=XXXXXX-XXXXXX-XXXXXX $MY_PROJECT_ID +gcloud services enable --project=$MY_PROJECT_ID {compute,dns}.googleapis.com +``` + +The example does not account for HA, but the VPN gateways can be easily upgraded to use HA VPN via the [net-vpn-ha module](../../modules/net-vpn-ha). + +If a single router and VPN gateway are used in the hub to manage all tunnels, particular care must be taken in announcing ranges from hub to spokes, as Cloud Router does not explicitly support transitivity and overlapping routes received from both sides create unintended side effects. The simple workaround is to announce a single aggregated route from hub to spokes so that it does not overlap with any of the ranges advertised by each spoke to the hub. + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id for all resources. | string | ✓ | | +| *bgp_asn* | BGP ASNs. | map(number) | | ... | +| *bgp_custom_advertisements* | BGP custom advertisement IP CIDR ranges. | map(string) | | ... | +| *bgp_interface_ranges* | None | | | ... | +| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | +| *regions* | VPC regions. | map(string) | | ... | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| vms | GCE VMs. | | + diff --git a/infrastructure/hub-and-spoke-vpns/backend.tf.sample b/infrastructure/hub-and-spoke-vpn/backend.tf.sample similarity index 100% rename from infrastructure/hub-and-spoke-vpns/backend.tf.sample rename to infrastructure/hub-and-spoke-vpn/backend.tf.sample diff --git a/infrastructure/hub-and-spoke-vpn/diagram.png b/infrastructure/hub-and-spoke-vpn/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..6d5dc5ff25e5bf95022492714d49697a73eb40ca GIT binary patch literal 199253 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

T>?2WiU%+a+`=agGfjas0hlJ~ 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 literal 0 HcmV?d00001 diff --git a/infrastructure/hub-and-spoke-vpn/main.tf b/infrastructure/hub-and-spoke-vpn/main.tf new file mode 100644 index 0000000000..bee66f7fb5 --- /dev/null +++ b/infrastructure/hub-and-spoke-vpn/main.tf @@ -0,0 +1,308 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +locals { + vm-instances = concat( + module.vm-spoke-1.instances, module.vm-spoke-2.instances + ) + vm-startup-script = join("\n", [ + "#! /bin/bash", + "apt-get update && apt-get install -y dnsutils" + ]) +} + +################################################################################ +# Hub networking # +################################################################################ + +module "vpc-hub" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "hub" + subnets = { + a = { + ip_cidr_range = var.ip_ranges.hub-a + region = var.regions.a + secondary_ip_range = {} + } + b = { + ip_cidr_range = var.ip_ranges.hub-b + region = var.regions.b + secondary_ip_range = {} + } + } +} + +module "vpc-hub-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-hub.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "vpn-hub-a" { + source = "../../modules/net-vpn-dynamic" + project_id = var.project_id + region = module.vpc-hub.subnet_regions["a"] + network = module.vpc-hub.name + name = "hub-a" + router_asn = var.bgp_asn.hub + tunnels = { + spoke-1 = { + bgp_peer = { + address = cidrhost(var.bgp_interface_ranges.spoke-1, 2) + asn = var.bgp_asn.spoke-1 + } + bgp_peer_options = { + advertise_groups = ["ALL_SUBNETS"] + advertise_ip_ranges = { + (var.bgp_custom_advertisements.hub-to-spoke-1) = "spoke-2" + } + advertise_mode = "CUSTOM" + route_priority = 1000 + } + bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-1, 1)}/30" + ike_version = 2 + peer_ip = module.vpn-spoke-1.address + shared_secret = "" + } + } +} + +module "vpn-hub-b" { + source = "../../modules/net-vpn-dynamic" + project_id = var.project_id + region = module.vpc-hub.subnet_regions["b"] + network = module.vpc-hub.name + name = "hub-b" + router_asn = var.bgp_asn.hub + tunnels = { + spoke-2 = { + bgp_peer = { + address = cidrhost(var.bgp_interface_ranges.spoke-2, 2) + asn = var.bgp_asn.spoke-2 + } + bgp_peer_options = { + advertise_groups = ["ALL_SUBNETS"] + advertise_ip_ranges = { + (var.bgp_custom_advertisements.hub-to-spoke-2) = "spoke-1" + } + advertise_mode = "CUSTOM" + route_priority = 1000 + } + bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-2, 1)}/30" + ike_version = 2 + peer_ip = module.vpn-spoke-2.address + shared_secret = "" + } + } +} + +################################################################################ +# Spoke 1 networking # +################################################################################ + +module "vpc-spoke-1" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "spoke-1" + subnets = { + a = { + ip_cidr_range = var.ip_ranges.spoke-1-a + region = var.regions.a + secondary_ip_range = {} + } + b = { + ip_cidr_range = var.ip_ranges.spoke-1-b + region = var.regions.a + secondary_ip_range = {} + } + } +} + +module "vpc-spoke-1-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-spoke-1.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "vpn-spoke-1" { + source = "../../modules/net-vpn-dynamic" + project_id = var.project_id + region = module.vpc-spoke-1.subnet_regions["a"] + network = module.vpc-spoke-1.name + name = "spoke-1" + router_asn = var.bgp_asn.spoke-1 + tunnels = { + hub = { + bgp_peer = { + address = cidrhost(var.bgp_interface_ranges.spoke-1, 1) + asn = var.bgp_asn.hub + } + bgp_peer_options = null + bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-1, 2)}/30" + ike_version = 2 + peer_ip = module.vpn-hub-a.address + shared_secret = module.vpn-hub-a.random_secret + } + } +} + +module "nat-spoke-1" { + source = "../../modules/net-cloudnat" + project_id = var.project_id + region = module.vpc-spoke-1.subnet_regions["a"] + name = "spoke-1" + router_create = false + router_name = module.vpn-spoke-1.router_name +} + +################################################################################ +# Spoke 2 networking # +################################################################################ + +module "vpc-spoke-2" { + source = "../../modules/net-vpc" + project_id = var.project_id + name = "spoke-2" + subnets = { + a = { + ip_cidr_range = var.ip_ranges.spoke-2-a + region = var.regions.b + secondary_ip_range = {} + } + b = { + ip_cidr_range = var.ip_ranges.spoke-2-b + region = var.regions.b + secondary_ip_range = {} + } + } +} + +module "vpc-spoke-2-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = var.project_id + network = module.vpc-spoke-2.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "vpn-spoke-2" { + source = "../../modules/net-vpn-dynamic" + project_id = var.project_id + region = module.vpc-spoke-2.subnet_regions["a"] + network = module.vpc-spoke-2.name + name = "spoke-2" + router_asn = var.bgp_asn.spoke-2 + tunnels = { + hub = { + bgp_peer = { + address = cidrhost(var.bgp_interface_ranges.spoke-2, 1) + asn = var.bgp_asn.hub + } + bgp_peer_options = null + bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-2, 2)}/30" + ike_version = 2 + peer_ip = module.vpn-hub-b.address + shared_secret = module.vpn-hub-b.random_secret + } + } +} + +module "nat-spoke-2" { + source = "../../modules/net-cloudnat" + project_id = var.project_id + region = module.vpc-spoke-2.subnet_regions["a"] + name = "spoke-2" + router_create = false + router_name = module.vpn-spoke-2.router_name +} + +################################################################################ +# Test VMs # +################################################################################ + +module "vm-spoke-1" { + source = "../../modules/compute-vm" + project_id = var.project_id + region = module.vpc-spoke-1.subnet_regions.b + zone = "${module.vpc-spoke-1.subnet_regions.b}-b" + name = "spoke-1-test" + network_interfaces = [{ + network = module.vpc-spoke-1.self_link, + subnetwork = module.vpc-spoke-1.subnet_self_links.b, + nat = false, + addresses = null + }] + tags = ["ssh"] + metadata = { startup-script = local.vm-startup-script } +} + +module "vm-spoke-2" { + source = "../../modules/compute-vm" + project_id = var.project_id + region = module.vpc-spoke-2.subnet_regions.b + zone = "${module.vpc-spoke-2.subnet_regions.b}-b" + name = "spoke-2-test" + network_interfaces = [{ + network = module.vpc-spoke-2.self_link, + subnetwork = module.vpc-spoke-2.subnet_self_links.b, + nat = false, + addresses = null + }] + tags = ["ssh"] + metadata = { startup-script = local.vm-startup-script } +} + +################################################################################ +# DNS zones # +################################################################################ + +module "dns-host" { + source = "../../modules/dns" + project_id = var.project_id + type = "private" + name = "example" + domain = "example.com." + client_networks = [module.vpc-hub.self_link] + recordsets = [ + for instance in local.vm-instances : { + name = instance.name, type = "A", ttl = 300, + records = [instance.network_interface.0.network_ip] + } + ] +} + +module "dns-spoke-1" { + source = "../../modules/dns" + project_id = var.project_id + type = "peering" + name = "spoke-1" + domain = "example.com." + client_networks = [module.vpc-spoke-1.self_link] + peer_network = module.vpc-hub.self_link +} + +module "dns-spoke-2" { + source = "../../modules/dns" + project_id = var.project_id + type = "peering" + name = "spoke-2" + domain = "example.com." + client_networks = [module.vpc-spoke-2.self_link] + peer_network = module.vpc-hub.self_link +} diff --git a/infrastructure/hub-and-spoke-vpn/outputs.tf b/infrastructure/hub-and-spoke-vpn/outputs.tf new file mode 100644 index 0000000000..0edd756d2e --- /dev/null +++ b/infrastructure/hub-and-spoke-vpn/outputs.tf @@ -0,0 +1,21 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +output "vms" { + description = "GCE VMs." + value = { + for instance in local.vm-instances : + instance.name => instance.network_interface.0.network_ip + } +} diff --git a/infrastructure/hub-and-spoke-vpns/provider.tf b/infrastructure/hub-and-spoke-vpn/provider.tf similarity index 100% rename from infrastructure/hub-and-spoke-vpns/provider.tf rename to infrastructure/hub-and-spoke-vpn/provider.tf diff --git a/infrastructure/hub-and-spoke-vpn/variables.tf b/infrastructure/hub-and-spoke-vpn/variables.tf new file mode 100644 index 0000000000..2d3feaf5fe --- /dev/null +++ b/infrastructure/hub-and-spoke-vpn/variables.tf @@ -0,0 +1,68 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +variable "bgp_custom_advertisements" { + description = "BGP custom advertisement IP CIDR ranges." + type = map(string) + default = { + hub-to-spoke-1 = "10.0.32.0/20" + hub-to-spoke-2 = "10.0.16.0/20" + } +} + +variable "bgp_asn" { + description = "BGP ASNs." + type = map(number) + default = { + hub = 64513 + spoke-1 = 64514 + spoke-2 = 64515 + } +} + +variable "bgp_interface_ranges" { + description = "BGP interface IP CIDR ranges." + type = map(string) + default = { + spoke-1 = "169.254.1.0/30" + spoke-2 = "169.254.1.4/30" + } +} + +variable "ip_ranges" { + description = "IP CIDR ranges." + type = map(string) + default = { + hub-a = "10.0.0.0/24" + hub-b = "10.0.8.0/24" + spoke-1-a = "10.0.16.0/24" + spoke-1-b = "10.0.24.0/24" + spoke-2-a = "10.0.32.0/24" + spoke-2-b = "10.0.40.0/24" + } +} + +variable "project_id" { + description = "Project id for all resources." + type = string +} + +variable "regions" { + description = "VPC regions." + type = map(string) + default = { + a = "europe-west1" + b = "europe-west2" + } +} diff --git a/infrastructure/hub-and-spoke-vpns/README.md b/infrastructure/hub-and-spoke-vpns/README.md deleted file mode 100644 index adbc4fcd37..0000000000 --- a/infrastructure/hub-and-spoke-vpns/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Hub and Spoke VPNs - -This sample creates a simple **Hub and Spoke VPN** architecture, where the VPC network connects satellite locations (spokes) through a single intermediary location (hub) via [IPsec VPN](https://cloud.google.com/vpn/docs/concepts/overview), optionally providing full-mesh networking via [custom route advertisements](https://cloud.google.com/router/docs/how-to/advertising-overview). - -> **NOTE**: This example is not designed to provide HA, please refer to the [documentation](https://cloud.google.com/vpn/docs/concepts/advanced#ha-options) for information on Cloud VPNs and HA. - - -The benefits of this topology include: - -- Network/Security Admin manages Central Services Project (Hub). -- Central services and tools deployed in Central Services Project (Hub) for use by all Service Projects (Spokes). -- Network/Security Admin hands over spoke Projects to respective team who then have full autonomy. -- Network/Security Admin monitors spoke projects for organization security posture compliance using tools like [Forseti](https://forsetisecurity.org/), [CSCC](https://cloud.google.com/security-command-center/) etc deployed in Central Services Project (Hub). -- Spokes communicate with on-prem via VPN to transit hub and then over Interconnect or VPN to on-premises (on-premises resources are not included in this sample for obvious reasons). -- (Optional) Spokes communicate in a full-mesh to each other via VPN transit routing in Central Services Project (Hub). -- This is a decentralized architecture where each spoke project has autonomy to manage all their GCP compute and network resources. - -The purpose of this sample is showing how to wire different [Cloud Foundation Fabric](https://github.com/search?q=topic%3Acft-fabric+org%3Aterraform-google-modules&type=Repositories) modules to create **Hub and Spoke VPNs** network architectures, and as such it is meant to be used for prototyping, or to experiment with networking configurations. Additional best practices and security considerations need to be taken into account for real world usage (eg removal of default service accounts, disabling of external IPs, firewall design, etc). - - -![High-level diagram](diagram.png "High-level diagram") - -## Managed resources and services - -This sample creates several distinct groups of resources: - -- three VPC Networks (hub network and two ppoke networks) -- VPC-level resources (VPC, subnets, firewall rules, etc.) -- one Cloud DNS Private zone in the hub project -- one Cloud DNS Forwarding zone in the hub project -- four Cloud DNS Peering zones (two per each spoke project) -- one Cloud DNS Policy for inbound forwarding -- four Cloud Routers (two in hub project and one per each spoke project) -- four Cloud VPNs (two in hub project and one per each spoke project) - -## Test resources - -A set of test resources are included for convenience, as they facilitate experimenting with different networking configurations (firewall rules, external connectivity via VPN, etc.). They are encapsulated in the `test-resources.tf` file, and can be safely removed as a single unit. - -- two virtual machine instances in hub project (one per each region) -- two virtual machine instances in spoke1 project (one per each region) -- two virtual machine instances in spoke2 project (one per each region) - -SSH access to instances is configured via [OS Login](https://cloud.google.com/compute/docs/oslogin/). External access is allowed via the default SSH rule created by the firewall module, and corresponding `ssh` tags on the instances. - -## Known issues - - It is not possible to get inbound DNS forwarding IPs in the terraform output. - - Please refer to the [bug](https://github.com/terraform-providers/terraform-provider-google/issues/3753) for more details. - - Please refer to the [documentation](https://cloud.google.com/dns/zones/#creating_a_dns_policy_that_enables_inbound_dns_forwarding) on how to get the IPs with `gcloud`. - - -## Variables - -| name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| hub_project_id | Hub Project id. Same project can be used for hub and spokes. | string | ✓ | | -| spoke_1_project_id | Spoke 1 Project id. Same project can be used for hub and spokes. | string | ✓ | | -| spoke_2_project_id | Spoke 2 Project id. Same project can be used for hub and spokes. | string | ✓ | | -| *forwarding_dns_zone_domain* | Forwarding DNS Zone Domain. | string | | on-prem.local. | -| *forwarding_dns_zone_name* | Forwarding DNS Zone Name. | string | | on-prem-local | -| *forwarding_zone_server_addresses* | Forwarding DNS Zone Server Addresses | list(string) | | ["8.8.8.8", "8.8.4.4"] | -| *hub_bgp_asn* | Hub BGP ASN. | number | | 64515 | -| *hub_subnets* | Hub VPC subnets configuration. | list(object({...})) | | ... | -| *private_dns_zone_domain* | Private DNS Zone Domain. | string | | gcp.local. | -| *private_dns_zone_name* | Private DNS Zone Name. | string | | gcp-local | -| *spoke_1_bgp_asn* | Spoke 1 BGP ASN. | number | | 64516 | -| *spoke_1_subnets* | Spoke 1 VPC subnets configuration. | | | ... | -| *spoke_2_bgp_asn* | Spoke 2 BGP ASN. | number | | 64517 | -| *spoke_2_subnets* | Spoke 2 VPC subnets configuration. | | | ... | -| *spoke_to_spoke_route_advertisement* | Use custom route advertisement in hub routers to advertise all spoke subnets. | bool | | true | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| hub | Hub network resources. | | -| spoke-1 | Spoke1 network resources. | | -| spoke-2 | Spoke2 network resources. | | - diff --git a/infrastructure/hub-and-spoke-vpns/diagram.png b/infrastructure/hub-and-spoke-vpns/diagram.png deleted file mode 100644 index 6e3b9daa13bf6b57af9c490e15f5cf4df9d823b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146632 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
6&+^qTWae_H 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%0Dq!tV0BAQCFXlwE2ncrV7J~jPbQYPuS1Y^*xbt$ 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

D=N28m2_`T zH4gY0whLEwfj0&%j+}Es8r6?0S3&vcI{r2?UQVfbW2d{j`!qoE<-=*nu==AINsP{U$-GG#5V)+7JpC6WTl)edUL#=J@pF;f1=dpSDIt2~ zoc3jraFc4tX@}3B(PK&D$BWmx%O|Of6!d*<@F*{+A0Cw=L zci@HhQ0;BsBT-X-n!`s_s+RX>d$U{+h_%0{Kf|q3s2HTZF;eP+}d-_ibtz-EnAUy<=1=R zH}vH8i^<&=9-vs(LjF{{$!z-jxQ9V*^3OuAGb zY#(5AJ(+;HM^p4R@T~B8%E*XZ;pFnN{wbNmyObc}zr$Z00eDVpDmkAM55u0{ikc`g z*--f%(*}F!%Mc;HRYN2({mfGM`E3p=M(p?D;Yd!Z=U2lox5$rPM{vr(VVrEjx?>ZE zXqcF|C{KR)Jq0=gYx^v8u33r82tolwE8PbR@&0k|@5ds z6vY+FpC>$~(skP=&wIGt8O;2U{`JG^+_Wi|A81DZqxOEk7Nc*2^pY+1E^GaETiQWy z%QukjLsHyUsR|Krfwy@Nr^2%NxsN(5)ZMtrL5#f;n6umiFynDB>G%tDokoc=T;RTmak>Fyj#(xGDj2|-#ykW>LdfdT0TrMm_hx*KVvq(NGSP5}{7=|*a(p`VTS z?>^5t=Y7w+*7@^YEMO6P?{8f7y{^yo+4&GO>VQ+L0I-(qb-f{4;(A~#KD^5c6SUYF z5B;*7b73!sL(^Z#wj#TvzI0a89icVUyMLf^xCMNs+7K?Bn=@agmEG@`(!CaEvp~np zz5ZkMl!@?aE%#8L%!FCzA22{OTgj^~{; zzgQ|m>TU8Nx`G$5fTx`(9JDX%qnXjMs{9WENjlCvIxk-xX`mkXF zR&;7-=1MF>fxS`N`kl6cvCmgUnPG?fH@`bYkWkk`18N;M4LBOhB9*J#(WENSB);>z z@ebmD_HTo?WO2zIycgg|78YO_`;nzShG5mp^i^ z4OapVI}Z(i4D?v7U89DSG#95_nPnsxtWi1kcsGF298WbqxQEsgoO^pSSACsvhzan8 z{zB71W0Iry)!AFAr}2HA7rUl1*{N^h_YJjc|LmqN;B*`=n7g(7Q}s94Hhm!GnDF+D zVFA_W3&ftRI5J}7OBGURj|Rw@&{A&JIxd9~$v`UZ4vtgBcT!~Ur`lt2|MB+H% zE1Dm=_+}v%e3OwApC&nkp2e+d1zb!WML|o2qV@qdE}NQro}LSd+7$HLQZG)Uh`OD| zmg)~o_#7N`+jM)nKu=GEU~vuk*b8zO=)#>&jS9{}BZqUvAu>Emo#Zvy%^YtJWpDbk zRJsMIp$3=_m{^29n2mT@d3Ug)h3uorSqxPdKHa(2;J7^9ZC%?(UnX@nRWKw$4904|E}aL&WuAT!}sooVD}CX@ic92IfB2n6509j)uW4&~?ujxqymZi2Lc(`MYfr zxgW$+SoO&(l^D7hVyRwU{D~(XGK24Zu0S+{UUpqe;7`$;P?=^ekiK?@yasxB=mfoh z7l2TNlt!5YrPh4)ST{$v#ZdwC^(IY$TGAl% z?IAM*UVM&^jbH1Df4&Bd6f~KvWlavQp#l^ov1_jdZ`V5c%0RaB0lwn*fIO1%;Fep| zU9G15ko>=bM~vH^w6r(<0DubNess2kO;5VY$;>TyxC72?HEu4>OUdGwrjrSwjJuH; zqb<+kYTi_u_=mIWpW*a&x*FbJPdr>O!0Wt14zG9el|~MH(&Vhk4^|`BeAFJJYAUu@ z$}V3oL{52iGx0&Oi3--Q-%AR12=k^w4gVT0W?^_6NSTl0DVW+ocKfsQ+NkAnul4C~ z;+=fi{um~aE$(zS54{sSxg~db791V2LNmd48K&UCz=iQ}+Ca|V`iPA;NAWV2KZ#b1 zB6Y{!A$SP>{KqlPh+Q5MR4zl-KC(8#tSC#DN5IRVbgvaLgS}LFf*DpiIw0fapS_B#5YK z5&)C1iLxwT#rzUu!4V1`#3aznCPd;^F6V-*Sa2l6)8Bhia4Q_ER5=R+9;cpMVYl=o zUemvF5Vmic&;A>HJ#^yV`T#-`0@7%^vzV9XgHzEEAe&5FW}6A5O{0VUJCr2zKPPa{ z*bp#IrI=&Hnwyr&nrH6y6pmhmrXviZ-;$F=L1>W_=8No#$t$~Vq)@4{d?-(nn`(_o zU_{|(U&M|kdGL6O6(^ptP$E$`cp+juqf*ZIBlE~3zS!J>O|u=O=M1mfC6I)WYGJ+OK`eRcy4D6U zl>0T=K>;LV`qgW34WQKvFdgvnbFoy0Nc4IZL=5}$%kXUO_4JDmXZA^eVMx zrm-yCd01cRvm;SEQ^+235pk!d=ycR?{1e4d@t2AiE$@zkg()Gx9oAT=u}uF!kf6x3 zwv9*NFD#iKfG7Q7M`zf%d>3Wp^hWg{|1@_sYgglSYnF)AU^%eLZ498)g* z#aVEy3JYinWNkQ<*E>04f^wptsY$$8TF=dBY}dF;?iDXON{dlv$IaK&31c8icau&p zc2DWu&w&m@0m9jWQ6F-sF*7FtyRFz(mW*zLIZ%!AlVFLMyPkYTONPN)726+{0Wt|N z)BRj!(0{4JuwtpC>{e+q|5k!yzm=Wq0rg(E`Jz*;gq6&KWGg_~)!JAFN za00zYjUN|2xNv*DkU1glulbs#FH9k8x(QA6lOE|T%PM^N37Ksd~~#N>MNN1rG{?jkyIx;sSGL)olZkQK4ZW4-&g=& zTFSNC%YfVQ;}99k40qRd*(n5u5*s2m>0bHFFkjvAhiR`c5Fexg@# zlqsk+ICuj)ChiTLy3rM+j3+ORlM~{)6vtj$al-qpOEqm69aVN1eyj8l_vU^)0jdI( zS$MNt7e6=BN6EQ?&T=5aouD~{d#2+P-7Dq~j&ktt3#TRvEj8NiGt{A;j%Kk98hDe> zsVn|P)uT&gJ;!ISr5`nMOvoI2dQih}0zjUn?+tY0Jo664as9FY#Y{MDmTibJ7Lden zoZRvL6a;e`hg4Guz0phD&nH#Nk3%Ya^lYq;2BNBc^XRTiE)YRijjzpx@`c31I!#Vi zVmvRS>)MbS)izEuG(0;l1|(I7JKlPM;ePyr`F; zrp5t9rd<1SA!rt*HkUX4+PVSnTpyZ3j$Q#1kykDAx%f~aHJ2;?M5@QSLF~RFlmv-Y$uj`MyXS|? z&A3}vQ2{I_Aw+2UOD)8Du2CP*CEm>X+(8d;){wuc)W3blKKM}WjomLNp}vPdO0PBx z-Fl^`RDdK_9Met$GBRT5{-)Ltvy2RQL%?c$uu)f?-cA8bzY=m5URXQQZcQ}#;DA9* z4ZI67A<0et9e{@06*Nu42C85?Qo?IH%H9`^dUIuNYv#<%pj0SXXRv~&3m`4+7ET+R z(yfWu=nk4BUac|%QK9`+@3Hl3vEQow#gDF_n(}7svB4QAyOe5;+k+$32s1q)J#V}l z@X4C$&*IK49Y)l^G3!)#26fmMsX&TA(C}HGQ-5lIEC+V@9AgFMY@T@tpV7xB9b_R- zRoz3RCkVYwk0@l*pFU^P*JZ^iNnGsFxbTjfTuB_XU?p;cI^f1}uDUmDo zxXIeDKQQUH4|8guB4H81=q=bR2ljjd>ea|BjUgivthJsqY*$61LU&Ay>F>`5%p-<0 zSPL(B&WIlMg?=1l>{qkELD0d1YWm2aBSOUb zbogocKDsuqM)6jP75;LXbmotMB+~RFGJ;RK$(Px2k|3Jd)u2_UD z2L;0d1FQ>7=g;rJjyX9J#J!cr(eSDxc~p6r^J#z;*l^K&3OAqIllHcP(6AP4O8liP zu#3Z@$2%m7__6MYp$FgnFw3$P$)A3Aya9R2&{;;ab7d&e0@wOEkU>`p*w=0e!xLVi3?t5BPvEGkDB|}ji za>*^Zc<+sA_cyn;AP*jH3YJum&Gjc=ygDXN`@+o~^=)n7vPb4##q$Ar0Wr|3Jivl4F?J{R|>ZGEOs?MV8=gNE60aG{(V z#E`0zhyzEHZa#=tBgP+?WWgCn6CN7oUG4r&jU0>^wlFV zr6wU%+hlht_on{G7OwWEz^1E;XOb^V&X*Q2m=5fw_Im_ZvdjGI*YubBmlcitMRe$u zP|KZ5xQSxf+q@+^ag9M}KLpq`g-?EdUU*m;_O5vxgKEZONfE;tHq;eS@4H~XU*|hv zzdr&dWHX&T;DDw*)p(zapKdy?2 zln#(#<;0}`Lqm4H`pKVn{q~ZBOR5V}udN|tdcGmmV^h>04?Km}h+?l2&>6kt_15RU zD^YXXzCGy#I4mDAX#ju*kE<;B7>SzEs}k-#!+}(qQ$CAbDyIbioy1mCc-0f1=DQw2 zo<3MBuR?{xVApx2nqIgU^NJyAc%h9dr9ZY0p@mQ~C3*L&^|zpr(K=h4*6SxjA{!K6 z%KVLgem`F;AWe!Cq9M}hy_UlCM{66j5F~q)Oct_qq}^hpO5^_UMDQECH_3sXxn#k zJ}D{3&TNbM{h$NW+T@%8Sn%EbbzTK#d(wejCYkf0I(0arnUz0xT#)onS&CL?rSi;! zX5aa33emzl$`0fv{~hImXR53bup;Ox6lQc8E#Rqme4CH9$>B)we?}R!V?bcSH``G< z=g6ZUc6wy54r6`>BYB9}!;J&nd6ezkr!DpHGYFAJ!f9d6CJ--icK-A?oYJojNh_NnR1)*6q0(69)Hz%9 z8SdyKW5)Z`S{$pxS{t|!&(pnKYGheIy~XfcUb(RGlNBxL!_Yx&jnPY^AvbTWEda=% zytU)`pyRHmUDfYozKqQL#KL6A>68GGr+S^58#S=do{=z~RXl4?#!jO(H1JHJ&x&H1 zG8YR|2495|iL%oJLLttg#!12b=JqG^UOuD7pXMgpHw?D%u1JD+LOa0af^JVQji|t=MyB9m~Y!0IRf)k)C)K@ll zt>@mg+C6{mNEwmr&iT^IjD+>VXsx^4l+Z3HPtD| zT%Y3nXtq3I#W|rvsjzA_m7}*lG_7~(zXOH2#M_3j0Zc+hJwQUD>9}3jWQQ6i-%h{= z=|C#n#NVZs=wPbflUuBYgJlf$D22%TVwuxZwq0`%9mF5thD}pe^<@P9vEfZr?@_Og zG~3ELUGP(BICRppbE}w6Q}rWlV3ClI&EIO-maR~}JXc@1@A(5gMUK~NKLI~| z{zPNn2#EtX zt=MG#VPT|p4gu%tU}D|m+qWd{e+*2(%zmXaaxi0+isGHJ1{oG+-@F(8%Q+cErP}*C z_!Sxdz)%j>Fd>@*-vIkDs;+b1`dv6zGq`5Hc7~I5|8#hcUao(1j!t`fpE*0FJ5mzu zi%tateTeJ#Q&*cZ!hy1&(l%fJ7sX%1bG`mF@|bUSUi_M#)Z}b&*~b4$D|;kY*|tOC zF)sJ+x#V0k=N-6*()RUXEWq`#%x3b97SNyPhpB&t0bSK*3ER27>em5G>bX0G?@Y+9 zzt@pWt}3!PK>aCvV{D9o;qA=xd&t46Xx0{UyD+7EHLoHm`}_wL<_71#Iho5u$~e#E zi1s?&>{@)0@!mgLTuummQvP&(Cw2Q5Czph9TB|VH zPpe9RUti3Wh^Tj|lbO4UaW6cgPwey+<0+U*kNS>#t%iS|p8$(^oZwc*8X1aLl8F$; z<+7ss;|e5VhMC(sU(CNNeW_5M%grCD^aYj981&M}$ZqnR^vIzn7Aar zT*0D#;MjP#PDzu8*BGDSaCjQCUVn&Nbu|D}8TR|2tx8W%zs{N9DJ@V?P+%FXcHMH4 z_f5cc@OVBsPW?l>BCGNTn?lix0svrh@BgBaMh`#!H$37Q1!57aLk|-G>`sw;l}R0e znR-~#HB{MXQk0}!+1c1{yK5L>I);0xO-vsXa(xQNrjy(*zvLFAFm$kGLc2}1KYsiO zce3lo^!MMM=5|}yZCdDH(>C+;G>EzP$oA?xaE7%C0tNCMV>8#+)Ie5>msVE`&%t?w zneS1bf8~0ktMc&jK4q8z=dFpaoGsHHElfu}K-(WXecg2O9JUKUmW+8T==>5B6V-5x zJM=}jQn!@kpo3SrqsLYM-~!hAzS5t+<667<4`{$|d4TXSFLR@MjPAl}%u@U~)P{p1 z5FSQOr%q1z!IMgnEkC%e)2Utfe!wjV@e>mJv-JAk!0 zG64stn)n-PW=XIDsFPFx(_tr1HSn!m|7ouE3cr(A)UFd>7!DOQDVTApYa?Ea1i=TQVep~| zQ)eEN=jkS=_B;S4#e^NbQ|o5Af5p|3g-5S0^4vYqonKeV{PIHh9M#i+T-*!0$*7~x zwd1FV#LIL-HC?5u5{hl2nrx=PExV0B;ki;WGLF7#zO5eZJFj?aB_9V!?2_IlQQiVo zMEu3P1-WFu%Ts>TBh&DO_R~4Lb%vR06DQxvj*brOc#dDVZLY_ ziZagGLO`dxyE~tC<~x&s>$ffY9hVjW_`=`GA!$6=5<{UU+cp?WD zQOi0sZsc?#UXLyQ7%(S@|0#=OVi>a6FBUdmG}+5Y{w@%@_AFq1`|B#fy*(~*W&c}C zk0nxQgj}6z0DyifY;XUKMxZM$vC$SL8y_4dPsC2K(2w1?KMP zXe+k~KCj$iasf83e;*a;5ps5^DjS+S=VS3B4GVmuFIm{>NHaI#MaG2^3oVt#Tvsif z?Z&eKz15HA8B#XJ$A(T0M|P*5jW|sB9e;ResJAlGw*$TI)$hSY=wo3muGB6<6|2h2 zhf%?#vR9wfk_9+m-Qr(WSX8X6N&x4~4o2O((qoHSxH)Z#8s88(!!SSasKlcbTOG@m zvSO=X_C1i7^gPJ3IRmQS#Vx7PQoi%k?kT-uB&ot;PYwZcuR9n19awkw?Mc-yj4CIn zud}~?eE(7PM#&;h8OgA3Blp-x!o=?x{SS@R0K4S_y~W>YldGzX!!oABoKq<+C0ubyFQ zCulx6%i+)EGbya5NvBx@95QoTTU+t_e)~Q+9j0xSgKNN18T>Z1t;3_^C>vZTz<|hO z0zV8SuFtTh0*lV&kro!AxyF(2av_@ES^%+3Wao>0z?p?V0@@V*&BymT6lA?O=c&+f zqpNXqn!LH3>0USkUM`mD)3vp*2m?JbJ0Cf71XAYx4PscUCHv*cXTP6bL|(281*3Ig z_$t~_z89}US3c$!3QRJ)nskQ+?LQ%wxItHHR%HeAh(CZz>e;$@Kf%$)lsyN)I7bgQ zU=_{&*Pjm)r>4|AX}RyCit2N>bEi^iUwd|6#f(a@3G(HNQX&?ay2xz5u;N~Mv*2{_ z9fY6GZuZb_+@;Id7dkcX0#UwQe<`w%Vt^?D8J${yJW0(L&XgQpS!Xzc4FK%NalEsQ zU@qq7PUU-2#{3_WHiF zE7q6$OJ-Q2L89DhH= zltaS>GU!?L+4#@{8D8BC7{1sF8Z&CB9y^IeA!Gqy7(k8D3K$EW88LXj;OGN@DoW5YT!$xBoRcy^`s#R=&I2pmwXgA;JLc}MPGoQjXl>r;iGG3 z+nQ;z?{aiQZE@c%kytbt5#=E49ENY%V|{_tQo@fsS62 z(?QN$(XWFqp5T9FJVsy05VaB@gnlaPFJ}5WWxsyd83by}w6=X8TI|SDdl(T*tYOwe zYI2jT|9bGH#zMimqG+~-%P246(|M{Hz(oCB%enEiCe3%NrIbi(UF=v~i+|AnYWupC zXVska8-HmeM4B!&z|-N0zq7sNO*sTv7^^*U)bT2L*`t#x+)Mo2UkM_YdK3oAn(eLw zJtNvW-4&1BTe0H1IHyBA)aSrS$~ut1bM}@^Q3&JyGk96rja~`94yB?5lY)tPNfR17 z0JJ6ugx(&mtB5$Se)|xgv!+nXdcvlQC`=QAtS4+AOBuFylb~G#JoI~A2kZr)a(McT zN?nP#D=qq|y5i%+XA#@~Fk}5~;yDvWETV~NE?<5Tk8oEFbjbFs~V}+)+T@ zwAxze|NK(FQHM!>K6jMpdNVW{XZhZ%!zQ4}clE#F8-% zI~Q(EJg<%gVF(&eZOOpN}GNZG}14gQX3%TFeY<#$S%m-_MUh-Jsogx+cQ=xG6!lO)9^I#NR91#$`jLDukl^+h9RmMye=PzQOSWMe zY^7e(CF;w$3LdvN^OyG!v|JRfOQCc$_U~5vd^IT6Co;1BMp;n?&Ba_%bKb=U(v4Tg zhw8oUV{j7v(WSWZqA#zfo9YXG%6(H1f6QtjtK*Z>(6YDRa>1Op#DI(C}ZYEJ&np| zL-%zANXB=5G5+JZN(~N=L5|C$ZB?~-1|u%N0*(S2NtQ7~KT~pgU*8*6Me);BVX9!v zy%~+t?q?53kXXzbP3G@#QN!DTLSK>CT2uPdSdc74tQ0?pz~tSC?$U*VJY4=QHe~38mZ53`4;nhW z5DZ#EdbGUcg>_R(21YxrAIQd99q^p_^XgvJ1QS`B@tGLJjvN1a|K@1B`_0r^o5bO& zCCK>}Z|}Uz9?ufujE}wY3%|6?`9+|t+)}4fSFZWwiR9sOmw6^+A<3)pHK>B%QE-8aeJ9O zt2|?m_}^FnH-!+o)s{YIX@TMdNK_D23Z~=-MMcGsA5@8$Vz?rX^@BlfTnH8kQ(FdH zZ<3M1AAJ2!gX4QV@{ z4Z_@uim!81MNq&V4>f`P_#V(AKvA|ldaCHa-h_ElFvl9TyS0biC~zLGMZUC-g@3r9 zG|+I0UQ*m>TiI2T@Xt-e~gQTn$(eUk%$9l(6PvRT%$p*fFZ0?`Bl|nw9(Q+lIwl9MHHoE0!Ajsb zdde`^6T%)$N5#>rLT8@~180Q>b>RAmtYV%hz!h zZh7lvV8%H1co?@<-WPUlN#~8kjit6Pt|KTGJ{J*1{v_; z$x-d{XO%P8aoUZja^Uzr{VG)$GL0LoBS+SgS(TFAw{(N4`o>%6zGRR6p5b6Bi}HP+ z67L-_46-SegJ{~SWOVdn3mJ8yZLzp`)kN!N8~Ia_o`SqWhC9+oz_vHoW`$6bZc6xu zL)5flcs4;@0uP*4GaT8t1eFmNur%xo9zmE&ejyCT^XTyu-&@7H=b)7?+>5Bps&HF* zF~|04k%GojPpHGvSS{0$NP5nMc34DaL$XWEBa4daJqhIvm8fY2q&xaqBntMcrv5P9Vsj&h?!D zq0~>({`K|j{u0+X-6n8Bv#S@ojKBPO%rkxt5p>KTbX*OR+EDa8xzXCIN4P%zeL)ND z1j#FY=VxTlxMQTDv0_^17FllhH6KcN@u4EgkT3YwXR}X3@#l(+dFj(L8G-aAk@G)S z7o-h6R>;SSK8g`+-lTt54#Wi~fzQ016SW+n<4SMblZ=jfnT>j?1_AV7_}LZ#aS;D)lLr1hOb^8oy4#A%uD+8UlN9oe}q? zaDd#2t<&hC!~OUtO(>|k^3`)WvK50zr0zcrTJJ}2Hf?-rniAL(?eKUCii;mk3viAZ zT`$#A5(;>UL7S6+$Vf~`GgzNGz1n?H3&zHwCPR=R@Q$5W@nGEqBJ3fUSL&_y&J%+E z0r2j1ValEwem3GG5n8x2Gu%s$(miRrOl>5e+w4ejI7%rT0+Uq@l8 z94^4EO-S%BCl?zM3<`Sjvad*vn41Nrqc~QPSBwYsbPHzzKAP@5V<^Ig*!INQhEah? zv9bPI2@l|kA>I|;ygZCx5cd5^VsmXdGIOH*HEMETZU-|VwgvSp$_^-<$~cUb4}rjq zl=H4N$=J2M-WU^5J(D9V$W!`kMg$B1E-xw%&wo6X1A46f{F^ud^jX8@GcRtTs(%KD zjt#*`)(6HW4?LRSNg+dDULZ>!NNJc?IBvJ6V_LwyL`tZ@M!-_E!Ak_>Y?G}?@u`dT zfgp@rx4-;{ZWr#~5&Lc`vu6Se_v|%YDh4*Ngj`(lj-U`=@aAOv)v(=J)s2g|!J>G$ zc!9u+{ZZ2{V9FRY5{2v75QS~e2Sx-a;HkiBkp5N$CP5BN!V{QqPlzYa!+hW-OWTb9 z;dFHbicT20wSE2>e|d5wd1;%l05$;+u(sN4zlCss5%saWq^2cDltMS2JAcE5@XShn z%%CcD{%3Xb|A*Cm?m{jP=Xv)UTmKbVL2!1#N3|dVeK|5kGWH1p9@zi;V_wI1KZ0Du zH}H!h5>&0V;O1E0wm9Z5^c#|B5T!I)WjVTeNUFaAS+7q3#p0=ks|N)GW2Z_|6^F|M z4h#ZI(dWqC?Vq22wK?c+YI!8BVVAXgw?x3q{?|*bI<(zgRkgbbk_c@~c{h^z36a?y zs7?sQ;&WCVC4`1fAKErj9RSZIk|$#R{B@J8psABm#7gV2a5~YkA+U^vU55l@P)2EI z&xesM6UhUBeLg5IVd>kvu#t>xx_QoD zR89wF+>*fK%;^4g?(mJRX`36{_DN7xl_R6%Y#2U3;_z#l(ctIyvW@7>84G=lpI3Mo zmUq^R;}0&+d~_sKmEEji>wU+@{Vrr%ySs1mzgiFC@NvGEkqWQS^Zv1PFGWcf{P-bR z?<{?yQ=$pkg~-zZi;zc|GT**O#Z)Q6df8PuDDK;pV(7{dco14<`oM)UFMj5s}bbun33S&=hiJGcF z-Xb-ozvG_yy&&s}h|!5A3!#E#kGPAz0-gYm!Z=i791Q2^!!VDk{KTSM^W77vh3@o(RVnSU}St zMcdb8?1eGXgMwfX^-hKy4;V%ctN0)e=+1=z z3wohkFYnz2QU8CrVBR><&2_WM+w2D&$I4S{n7H?y1(b&eVw044$;0Cwz6M@o^&HrY zaMCdf#DTn0dK`u>3mLF~81W6zXac~-KMfEIYijk#cUvTzdr52d?#R~(2UCC__FptR z@BQDsF#I3(Le22e?6(Fcrce<7Zko7Rq<9E=Dh&e9U@Vpk z&#aV$&&`630mN(UWQM#mrGT`52i!oF38iitmE`i%RS)%&N)FvE8Wa0LSm)hk-$%as zA0PRTg1saw5W6aUp&In^1z^!}_W^Nd$=6Ut=O%*z4-6W3S)&X@`7O$Tne8zm{TK~| zHVsQE4GIL)wRZ#ZY|3B_Uy+dEV!0Di_~Rf|4&fB&{bX_kms-`wPD4OP2)!N-?(Jo7 zK4I?8N(oCVno^4)f931p?I|U8;fgTEI$K+hoDErYuu*>kECbAx{#@x19j@;Z2(|jV2FISe+bw z%%fdN#-2L7G!c)v`9~&n=0&!zUSaFoOsa$DK|w+2U6E$%$6_1|2_17|;$UD8>%ZYa zK7W>qZ}f&ZEisCUiCyFc->tg#dyJGfX5&(qaobPK*Wr>yRQy}7@1F7TA`=_(aX#$+ z10Z_fXmNLoCwRt-Fimk$`EJHRA9(th@V$MdVp7P!8?Xqpcw zz-!8X6o6F^kUR?#K|MF%(9Gc;Cap|XpunhO{*4S#3W@kAshbA}1P3l4mikl4+s7xn zrlO%Hi|gVKs5VA+#~lkYgi8IOq83CC2ylTiqoLU-aGg*DmNCp*S?#!&BIQ@^% zl*IqZvD#UEwL0mlol@<~{F-KcYALh`$QL>xqwY~Ki1lq-20ykh5NH6R7vabOcz77# z0i}T(FUd<&GwA?c(kYw$&55c0_jm3@j0#5TQtM9*mj5^53t;-RnaL2KSF_+kA#PU$ z?a9;&iZXZS%#L&2!8m&IfAn|pE>r@f-JKmUppTPWxEJqSH0g3#DT}h4-`@}GiTFYtc| zwbrQV22RaPY-vAFF{OPKr4`ATYojA-uGi$m|Ic>qKh~un{wWqdMA$ITqe~vVp+%Wb zo+pDY`b~u__QG86MPZjG;8`H^_ew`MfP=+R`TtkI8ROBmi*`+iTPE6dd%6PE7@lqX zKmtRVX^R!gVgD5>a%*uS0;qc-V_VzLz8F8rV-nO1_2U zV}^`9oMY8J`CBjbDPRM_FU5eV4uRj6-UIZ6#{-Cojc)|>?XV&9dKYr3(IyTf%X`r& zhEFIE5BwCc#49uw{dg&?KmMV%mlrh=v(xyOjt^9R%7M3nz1s%(hw6?V+m=0JI%^9_H zSsfs6yFUF1>t8Ww^K^OIP@VO1T^0Z`g~ooic^yHhE6U4zLh&hu zd`|5tSNfne8RzAj`}_U#?LIE312c_uVeDO?6=SOL&>r@$li0Wx>}%?~gUys~J~-y@ zZek=A2R*i?L)22rH~b0%AsNgq_E_m)C*KtY%ZW^gdH5kE?umtPWMiY4@w*>ynh(AZ zew~}6hmpR5340+e_>3C);;bgPa6LLE-tGOec@2X^GU{qWcK>We!A9%v zF)C)C7BTRntMQSTV*pQxmC5jVqo#9xpO&h!^C(h6#@i8Nlz5GK6KGgsy9Ve_uB@M* z?JvaMK`X$DD}*MfU^=pp=3g7RZ)Xfq05XIQ!44mF_}RF0`qQa@Sg;cgctS>LPqLEi zcl0+%!CPHmU4Y}?U3dz_oO_tu<+!b%8w(-(@|+{J21d7^-mq=vimZy-9M zr>_xBQ&ruFWsbZF+>&+_woJLf={%nxvAiOrr2;#=>rri9&#;TpOduC%JDybcygKZ! zf%X#k0x$V|U`Xe0l9hLPbJiJKlH*NqM{r^R#tLZ$6#x)38rjPq;}cU;A@lRE-QT`l za?8EUVhPs@CIl1_HJuzTzCw#wwBa~wPwQpc;dej7%?yiQNVqQ|Iq`kzS8q)%=`p)J zmOyJWP)j~I12(A{Amll`Yqk`r&zMfdT$S2yGe^S>P#GFcl><_?HZ+|R!bQI;dFy=2fFIdv@4n^xuQ0c zUk#FT*KTuM4*Q}Ro)V4D9^F+X$apo|)^x;%VN5T=M!#`tI_ z>et(ynRLDtkueEOfV-so_G z!`gR0)fmH4&$(+Yhq4pZEBMLoyTH}_{&fzWr`H`~s~JFfX|$AAcg#&V%jEjFPV}8` z_t?mG`_=UHOlr zMhcl$#_mWyBrgBd9WDXHIo58-fbsTK|AP=_##frP7K0R&Kkt4()Z0vHFkVk6lBRFG z5e^|?U2F#CJc^|0>x6#*HbhoLB!kjj{I@&hiz;I;c&I(0x#8YPca+1;OjE~w*pj&l zB#$K}LJ+A;Z@_yUW`Irx*Zp@hvtui9TW}rbxBSHEZc5Me;js+I zKm@MI)iQ1T@EQ@K`DjdPlyF%j_Z)5r7>Idk=|co1jy72qnMNE;_Ix$5nB%hb1<^fLGt*|>^+-pgBqL&x*56W>Oc5sb7 z^i^Q7ji`d7^4hj_+(Q=#)Im>upM<5VDgWzh2 zpy@EV(*$S1L+1#6*x~%k?wZ}%5qa;uB)sA#azR^ho;l>d12CsRGq&j^Dl{xwj3~exus<0FA+l;LWn&NJ0`+KpqZ##yFL)zeq23)2e@(DMr^NC(-q0#5my!AT&W}l>YWT8b!_a%UH@`)V*(E z3>yXz|65us06~GZ4p|7~NEMja?fZb6jQ}z2;7b>0FT8G?vwGA?bwIS1-?pB_e%p}- z(WpJq%*LI4j)!_=gQKJ#;$4AnPV*}D<1Lz5`4zb{A;wOOhcvWGg%NF^`47{MmakoO~&uDnp*4j z$8rs21e3?FpgWf>PDgv!;_Ei8xMw;_i@Ahj7ETM4s19RqQL*d2U;Yqz=-YfDwhVH1 zSp433^VV$+k5#!WGnfFHbkP5NwGxQ^35tF`(fxb+i)l3k2K=w{IM7UlnG(|9xaPdG zLUZ~yIYqgf2%Zv-H)bEZ?8m!|6DXv)zJ9hvQJ0)PSY!X1D7S+pXlgxK@LQV6=I!48 z&DftJWrM}N$MV*4rH6xB{*!#;rZYn3MwaBa|A)J`46C|Z+kTZsIwYi}L~??32`V87 z5+X=9C@}%)?wYjHDInbn6QyI)B_N$sLKm%c3UL(;B#ByE?Z2^_8r{DE(W&O%;58WP2k#6fx(zV9SP& ztA_Z*0m2!-OcPO|;$Mw-hu%?!jbu5X_RMvXao5A0IVdMixKrsnojH-(-m7 zIuPi*I&SMLw1h`*EMkVF+*QW%=C}=s#Z%j!KhAjEI1x-06&1xc7ezUN?s$KNaR`P@ zc%{c9t3L42Pr1a@Bi)n4BUUTnwt!&F>lwTb=w=5O(fj>+%A~BlI~*1!#N~XfPlXVEm&4#-YU2t&wUFKz+b$8OJB3#KX^lGZt}*`=FTby z>S*41m;hN2&4b14RcpmkQ*Dnj!+p%rUL_)8bkAE8R9qP7`@0#t?;K%P zyUCF=hzt9Vvtu=<9Y6sz<9sQUL5SUrSOJcHDS0w$dfdS{W`qd-O!H&2qB(lkYfU`F zPQa<1&QI8W-p0oT`{jvwi(~F~IOGdnaFHK?Lb9>O7G%R*s(Y<66#Enr7X?BlGBWtT z1m8ZAfmI%XQ(>ihgTyug4M*GE7xA@o)97-J0*+$>Te`^8YRct5qi{T9Y8!PkFGj;aD`#ec^ zXsi+jQb!+C;TgUjd1OH?fE@HaOY&_MT}wCJF_-8&4gG1s*B7u45rDvPC54GraGB5K z8|J~m0V@F7gOgOV*Fk3Rq19Kyc4&?^>*oN0Bz6ro|if~S=zf~L5^tavOshajZ?y-Ze<9#_o>KaraxG=$FxS;?dI_17(6i5a zWuRgB$X*XwI&{^fyi=J$X5J)l4lIaOw!z>LXEix?OPeNrFf%5yyi2e8Wz^pEWQi;K zaJ6#(2@R2b4-u?(5G6ieN@WCk`yyS#?O>nf1?H z^dnC*d;vU$57BAi9Hgo^OXa<@EIpD{K~vNcSBH5CD*V>gebYPWPwN$sYaOW~;m~607=o1$gCzxcUT8ad#ImU$W<0`H&wTKs=>a@WDFydLU z&<f~H`GnKy2$H&?wi%8qhDcVjl!uhD1|+%YRMt>CH5_3oZ8{tT zms#0SrF(grKgW{RnTX2x$z6l%qwxT(ghKeX=@fho6E9Bt9b>mb$L&>?Ce<>3nLr<0En1UUn$(p>#Pu7O3vhMI5J4r}bP%tISJswBkt{a1? z;WSe2K;kTjJsB=v!5>bw+9(;?-$JMt5eF*Cf3@pLmASg_KOp>s;$g=H5oIQM*FXu}x;4ur;BKvf15YQNW!etEdAPV4Fl}WbL6*!7wNx2q48yi z*T#6sK%~)!PcdJYtdfS19PDY{cB`u@ zrXk2mZKdBWf04`>;+eNmk;N+^)762Cs6E16>>XW(fBPn~<2%y!$=3StINxEa8lwqn z&iNufkj$QDfvUw=#cJel3{Jiv$mMBjn3FD;-Eq&^;$(>P;b`n ziG9AqNeKBTY(FW^=*Brn-j`gWLk->Ar;91L32`Ju(WV$1G}J_)AdJ1V=TxuD5w`SX z|8*^cr>AQU`>21)zyV*GL+w9hu88nDj9fZK#j8)RUf_KujL8fhiM?i_9A@G;y69+W zA+P7{LHn6->a-IJZ^&k7F(`%5PWxGm*`-nGCvR1(6Nw~QLquzS->vev?NfB^y$W!vxo8c&yFQSHy2ZQi^u*0*bhwUdHPi87%k z=5#fx2xm(nA`=n&!JHd>MI7B3 z5H#l)Sm=GQMiYy;)27}5)pPh}JWyYMW&{GovHfp}Jy1RCI}Ed>S6|t=YNThZCQ8Et zmplj5g(u zB08C58}#n)=+x0x@fyo#C8NPkQWl`e4J*4RJMA>S_4s%8gL&9T`)-HZVm{Z^ivR0Y zr}7lqZPpXe)IR~<3`ch4*Ni%=_s{g8kD70-nH zFwyRJtRAJn^F?Ap6f!27`2Bu)zr5xt#te1)7M_8qu6OV{{(`IBWOUl)gf?!j>+I3XQib8DrzTw#hh37 z!dkha>j+<*e%J7JJAKPlU4O@BrY+Bma3XyqjAqCh@hx%ELEI2djI8bQi0wgOna+?1 z{rr~c5JY9z^q4p|w&L*Y6ewDl!bq%$xNe$2MSEQ7d;P{dK1B8@4!(?8MhGA+L=`n2 zkSCMX2jeW6o(DUALRcY*K-1FVLRiIwUo%0P-}M4iAh z7j@|RtyXGBgUqh5%pOGjG=;egWKy+$suQ4;(4=lOKW8?ymjV6#x_*_I3)AR-V zp-Gri`=A0wQPcH%U#Cz?Iv(AxVMS$-Ny6dLEP;J))W z(EJ7aKVlD+_wfTF3tY_h8|AqL>V>AqwXyrtwv1lX1V8L=rQSZmQr_C_T`E6nbX(7t z;!4hJ5#&sbEsbF!jWStl!l(DD>Pl`rhOy~=Y#34ADf2C3AZi?ou^DU?m#=m^F#6ij z)^Ixx-qRh1AjJxTPlVUJI~~Jxws@eXubv$b@<&v2;;!gq%}em~5ofAlHZPF(nWjYu5n^kubZ*>%K+NX>2^&s>f?k?(7V@ zmd=7Pq$}^M&XQfTy@LYJ{oWbsey}y@;Wp6$qPO$u+p z6W(Xzb8kzKg*mVwu2iqP=EG)7YZGa?9#5z;TcDdn^uvHID@_HLXu~ zHNZUBd#Lnr;xS0yWB)8Q*xH!)AM|gxdSxq|=Bf1~KT48wN_jOZSK%9kMF+= zv=NVdhUa&X*s*__=+off_fuyttXM<}>OxkoH*fYrhV*i8RSN8y?98eOm#e~hI+7=; zI&;BYWn3^dx$|5lrT3~ZBs%Ez#^!D5OLT{w*FRQN5J@zrQp!AXayoznZst4)-CIqw zH`y2~(yla;8Ggop>>hFqJkuld^U>C$@99qZUyDV7>;lleFXQ?9v8E^|ZkuaTFx|ai z0(+mG?7L-|Kn~JnzIBiAgyFv7du)wcmPRI*fKl=KiB`u2yiJqGyX;yvS%>jZy;=rHvbLZN^1~Zl^ux~}&kWz!x zhpNp{;+fy+j?{R%3zb+Zt0kj#wb(0PySTHx$HAjw=T|X?b)Qxi6@9-`YD9bXRo6b{ z^aP}s__8p)on2j#M-zs^4jaHR(dR2R^fupY|JBpfT6aR@XlL1#ll@TnOpxyIZ-_mWkj=`;E`V!|&+7x}%i8E;t2iL4 zcFXZ{1~SU5uf`tg;8w~@k3!46!e@4Sii~RO5iN2L`uS-_*~wa{BR9bL9c^2HRJ*>g zwWFfp*x6#gjmCOQG+vO*J#C>k8HO~|C;v=%^M)PvNq4d&Yd8>^hiwkxTvj?DP&gyui(}~GXwA!{tV6FZW(cJlYzdvdDczecS+PWa8tc+WM@>#I046GQc zG5$=QICn#DwWuwCD)XDh5(zmDDwcBID=uWGpVW$HzpDK8VNN79HP{37MCs^#Dy82w zoPyOB;X>`<7m?HmJ@o;}rLVR$eAHc?2K_rCYaJ|7u00qZhH#|t0oj){Jvg5MQwJ4s08Fc#Mj+B?3 zfJ&NS4B}j7x+STrb%&OI_Dx&G*8*rZ=X1V3W<%Qd+xj2hkq8@3MkqeHPKIBeB>}w# zcnfz38KhuJJoOmhf(#)YD%!Cu_f=|v(=69{cM$+wkPo7=E2cl`H!7y_4^Xm~fAOl@ zGv#{Q&)FlthNRk4nw*zT zG5bp1?yDxe=zlWNXOI4INs>m$hVR!YsqtN-E3wC};dp&&crTZ~IJ(9VUG@Le&U%qO z2lOO_4mpD0oX0Z(^sFech3zCGmh*O>h}*ZfrQ!A|DQf*IlS1iwx5#c)!%xRNycV}C zK0JwLKIiFC(3Q~V4K%b@NHHJpG;@a!tzgW}7CE3CQRN0BW{EGb-GTe#;uPg~$1Mkx zh(Kz5SUsF8)qxjT`>MPi(c#f9gO7k>V8awsS|Rr`X{-$+myR8gjUPIFz3k-WvB7mF zaUhDM7uJefbD?Z@m6;HYN;HD&Y+kW#%=Se|o|USbIJj1b*Tzlrxme6pH2iqnpDMU% z#g)z=T|?0S&;|L;i`y!+&y|XI0u2MD_Qr86Z3Au0Ic^UQBdnpi6FMTp`fittjD>Ez z%*ZjJ02o{P$h?vI2H|V-{k>J6*-O3NtuJ~T&g!~!Qcez>gD<{7X|MB+Ye7z$fAn&? zt>=VRg1oPu$M<}TVFzA93suRZH$8)kKjLy)jI4_3u_1&n_1aqT3DE-LzTV>32uQ&X z!V9|g;SpN@^)wFei7DTD%C<}7X zG_7bF7bv~Lj_b)~H)vj?4Af0P4dB2lC(Ki^)aYd$dqI}X=NxwoFAc8Mjt-RE7B~nV zdXJ@-ri1rEmpC`MS!hn97#u0{?`qAR;F)OF{kr?{*f@wP{i)|=cGOH(|6)~Hr|SE9 zYU+erHSlk5JR0nuk*%thJP_J6;uPS^-0nrQ zBT#GB2>wpyR?&1`+(SrhC)%Sx9B8{pngDJ#5Ptte@2YJw4Z=OsZb^2V@5*AU#}_6b zAK5wAxPxPz>3ii)ws|0igbDQ1Gr_(e%(Q%h%$r{{K$m2dQ}6Yd-PhoBW%juecJ2-3 zdMhSHUYvwFtL8{8e|G|wG$NnP*r;FLy=~08)tEzHT{t%~U$nts8q@DqG2(+qN!`3& zG5CCB3ihV!Y`&+LHhcYi<7O0m<(-zmBDS}~qUZ6HWsHRw)cWDoBa{9Q;y!1q@!>fw zetBQj z0#)>dX=SxV<;&pteo7Gk2VqkLKKL&gpB!9lcvy;VNC<)(qMvrXuF;WlYFp1S_D1NN6WKtzRo#pIU z^t+g>1!h{rVF~Nj4f|{CQ8cmj|-EptZt;Q)0+&?SbMt$ zXO)crJ1^3H1QjmnHzjFulf6DML(;se=Xwso?ut-8*2~pvkJH*o?51gNKxP}cMMMmI z&Q?M+o%mS7ptb~cRqTEHD=jjKfr7u1aa0ui&!~mjR^o}8B-|KkAj!A8)+#UcNM#vY zKgQA8;K|^-juzz5L_4;y`5E7$H+&pK7bMNHIx8Pk8My7SQ@;iUHzQGZKd5ZQk9`4u z6Rw75P}(2_)Wrvi|2uIt1GtLOb$6RqB7FDiNvG|P?=B=Kr_Xlcs!WX0B#yon*`g~X zRbq#{iYz$Luc^jm(4B+fMz!t+x2{o4iwOKuHC{(#_Ujqh^iw5O(KdIJnZWmHkxvkH zJCr9mX-=zMp|DH%MWB){4}1}=Cgzq*$NMK~_+i>W0#QgD=)hI;h^nue)>LPDxn^#q zA0~-wOy$WRZO%1_tAJ>L4J1ePZ!N^Jksq9Va&k81+D3e~)54QB)xx_@$b>cX2;RzN zH_hI$*uiM3t77uCcciMfgX*#q1abE}HvmW%{gU^qF8t&XUJ5By(B}^r_4jTYv4xr* zciuTll@tX^x(S;wVPWaJT`TcA??1e~0TC&5%wA1$B>ZuqzGINrK`S8|wto`L53y_5 zmwOPW=w8o@tuZ-UGW?x|b}>a%#_0ZepG;<;xB#Dmzhb}Q`+A?fy5`HWGdiXaPe@w8 z3honaqW52-62l>+9Z-K_LuHKHrFgtev?;aSt?4z_G30F6kLXj+)Cse&kAUJd`wc-I z5Q^Jhss3jvJCOiThVi9&UhO&H>7Kk18r`rkdzgL?M-T`kB0UYE0Xw6Vb&KEXkM5|x z-Xt*LoAKMfjjSaCViWmh*k{94T%6>kzE^NExgSU^m2VFG(Pa8()J(yQk$75ErcPg? z$PChhkToe!`bAQ78iF`6c+%mf8M9HVXA+65R*VwHiJf4PwUyp$LAOvPo_6Hrd0^Zy zV`SZLWZe@u9kvP_qX2fc3$Fn!zZ~RE%vh^`?dSI%o_Bz;hxg?@X z8j|L#!9KZRtYu6Zsc%!LsJdz9o-WUNg`v~BO4AdX;Lm-yBTW{Fey16h&_<|l-`&Wg zkStnA;zAxkMLUTL5m_WGlZOy9|9VX-i@zh^+M0R4L((_R_FH7oo75uvofRH&<6{Ik zAo;#7TSBeF0aRz=ieyGm4qRSWW)>bpCc(AT&XH&s_q^v@=K@AK^Rry7m&{>H0|^0C zxtkL~1J5 z%q4oZOnJCDKoTVe0X-qdgZxlsH%{*{=fSwyU%q&tU)h#V_aG*8C6Ah&yq?RfGvcOc z2o>xS7jmi}nh>zl2~TN8nz14cK)-3!?Ne;{Zuyw~Pdr2IRu$dOh@Sm?mbEM!&DIYb z&>ow3UT~KNnNwB8jH56H*EG-L)ZRFWACe+m93h^I~wSH zZQTKUzEik3obO6jqEJsFqefXOf zkvhW|H2olxzxTBrJTYjUP->GsuI(h(HRy~O9&dwSi2v1`B)_s;h`yej9Eencu-rZN z_$|Xd0T+Kuo878yH>EIRGXn!=wQGG5Q5npFzm#pTIbR8ys07iAt= zAp|PL{v;xKYThZjiWQnQybzYn9&q{B_3U-z0sN1|%iHWU+2n#n^W=(A<0tMPW-LPSj%><$~}jD!6Z zrB~)ot6Lm(WBRP)2@mq}LbAEChE_meR@q1eF>*Bwe@o^HvprLuzO8Jm*PZ;vTuCw` zXa(nyHoJu>=ui&)>_pU_j=4_|nLJQqvckGpJ`@{TIt*qC$cnIeD#e|c4qJNb+HDml`Xb35}!TXF&jaB1)f zt?W_{4Q5F%nBBvdd=yhfZz`dp4B@e0tNvWYZu%VIt(Vv@GBxLwsndml2RlKnLRJnY zG5hQ%`HO5hrRX{6riYkrtq^^ctK@vt{Zq;_4IQn_v5O zd!`dchB$*ch?4Y(BtsJWMW(JMZ|RliI|-(wMxYI?8zy%pK>zUzs+emN7vV)a>0(ACHw+uOlw*?lr8 z)VFB!aBqs7#uqw0weUWpwQ#7UH8ipzGQ3ervndlWNJ(#~$udg2UJncU?i>Uei{zDS z5;<)%)d9z3lGl}GUdF@63fucjs&0D9cgRfj%qq@8QT95BD*M!2Bbokp=H$_8;+ofD zJ&=Hxe|b?cAy|@vM%QEP2bRCG^RJG@SLwm5xAiu5; z3lYAnz8Fes=}!*yrZxfC)63PQSEd&!4Cq8<&ARM6{*ZeHW_o>FxL^H`R^Bpr^7~k< zVhgZM${{ivPQ6^i2{wC$AXpZ#UXd&ZNkLQCW2@GFb6FC^kOX&Zv#G?8NanWrrMnqd z-+nXo+QHA5k|pi)=d6h%h|FBiM>L)uTOWP4!?aO#z44uksg@!)6+r(>c^(M zs1y)LgWZ4Ocy_WU%F^B+yXb@?F+2B0)XmY4g>kXZUr9m)C&A@_>bYx?Qj<#ic+X|} zO0s!tE!@*#&$X2ESJu!fUXNtm47Yq*L#XXaU*ypu<4_Wzp;r)HDQ6D8A)gJAfVLa& z=76JD5A-QRF|#3T>{Z?|%#va6hm8zA??%y<0!XpAZPMt>@Lw}!oCyWyh2b|=M=0lp zSO&`6(r$crh zqDI`w&AGxhI^XfJgxL7KjwPe4VB1l@Ky8))+$UTn(jS2@Lu)IfNUlFLcjd$4HMdmd#cDj?dU1?+>VDZQy2s}`LU&u z&#m;O_ic5ai49S8#=n68W-_v{`Us7f`t6%K z9gdO{0^!$xOCB(YE6sS6HICk=!c1*1XZmjHe`mo5vHtAaz>2qr%8g_1YrcL660Z%; z(f>Pfz~8Rlf4JlRgKNm0W@)ef< zMw>|aLvHw|Cx2m(LCTKOhj;!qh&X+=u_S z5EcJV84rO??%!h@I$Uh>oV>fgeF5%LHOi9z>M;8kcj-N`j&)U6)181R1-1l#DJ4X{ zAGwIZby=9VCsB4qS6eBy6I#L@ih*}bBcbOVe!N57Qx5oMx8eqcPEAyQ15w%yB{}C( z8Rhal3&l2{j}zw}5SttcQE;UjW-OO{RnP9dxxTg4=HD1PlAaz0Y`M`R<&gy=+6#eJ z9mkI#c({imW$O=+g~#upv9Q%);(@5|_0dR1RNnYu?npd=whFno3%*XlVRgGCSGbRI z#B0WgDnYgdwr0jd%hcE&Eb*VCqoYxSneA5APu?}j7|K1~d3cu*Q{ryyfW^p6hAfwQ z46rbJd=PSUkb$-2l_`MP=;rstmb5csM^~gn++3N6U{zh$7Nd zcJm=HHDkUHW|TAfqLpVX1B-aAWb~G_14nuKDNVUnSnJ8lV&35La>8pWe}9pmZzusl zRI-Hi2`o;nxU3;K3gd z{BK^he@fE+O<(yxfCGTaTEF>^?>Mb{H&F#O2efYM?JBW8c=_;u0&$qvWByy^@DCI5 zOC40X8#w<%x&L3|1U&x__4r*+n(%mA(xYQz43Y9XR9ukoUuYM_9jZ@)0vj86ilU%av>CFHs zdd z6^QOIO|-Y5p0KEP_TzhRpV}Ksj#GmMdquX7e;eu*Cqvv$Np1e{2B;G79WAFQn81}3 zRTh&bZ2-zqFyaJIM`YwU2+l?*4BT;MC5|jzXJXg)U%9NAWf2P%5#|EKMcU zNBM#21oI|P5p;`+9_lt$ZsEu8v)${EH>A;h+Tv#q=6h|Ulq3|dNWuRpYTCIWToU)^ zgNU&(+pr+#H5?7#fSZ9HI{ft$PEFd{^A7;iad%p>I30X6Y844bty{}?o@xB`^!J%i z@B51zhs`ASn^~c*@Xp~9pzrinCfdQq1HAL^oo+E|Df(S*6nX5&uN!}OY?sD=>H^Gm z9ALJ4*_-ha_U8+OQ1d?uBf%GQ?d50GzcJE+A_uh(z0CjX6{v#%>J`KnIVIu0%N7f2 zl;j6t_NvmKn%n+;+5mx1ao?er;eXzSUjcT&9wC2zQ2+oCMjPV5f`qM|`S9{Xl74HQ zFdeasZ^@aZl8~3*h;#>j7hl2O6s($0L;w5&s-O8r5P^xAZQnJRzYPPMkg|-%yN~ZA zi$)dy&vka=yP9l+C%WpN=Ju`(Jg!FRSc_Tpc1{OLvN z**KVBEcmcM{6cQ;&|fB=TM<4P;kup4jcLjFUnwVqJH}GlJi;A?UDr~XIT&8@p!PSv zoAa)`e>$1KzRInmT&=q|uzRcs2}Wj<@9v5a-Ug&xm3z0ir~FnPj#zfZIzQ&s{n4)Z z*ZV*@?+f0C{p%F2_{g(-mN@ra=hKxxH32A6rC^A(p!GHUU12CgVY{zAaJW|#as3%L zU<@mQ9&!X|cz?>q^!qiIV1oOn*ML@K_`6jZF+ZfU_(_32XV7>{@DF>*c z-yNFq;SFH8_5GJmdCC@Y{`=3rOOg#Pi$4bscXHI{Y8ulJk5B^M5+~5&Yf)eV~r*Ct3u-r9+44^`8 zUzareQ~m!sf2`ju#0pchRUz#t6pATo`qN5J2T&78&55S#-_^s4BHO()dIa{GYC#I0 z9{n}Y{ZzpVPg(N%|9<*H6#4GrBp!GpQ+wb2-2ic@@v-oCO%mV6EWU0c0*(F1{cVeu zv0%*j!++IL|0xfbs3T3bT@5TF<9j(UQ=_0Vf-==@|*X;NbFg{?noC;H! z|L(EVOmhENwg>oM&{E&d* zgtW4HDSKP(`A-5u2RyKA+q!TRY^?i?Cx5&vr{5v>P6hpO{p$(PdL{RHk{(R=GKm`B zeLtAYYAjn0rl4PR_fx2@UcHE-_$)?uXznLrb}nz^ z_nI{=y-UrUAD~H>n&*LcAN4)rF|=q{pQ>JupmiYMHd5OPZj^{A`ODq_nhK+nroi=q zPCn=Je%ZRsgS&vCl^c=qoEvyNe$xd%7K83w=HX(qP1-?~W|s>NKzoLxFF)J~Q;Rat z$anH)D`uEi2GaI8h~$FRj2#@VcA<`{yyeq>1xOQ}Y5$ps*i5dU?er z150(Sx~08@>cQ|puhL9@h!?rW`w$dFQS!kJSOxe}WQlLSWaBMxjNC6;p&1KrroOVW<}W(Sq<_ah??BTx{4Abp+9f+2PP#L1B14(zAd9$j)P29G=SX{M7WyvdXF)YHI zvV6J&Hm)KocF?{bJbSf7Wnk5cW!R{$5qpCA;p*pjPg)_&*xX+e;s5rafK~09Gj4Vj zPXLwgFe~OulxyV{^j*x8!VqP!t~1c_Xqam-f@8ACeKwRM)6CX4l`QJo(SEBNd$C&= zgPOVd?9E}vcn4RRDb&;@RMh@6JDX9?vKLr6g$$&Pa)q}~Z}YWw=Do*B9E>p8huzsA zBQf0S4f_XBsn@d>G@6te8<@(payvmlGB=I6keBIqJAeue_zw2gw4a~pEg;Z@NBabU%6xfFJcec#ef6ve!#$xk5x5{Lj930E3T zXOtx?bh?4+^xXt2W--{*z+N6_{&J=S<45DK4(9U!-@=G?$9b)(zxrUT zNcGwpPxMkk$!0%(aQO*&r7aDJz)(agCf-HS&K}b~d;bz(Rd_$3W^k?CN6k!fcejnU z6KU;yr`wtajg~Xa^f4%TqLu)XhR-v8Ipe}7X~3d2xuCqf{MKjFJDXf!$kqW>WY+}p zt9mmNVqOK?wY&~G6iLB9wU{bcFB))C7KVIa$vUhlE!Fp!kXFR6OtqanDl2=N;JBG>Aw0rHCq$UD+7Ccta7%o%HCo0_4})zHW%M|@PW%JaC_)(ym2RE!zl=B={dik z4R>4V5FY_kI{`s5zxj>n>1jUeFL~9X3A-qsk3VFn+Psf^_stV`dm=ESy*Ctm3wU^J z_Rp;KFN3JOsX>ztEU~NVOpcQDmj-bPoOFpe+u_%hsu-PJ?DvSdeT$Rdi?a7Z4<%wi zz^DbHkFPb2Sq`R$kE%-30ny*h>6t#t#RcDAmnaGg6d5I)>ig-gA?O3WCu*XvvQgR%c#iy1&w zOJFl2F2cFL^-A>3kH>3+GgUbK^VE8#FSZ1ksDbHb`E zL^G``pr`_X;;VWi?N^~QWYje|mK>Bwx=e}qzMMP}>ld$nQ@zgLIzYn}>nq}xGj?|D zBrXp}+0YNs8BVX+)vr(_WRM(o=Chy_uK;Wl@s{&>qI30~_1f`jpx1wKyi;XM_V@6R z!M9s=UwF^ub6gs{)3mJkP@gtgk|QAGbWxElMbBF2CVPKG>Qs2mG%_qnYpiBpe0K}&_smh#mtyPzXVX&y}U{hbk-IU);czQE+6SCWIG*X z@4czJ_#?i;_j1!hl#|y&i7yjsih~eKKkQkkiR4=z&Ur9dV;K`}7=mDf`CA3cesUC^#g2~GX6IMubnt%`F_a=*WQx}u!u>O zb^TOC?MVHJB^HRnT0dr|EetNnZ*tkpKf9P7eu!_;wcV!@l_UPPbtrR3-u^w|h<1b3 zihK0=hO1lZ{@+>vGSkzsRIz@JgZIyK8^_N0FPDgX!%+Iy(=SO)K`5DY1K&iMZfF<1 zXMYP?Qy4BgPyIJifrOT1zG|S2neqcT2O+j6!mih_k3JJt2N5z)8HOB zpt)6@`Iaj-et(~aKfLHv|K?je!xdkq_p#2!y1X>Mr&E81I3>Q&>2gdL624X42L-&F zG%gY>9DMSf&%06rC8XgGmFG^WH`~`P0I$no#AhT5MV?h_&@zT=fPDZj>ZD;U_~H=V zg!o0NuVg5Lq`~y+N)HCe3gfTwhA$p@qr)vtv2@0mNV%ovt)Iw2JFBVe#0m&!DNEBQ zto8{{uzxeYz7WIjDEYEeg!;iL zbT!3t`9~kGFm(Cjgn@vP87{lF&)Q%H+vp*H&*X)B?|>_!4i@wV+bn-0nSi6>iSoml zQtTX**2|vX6qldMa)c-8T?1eM$;lxU(lt$JKLG7;r}%Yez{l|mD!X3;HstGXUX;W? z4{J@2d>~1<<1yHlK@+jipCCXQY5C9rK-K`ZWof_6`*b8%a(xRq9Ul^ZA2G{8GW%Ku zEh($l2KJ+IQr@#jAaeQh!ezO8kIz}3A)@$M3H~q~!1f^C=dbxZ0|U=cB2_;$8EhHE zjD2$y1GxWa^@LIndR;|i3FDxeRXdwtdO0%dI{HK)DbHT#U{o3Ea?Svp!7#5Kr>yoh z>0j<}=WjJsRKx*%$&3odY+a!3AE682$oU-CWKpL@QgF9L+~n+c60(~OL){2=KRIWL z?c;GBpTRbWYV)pe9@K-)^Up_r?dm{zSIT!^Pr^+bgMSK}M-H}Gl?L`?0xK0QUV%LH z#HkUE{7!yercVre?O7y zRbAWaNSJmNi`?z`@BkZQjBxUk@7omaDX`sl*hpeS#HpY-LZ^fMp) zwO?Jn7xItPheLCIb@+V&=(L;et&I-6nufze%WK~wM`GUH3Th%e=~#?D6<=|$4(hl= zR6Z_J5=%Ai#%XG!%FGIhXWN^3n zyO;&2RBx6ja}6>LeV8D^1z6b*N;?mlgunwTiS!8+$>!C23IdVN2T;o^Rg=&!AzE9Lxte(oUu zcobc&IAjtKyK>3%Ss;bC@_8Rfhs?=c0sh-F%-2_dSf^Jct7x)!gu=f)xZ(C?p-0YS zSl)5Mytq4(x2bSmJ}cr?BjA_;Hr*-qGp~#*s!Oh&-RI5cH=BSConyQN)wGrWrfF65 ztM?gn37+Mrb(Y_tCc7mlQLo%Z*7v|@+}3rNbzNyv#BI6-TmH$Gc=kPR~HG>wrI3W_z>3?jtHPc#|BbN>|S8xu>_X)2T`y@X9_+OpNvlbO5zSg&L3 zJ-8?1>D+dDa0gXiGbA|6mW2gt)PgAE<9^#J`Mk8A8xy_@nY+a!oL6?VsX}dMp8(z} z<6h-ua`1ySbdibeY|9qv+7@@+nh^G+(7knTriZe@S-vd0{h%Sodp=QW>TtdIfP-?+ zsK03flX7pC_Q#hu>Aep}O8R!DKPsJxD{r16gqw13@Xv*SA@IYELFjOn$7Wd_Ii#(H z5MAa_Mj;vQpZwaFJ)t8TEs^7s<~xgnNH~v%iozQ&0N~R!ixB@&^h7Qo!|T`yBdDV= zqd-OhHeJ7|CjMPP0T#4mguIS7)NHw^{M-&JFun^RMsP%uU9upfJyH2m=330ct+qp4 znaUbP%dFtYMv7aTXvxD=nt7dZ2SP#&{LktVG?GHu`6-?dEY?3Wp1mijO_E|Rl&ivu z-n=J}bO5d*gTKH}_|~>?F|gnl5q5}{0waBDjbcXLWHMs$8^Mjg^<8p;nl+oG;KBW_ z7Ch3xZDV~$=7b9;ze+e*FZKKD)CfNHIPR4%2>mXgiMg) zBa6GAE8=3QSSSq^OpCJ#E|c{F=j10vqrRby-@5pT9_j359Ugt0cnyD4tw}|7R!+(L z8uC_vLQ2aLC6&t<0qrm0H#O6I(3m4xIF|FiZIJ8Z!Hc1pUX`{Loy6f z(%cKx_*klyF8B*AV$$3fb|(0{xtgTyCQ9Q16j$Dn5F4fO#N?3AtC*0wCmL8DT?H(h z_im;b^;ITPRjxcmv)|h2>R_Xbv>fIO9hOo70>O44vgRq{Wo1jB+qt6eIpmkV&pcRM z=#z|qxWSY!E*E|^-X=&Eix*wT*&~XQjx>%d?cTy_3wZ8TmXp)!L>kzEJE}NoFDV(o z#3EOpbsOL1ji~GO3(Tv<;2I(I-S~eBLHFk?%tx1>8Zk z6mc^{&F*ahhEWxZ?T1w!5Sjgj^U|!3P|&(l4s6-md@W^OT@HNJI0m2n7Uns{g=r(IuyS>wE~hJ? zn-Qq}fh}wX9d@}P1^;ldPkx3urw}FHSE*Dny0Fu&`Q$crqN6Oi}Fru*Y2@IgH{8MvSxFU31PGe!s!H_lcI z>8(>nz$d||&sHCpfx!C=uHDxjoRFR?P~G;CJ6Mb@Xoy8zs7Y#IGL4vL|8Rh{@r=fT zUm>dQohG6UL7&5EC5k6*=Z`GL&vgS*Z-jXH#r;Ber$n0sBlTV<8~Nx)cJ!p{G1m?m zE4GM=>&c3S+&{Iks&yvzp=l3w>0Ic#V5H8x3-5x)EavE39?Kx)WD<+;CdQ%0<2%!T zpZh2p?RHUQOcj|+-XByn&d=m(ME61{x?n28FBDtp}h2 z!{TKhKop3TDkYDii1;^A%;aV~OS{~WK`42FWprut!yWU2^_i(eTQKiM?{OCn!V}^5 zx#SXh&TwDe*nq)uD^+cdg(F=bajt>u*+Y-KfPMm7w*r~XZ`b?2%D)vb?-952qX$hw zvBRurE4ejC)gn72{etfirzEu90zY-a2l(%DbrTf-(w0~b=@Zi-gmL$2A4d3Mb!^lN zqV7|}nCO)=kD%e=ATyvPvVw;KNAUeK4a&0`x%S!@-+}he)c4A1#&KUCcX5_hR%*@px6IF-r^Y#~#d`L*!^OJ; zt(YOYe5GJ29f?p_Qol|nlp6P^NBny#_P3$7cOd^_D$+{Uu@LiFHEA(9jKfvJ$0br> zA_JT;`+<72+^5PG;-9>{k|JT6`PqEEs48SvA$~O*BOv$12s9opFdeqk^mIxgzk;{S z+SoEZ+MhgtV0r>PgN^y@T9EG4sRjc?^zyaT(I@VxR9^l_URU~~sVLNU^zSSKj5V9e zZd17(Ref4KO^C9{R;*ipk5I3E=h;LOEB%;DAs!znP67h`;*sd?wq+cX*u+>Gr^q*w znqj;nLsGvKiAOgqPKX7%?Hu$U`d$cXtR(mn*tpe-?R3GA@h47zqredzuZoQv{!>zQ z5SdB1CE?CeDFuzmQEo79>TLOHUWUs@UgV?Q1H1+@9^-UIK36p6jw-7sGc;LJNGqKU z>bqE(W0+pd`K#y=X6~*oG;>S8f}`5poomXdNt!VPXTWYFm}@o_-KqR}-u$$YJhj;L zQ7n2Pn7nc_vDn6E0U`1HIU|@BqYOzzc4)|$zkuFuP&TM_R8v$)WOLtqrJTW@DB~giLV*F6)r$@bP)3L=E?!m zpIeYEHM}c3xh*c_f*PlaOUE<9YAb#P)u35H2Ei8PmCs3~-A*{FZ8C$0CbJ65bIR&k z`0rcigdIS~8`8bhxY#op_-Qz3vy2ba&Ui0P$G6<~aB*4fcpU0@RoGQ9n@kkoVixif zjQW4XsLy$ItKLVNu`egzD`TgVi&J*GQlGiSYu#pzZCQwIh5tOige56mR`ztN@SLsQ z(KMXIIb8`(VB-ZGe@dd#@G8(R(DnIY7dCW$&;pY2rKKa22;?{q*$;C)M>vK;J- z91(o7WtXSgpli@*M@Bl(1z-&xpWGQl$HvB0obD(O;<~^{Cfg4iClQy|!RUmElZnC7 z%`ae5&kKCO2=3ym!V%RQ#5yH@FSdF9?(|*Y|AF zYb|BK9g9mb51-lz2_#@~QKUF%#kJlR9wJ;p_rUDP9^=#GQTZlH1#YLH-2L-H+Pt z@Vhj)*s~`92QJ|bf8I`XsNi5OU_mdZS0_#^>Ia=k~#izqZ z+Q9-d@p;G^60v?w;N6ZeORyi9ggj8j;>8>oxf* zkR7B`gqWmWo=Kp|_lVy#vAsA$8rLS2Vz5ML2o6{3oNz{RC4HGMlE1nmNOF90!DZS* z?|2*IV$ig3SQ;ktLK>-ns={vkI19NOtYx&MKT&tYr>bx@{Jh5BXW6ifL+J846>Q zfyi7|I_nc^OGkz|tk0QEzRECL(%_*`B|5Sun^9ZP;>RWNU(&w7!=Oo}Dx+VGHAPd` zjC2Vq4S5-%n;^#d3vYzyN1r-GMzUNlAV!jZu_h<}!>Uf?A%rp2LNcFR;C#dnfF7{ghJ} zTzR#1&RE-K-7fBD=2;Pvbjn@y+1+g!PNZ$cc=b$R!ABA-e8-si*^(j9#PlU1{R@Qk zT^Jh+x)Kh_ISw-N7dJ?!fOD-DX>l)`akqU8w9KBXn(Ngun)E)Bq#pu z&?(U=pD!90Xu+@DT>DI=rc`U=sRn^C0#EU$5Qpk7@aQ44vT*Yu)?F<(?h1^oYmBn; z%pIqy=ndowE1@;6tG&{%W}c!TcJt#CHIAXK=^3E_bCzQd@iJ#DDP&IAVXq_}g%Y|d zp$1DZaR+CqFGMr-e5Dm-eHI`NS{E9(_@Vdl*>Wa*Nu9DRy68 zf0s0TL#%Mo$yl^GNZKWa5;ZYo`Cggmq~J*buEFt`vH8`>mrwHf)4~0iR~Q#Q%@5kM zEZ>6l*k14;c&<1H25(5)pD=qOjlRB9tjZ4NKcC2ro^6r0tg@ka?^sTGgJ4j<|jKO}cGd=U}K-w_^9ZZFL`!4gTK`hY(aXst@xL*~QACAS>}{f{ufp%mW7^;=#W5(!v5aNb&CmxXsqvpWuJo*{-WDT;8PPI4~$(Z^a6F zK56L7$6S851!12H4-&z`2Sc;#31kfn$g)kcub&LYW)P8s4=d$i8YeQqB25_a-`jz z%lJe_I@HTN1O> ztMl%RY@}sby9vMQ+!@L>O(taIwuaZTMqlq+tT`qzoCN6y?sQAVYqdk+5gt;`Jh;J= zKh?F6&N7Vo9b#n}jX~QeCzJV$KOKL4bC6HbDl=Rz-7^#sVJPtQuH=JnhKnwpVXnEv1SuwRBbxVjDq2kQfpr;}oPO=E?zQMhhT`e#4x0~Zf^Pf5~ zaR3n4=uyJw+~Ic@2aDy;NdyZP+}ESZ6W&LjP*DQVL~T_=8sOR+NQhuJjLE&m6?J|d zoWZ9a?>(8mi6hvhldD#B&~d}Fwowe!G@h^i-s`!X{OZy)XF~g+7!V@% zLby?;WB2$RUEJdTUJ;xOE79Uwxj3{_-+^%Lrc_JuAl)U};1lWl{lpY#zY9KTBqxEz z1e2BpqF^Y|TOnv*A5TkT6t={2-9X-^+w4yo>saj*R>c zga{B1=6oGA{Yz;{!l1LzM%iFSwdSUuQ7T=@r2KV_5Sav0?6+a4sWZEumYAlK=uI{&9BR z-Byc~8v{&q9xf?s_rFyPM0Lo%@u#9IW3L< z`<=f}iS4bs8Jb|s)`c6?YkKF2Py<(~Zhp-?&cN>iq&e#QQ4d%z5`J;z2XXHH<`=jh z=z^h+r`MKnE>uc5WEA8t*NS&cyif0eJD@wevwbcbdA*?T_ha+IBC+8ilnCOH_F!i7 z`?Gzjq-bhM>{87~RB$%q*w;wH)IFwbBsD;+4$?0++$DI!eEX{nPvGPu={g4l>qmxd z?-|~x!E19YxpZRYd9~o3OC#E&8R)=FSNV@bTWgqKBPl}~Ee(+o&ly&OFM_h&Ai3Gl?$-UjVG>Fg>Mf{V(KI`r-5-+9CO?_f- z{O&X3!<7970;BOpUFxGlLJITBJPD(1drbANo-=6_my8eOE%*4xoVdMZkXSzkwgx|T z>5`morrbPhcf@|@e}=K__JqmMGo;h^&>*GIsk(Cqcv0WhH?^g2s`IQYZNkelC+U;5iq#u!=gS$OLgdH~qt`er7`#Z=1pG3*=lXv!Sj@lWii*wMs?{0>bBX&d+7Wi17Y$uMbbhQC)cZ33<=$&Y$)U+xsVQN#}$ z;^pE46LQvV3y?&qVYWt>9O;A#YtM%%tC0K2Vp(f*&_s&1G~%2V2OVFJ^7+>1AsgI% zbES5=5TiD`E@S{W@&xNenIbu=59zhQ$30SJc;y_*#bT^2FZa8`W|mhwZ)z==E)w~- z3xE!7`aWs*&+hQA1=H>$_YRMQzkAbvTQ0~+Y1eE~>Equ~af}+dY7^E)#eD{0)vM*WWa|35|VOqvzE{(&@xtdU8G3qH@O2;>F)GW|^ z&*5xa4Y-4e(M-C4&te!4Xu*ZP-jC_(FxTNHy5w_Vz^q*hSD{(OwDMZ!J{id2!2!K3 z+O4C812xiM(n4;(<{W2gr&byo8p=gql_+c2Y8tHXwWlCmCF|L#9m-S4Mw6-mHvqH) zyUOxJN&$=<-|MpOP`+bsU*FAPo~aSLmeC|A0@UUaOSXZJ^M; z02<(2uDm=ycGk5Zck29{upipOa#!SphNoh{pTTDsdz`gJ-n^w%P+=n3Rk_c zYA)oB*4r+Ydah2lj<*Y9USr}-$bN5e*_$b9wns{&Q%udh<#s#thV3230>?++rlBd# zCGo()k@Uz*i9;1Et_4G%k##HaE*6d|FBgCKfa@J4J%Q|Fq!cFz)DmeP%41o3`1p!` zrH6#%*x8tS;e*V~FNaFd#d7gLuqoFwKKCn01lB{aVbmiqoszu0i>3FzPHDIG2E+$6 zB6*YZG(uqbIS$%pa=j<-p4mbx+xu3+X(|i&Jpk4_X%NZWA0|UnYx_aJeOcqiz1F3- ztC4i*1Y8AE$WE^|zPMP0K@~+1MMQ(+ygAYZ!RAARr4zC7x|s(Uk*T@ei*Md($>8UMli z%86O0UJ8%Mzx!;u8GSo4BbCd-G9}hYq=UoFh58YUzo7mbjF-c3LMeFx^t0xXEK1&Z zWj2nS#$(uH*0`(vK~;9L79Xe1$`3Rk!2Iq)ubez}?~rYK@&~vR^HsNYTd6WW@0-ro zi_yNHPt-(Tln}Cw!DTWsIMcr>`w^spzBg=$4rBR-MJ^Nuq|O*J`dBxin(F)VexX?- zk11?vYbZ^4mvGx-hKj=Rj_ew8=CNHdLGK9TtnQE;1S*jXnSpNNVKwm{BG-Or2p9YF zp+Mh~XX^to$Xl=BLZz_o@&V{!DW-!X`%p&?V~ncm2PzOZ{iq-kBzd0HYn)dmOs_-I z)47zOxn%hyYJVeHoO->T#D#0tiqB;T$QZ`Y`3*~Hbp$6`Nn%OhpXsx42hDHFuz%9f z@|pSm{O%{#wZ>|NH|*($c6VpRA$ES1X5=Z!C66sKi{5XQIxwIMA``5y6b|!800Qzf zp)c`(J|x6KM{9~@8(Xz(Wtd~=U`of~6;xjXQFQ$`k4g&1or>=UCVe!F|t5~97M{Qfthpo;2mUHgWB(bKB^2)8Bx)%p=WM} zKIa)GhVeWHgAn%1E8Zn{$t!;>p(Fovn3sU}W$g)UuKgUi-s(&Grh0U1`6fRg z?aldPS9>7Hh$tCJei{JemX!ObbL@RgSFe;9^+}5d>6b;Db$Z&+;hTJvv8U*f)F@`g zc~@4v8-v}u)vE^RfEI^kSd`iC96tASB8TG69Sy-Lzf1L~mq*6p@l%k)?`?55yea_c z$Am;6z4z&}`fO#s=y-eSh+jwfT*X3y&Sa%n@93rQ&(K6G^?Et(0iddHI>i@+f6I@_ zjf&9T?|8f>O{-UUJE61)fGNtl7qNz1FdyjT>(XQ|Jw*N24f!vh}FH9uDY~lfzK<_}tUqVb5M`@?d;Z z>kU)?a$O7K$F*h7!tcBt5}@P4BzD}4KRNZ0CrR;SCKbu({#_tX%4f^K-S1FP_rf39 zK6({Pi<=QtxBz3m0Z{8|CbCqLG?7b-tTNn4(%7ys2*$!Y^m6i2P2)RM87|A9wEiyE z%UiDoA(a$W5z3GbEMuU%^BgBbQ68c(E;rHo6ClOy#f}rBf-C1?m95V z*8#r-zve~-V8vj*T0K<|uI&$tBKbkaoI%!^*?`^8i-;Y82raKL;opAA_r8|TK_GZv z7R2YeB<9EAw}`p6^aI?B+c>jcCP;!uq<$by0c%G=pv5pWFP2g58ZyT4!2*IHU{7zE z+acw`G=$PVcCJziwfOqNElQDFy7;h{@)?2@gq<%XW3v0(PR=8IoAkVVZc;_{nnQw~LUDcBuH!1_BfEoblg_kBc<3%56+_-}qv5f1&|`AtId$62+P9V;yAGSj2-N#@n^3Ydf=Z#;dH^ zL$td6zgRWmka0l;a-IE`^TEAG_kqBF zN#}dtDmnlaYZ50pusB}p`^NA_CpF={-ZS1RNJ1~=lOCV_h2~dfY}%DGq47^o~> zu>4F-%;Y#(bG0k$_0c#b1{V71KGgTotj9F}v2L;orsKggE3{5IV>4GesokQV^yR1`P04`Pzhliv8B%rO5KSoID1(U+vHG za6hks&WTgkCF-0VhlyIf*Pv09u*g~waFQ`gq=Gc<0_!%Hsba+-P$ebCSp<_VS&&e( z;|h@K0U9lC>P6Bfw`XmZFxA-$JIs#})?Hv7>Y#(aY(`*%LyveNIt%o4bb|6bF6pHuP zuAJfxDNsPhsn-Bw31zv>!34l>C?6I3_90{?(tpr+caYG|^v7EoI_{JX_hzpFyYWpR zJvU4t$LdtZHpIY_ec+{<6kBm;TIB2sXrD=L^??ZzLheF&+@KElQ7zoDAyr9MuE^Yy55yv&jyykMV z-)JOh>VR4PT&{_2t&;%(6-@xH7bcU#$m_kxA~!syDxtt=X>VUc?JzlJxXsBqyF{-L zS?5|nBM4M?(e$v+m;5md+fT#0oOAYx-!?1c43M^)f5R`WX5c6lV~HDVT5YLS$Y?V& zH2AJJShCEjkyM?t+CZxGz#n1CQ3M-Sn*AboA*|h$;EQn=EKBYoH8bFZjE@f$t1Z?R z`B_E+XWAJgg&g4?uV~Wpd;o9>ObpvGY7@h2SrF3tGN`=q__7HvesNfAVeXi8($hP# zACBz@uBxl;a@TC&-fyX!-OwMp6#2l?r+>5pk9AsQS`efN!+l|s^^(IP=-!Ms%OD4q zUO6=l!Nk${q^Gx!67FsR{-j2+ZMICLN2(o(TB8$p0jS!sIG`YiV~Rn&OWbV4RE=w7 zq+kSsWjSr7&uH+jD1mu;sQFv9K}F_Mg5L|DY*G}&sa89$fao@r$b_Mo4(UR9GRSM4 zbU~0TSdkXVB)7YJU!vO*;TvtZ_$170ZOomg{KbOxVx_9`TIe(J26&T#-w|x@(b2=% z->+z`P6BG{$M{Eg&))YMcD(^ZiLOna>ciP>CH0@~FeRT5zGf zvCv$cB1a2xm4hgzaBm0_ZK8hYC>$zi7*bZcvPBe^`>K1k?z>OvpSU0#R6CG@<5s<|HfZ6icl#*etHQ;;nG?@Jus3f^ z#`~2tY%PfAXo`M<+4!eG2OPk}?)m`cbExUB+y@W|hz#$$mQ`c7!sZ^Gx+x z%#NPx8aItewVZ4FaQtAZ#<(-I9qx{-oO=g-+;8RfOfQv3B4?N-b%=ZMcafdiyK4fB zR6y%hgKV7cy&?V2Yd+`yR`v_M2jPD>)7btJ=k1tO*}NM(AxksS{Ye`uvK{|o==8$@ zGJ;5EhS%CHga<5_e;p)Z(8A=xbx=z@3#4p`?eaLhfQmW}zHetthVfv;lMPyuhq{?g zJ&FuAG)B*)5MOpFqyiAsjnsJR1VcPx z?kerPiUh1bUh3%(yl*m0CM#z>_lcWc!{j(8T9#D_Kko1BGYr`Yiw!f8b|fftimINMcnh2xvi^G3HHb=v2cl3+ zTD@wb7eIa|*F>1oyVAZ>8B03UP@;Qlfk!LDvgCQdi4F+p^l{4%l-|j(%41|dbf$@) zGwM~VN9m3je*+QlvKGq7=zZDh`|>@@{3B9pLae3nHHxjk9XgWDHm9nKd`=k|Q zsP?Bx&midajO0xvz!leJ+&eUm6j0Cc8dI?_5iB3>XhlC3GW&zL0NR7E=T+_mOs|QI zsp)z{yoyx@u80O5$M|&Jv{?3l+AtPgwfo1_;~Qz?`xGdOOyt)-+odOoc<%YrP>MIM?=Grkdi->*#6Gf#6*FUX#37jXMwo@UAl*zHL8t z1KNzW059sHPVxG{Ln|vQaWAh{0mMr3a`r*vwm<~b1eE>SC ziLJc%07Aoi!IQgZZFKCG{bO$5-CY{P8!ytfe<3SYIINGlwd@Z<)4$)<`aRCNRsuB&eh0cw9wPP_^#bL)msds^F%^w zO~Df&^)~$VYGzRGhT)EEwmnspHj>`10*H~H>y3OE=N!C23^2t8)*|@h?VTYISo!$1 z&$WGngm`VEFY+{yeM(%Y7nOfyu;K7_PE!Gg_y7edS6vo3vuXT&0cFlkF@$o(4ah(o z-C%mQ`T^U;zJw2`e{7TusHbm5u_WqiC^IR)J&jf3%3&!n5hLH|R+aLZfANpfC08Fj z-Z2bGtY|~xaEe3S-}+f7fZk>GxXU{b7>!6X16Bj00tS{?BIC(peXtN!VCOb~P4;iE8QE4mTCJ%6LQf4~jIHL5 zJOKVoM#S09U;v|=9?>9trwU{m0?SHy0V(0>oBjSOVH)|pA_;bd2n@*ENsavNt3)ul zKf|jqkYx4~@!WI(+%8DBFFEmgEY=+P6UH$BA*sF6Z=vKzW->1pGyIty5Iz0>KnxUxPy#?s$DG-J69?r)Yg~u48_B1@KEUJGP!?{M%G;`{=We)3P|> zGGE%WnJH0F;3aWt6>%4R9QpDD6fHBTkEZ$lQX05FLab{&1zigYvRQd^26p9A#87$> zAbLlMFOMMCTk3HCTJTsX#*ocI)%D7pA=W|T|fCj+k7XtLu%?f7wF zh{`@bX+3WZ6TxOzu0P)f`cpm*J5UViub0UE^%9PPe|_jr8H)}m&Bk|ZM8qfJVgMJd zQGnk+cf5UEbkNs$e}5d*8~f(*&wrkJ^s`t2k2_W-PBi#GBqU4+IsYQ{FN(lcXO#%h zJdE7o#D50y3=xtgMUMFwDd1hXA=ucoAr~nuk2Z8T^6$s|8Q3?7{vn3=MD720)&G9R z{Q=`YCP%4&;eqsL$d5k~!4~dFz$!&3`bLaH=h646kzE9j_-(EPvA{KLIJ0%v?Q=V;a^M1OPp-vsyOn Prem in a Box

+``` + +## Operational considerations + +A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project. + +The VPN used to connect to the on-premises environment does not account for HA, upgrading to use HA VPN is reasonably simple by using the relevant [module](../../modules/net-vpn-ha). + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id for all resources. | string | ✓ | | +| *bgp_asn* | BGP ASNs. | map(number) | | ... | +| *bgp_interface_ranges* | BGP interface IP CIDR ranges. | map(string) | | ... | +| *ip_ranges* | IP CIDR ranges. | map(string) | | ... | +| *region* | VPC region. | string | | europe-west1 | +| *resolver_address* | GCP DNS resolver address for the inbound policy. | string | | 10.0.0.2 | +| *ssh_source_ranges* | IP CIDR ranges that will be allowed to connect via SSH to the onprem instance. | list(string) | | ["0.0.0.0/0"] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| foo | None | | +| onprem-instance | Onprem instance details. | | +| test-instance | Test instance details. | | + diff --git a/infrastructure/onprem-google-access-dns/assets/Corefile b/infrastructure/onprem-google-access-dns/assets/Corefile new file mode 100644 index 0000000000..e5b8432d16 --- /dev/null +++ b/infrastructure/onprem-google-access-dns/assets/Corefile @@ -0,0 +1,21 @@ +onprem.example.com { + root /etc/coredns + hosts onprem.hosts + log + errors +} +gcp.example.com googleapis.com { + forward . ${forwarder_address} + log + errors +} +google.internal { + hosts { + 169.254.169.254 metadata.google.internal + } +} +. { + forward . 8.8.8.8 + log + errors +} diff --git a/infrastructure/onprem-google-access-dns/diagram.png b/infrastructure/onprem-google-access-dns/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3073143d252e0cf2c3c805e813f52cf0996abf1f GIT binary patch literal 143289 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 +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | +| prefix | Prefix used for resources that need unique names. | string | ✓ | | +| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | +| *ip_ranges* | Subnet IP CIDR ranges. | map(string) | | ... | +| *ip_secondary_ranges* | Secondary IP CIDR ranges. | map(string) | | ... | +| *owners_gce* | GCE project owners, in IAM format. | list(string) | | [] | +| *owners_gke* | GKE project owners, in IAM format. | list(string) | | [] | +| *owners_host* | Host project owners, in IAM format. | list(string) | | [] | +| *private_service_ranges* | Private service IP CIDR ranges. | map(string) | | ... | +| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | +| *region* | Region used. | string | | europe-west1 | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| gke_clusters | GKE clusters information. | | +| projects | Project ids. | | +| service_accounts | GCE and GKE service accounts. | | +| vms | GCE VMs. | | +| vpc | Shared VPC. | | + diff --git a/infrastructure/shared-vpc/backend.tf.sample b/infrastructure/shared-vpc-gke/backend.tf.sample similarity index 100% rename from infrastructure/shared-vpc/backend.tf.sample rename to infrastructure/shared-vpc-gke/backend.tf.sample diff --git a/infrastructure/shared-vpc/diagram.gcpdraw b/infrastructure/shared-vpc-gke/diagram.gcpdraw similarity index 72% rename from infrastructure/shared-vpc/diagram.gcpdraw rename to infrastructure/shared-vpc-gke/diagram.gcpdraw index 9ed42b4f65..ff8291e67e 100644 --- a/infrastructure/shared-vpc/diagram.gcpdraw +++ b/infrastructure/shared-vpc-gke/diagram.gcpdraw @@ -7,16 +7,17 @@ elements { group host_services { name "Shared Services" background_color "#f6f6f6" - card dns - card kms + card dns { + name "Private zone" + } + card nat { + name "NAT" + } } group vpc_host { name "Shared VPC" background_color "#fff3e0" - card vpc as net_subnet { - name "Networking subnet" - } card vpc as gce_subnet { name "GCE subnet" } @@ -28,15 +29,15 @@ elements { group project_gce { name "GCE service project" - stacked_card gce as gce_instances { - name "VM instances" + card gce as gce_instances { + name "Bastion VM" } } group project_gke { name "GKE service project" - stacked_card gke as gke_clusters { - name "GKE clusters" + card gke as gke_clusters { + name "GKE cluster" } } @@ -46,4 +47,4 @@ elements { paths { gce_subnet ..> gce_instances gke_subnet ..> gke_clusters -} \ No newline at end of file +} diff --git a/infrastructure/shared-vpc-gke/diagram.png b/infrastructure/shared-vpc-gke/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc15e9b29e14d39312cf2738aa4135136a5b7e1 GIT binary patch literal 95353 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 literal 0 HcmV?d00001 diff --git a/infrastructure/shared-vpc-gke/main.tf b/infrastructure/shared-vpc-gke/main.tf new file mode 100644 index 0000000000..7c47ad812b --- /dev/null +++ b/infrastructure/shared-vpc-gke/main.tf @@ -0,0 +1,249 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################################################################### +# Host and service projects # +############################################################################### + +# the container.hostServiceAgentUser role is needed for GKE on shared VPC + +module "project-host" { + source = "../../modules/project" + parent = var.root_node + billing_account = var.billing_account_id + prefix = var.prefix + name = "net" + services = concat(var.project_services, ["dns.googleapis.com"]) + iam_roles = [ + "roles/container.hostServiceAgentUser", "roles/owner" + ] + iam_members = { + "roles/container.hostServiceAgentUser" = [ + "serviceAccount:${module.project-svc-gke.gke_service_account}" + ] + "roles/owner" = var.owners_host + } +} + +module "project-svc-gce" { + source = "../../modules/project" + parent = var.root_node + billing_account = var.billing_account_id + prefix = var.prefix + name = "gce" + services = var.project_services + oslogin = true + oslogin_admins = var.owners_gce + iam_roles = [ + "roles/logging.logWriter", + "roles/monitoring.metricWriter", + "roles/owner" + ] + iam_members = { + "roles/logging.logWriter" = [module.vm-bastion.service_account_iam_email], + "roles/monitoring.metricWriter" = [module.vm-bastion.service_account_iam_email], + "roles/owner" = var.owners_gce, + } +} + +# the container.developer role assigned to the bastion instance service account +# allows to fetch GKE credentials from bastion for clusters in this project + +module "project-svc-gke" { + source = "../../modules/project" + parent = var.root_node + billing_account = var.billing_account_id + prefix = var.prefix + name = "gke" + services = var.project_services + iam_roles = [ + "roles/container.developer", + "roles/owner", + ] + iam_members = { + "roles/owner" = var.owners_gke + "roles/container.developer" = [module.vm-bastion.service_account_iam_email] + } +} + +################################################################################ +# Networking # +################################################################################ + +# the service project GKE robot needs the `hostServiceAgent` role throughout +# the entire life of its clusters; the `iam_project_id` project output is used +# here to set the project id so that the VPC depends on that binding, and any +# cluster using it then also depends on it indirectly; you can of course use +# the `project_id` output instead if you don't care about destroying + +# subnet IAM bindings control which identities can use the individual subnets + +module "vpc-shared" { + source = "../../modules/net-vpc" + project_id = module.project-host.iam_project_id + name = "shared-vpc" + shared_vpc_host = true + shared_vpc_service_projects = [ + module.project-svc-gce.project_id, + module.project-svc-gke.project_id + ] + subnets = { + gce = { + ip_cidr_range = var.ip_ranges.gce + region = var.region + secondary_ip_range = {} + } + gke = { + ip_cidr_range = var.ip_ranges.gke + region = var.region + secondary_ip_range = { + pods = var.ip_secondary_ranges.gke-pods + services = var.ip_secondary_ranges.gke-services + } + } + } + iam_roles = { + gke = ["roles/compute.networkUser", "roles/compute.securityAdmin"] + gce = ["roles/compute.networkUser"] + } + iam_members = { + gce = { + "roles/compute.networkUser" = concat(var.owners_gce, [ + "serviceAccount:${module.project-svc-gce.cloudsvc_service_account}", + ]) + } + gke = { + "roles/compute.networkUser" = concat(var.owners_gke, [ + "serviceAccount:${module.project-svc-gke.cloudsvc_service_account}", + "serviceAccount:${module.project-svc-gke.gke_service_account}", + ]) + "roles/compute.securityAdmin" = [ + "serviceAccount:${module.project-svc-gke.gke_service_account}", + ] + } + } +} + +module "vpc-shared-firewall" { + source = "../../modules/net-vpc-firewall" + project_id = module.project-host.project_id + network = module.vpc-shared.name + admin_ranges_enabled = true + admin_ranges = values(var.ip_ranges) +} + +module "nat" { + source = "../../modules/net-cloudnat" + project_id = module.project-host.project_id + region = var.region + name = "vpc-shared" + router_create = true + router_network = module.vpc-shared.name +} + +################################################################################ +# DNS # +################################################################################ + +module "host-dns" { + source = "../../modules/dns" + project_id = module.project-host.project_id + type = "private" + name = "example" + domain = "example.com." + client_networks = [module.vpc-shared.self_link] + recordsets = [ + { name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] }, + { name = "bastion", type = "A", ttl = 300, records = module.vm-bastion.internal_ips }, + ] +} + +################################################################################ +# VM # +################################################################################ + +module "vm-bastion" { + source = "../../modules/compute-vm" + project_id = module.project-svc-gce.project_id + region = module.vpc-shared.subnet_regions.gce + zone = "${module.vpc-shared.subnet_regions.gce}-b" + name = "bastion" + network_interfaces = [{ + network = module.vpc-shared.self_link, + subnetwork = lookup(module.vpc-shared.subnet_self_links, "gce", null), + nat = false, + addresses = null + }] + instance_count = 1 + tags = ["ssh"] + metadata = { + startup-script = join("\n", [ + "#! /bin/bash", + "apt-get update", + "apt-get install -y bash-completion kubectl dnsutils" + ]) + } + service_account_create = true +} + +################################################################################ +# GKE # +################################################################################ + +module "cluster-1" { + source = "../../modules/gke-cluster" + name = "cluster-1" + project_id = module.project-svc-gke.project_id + location = "${module.vpc-shared.subnet_regions.gke}-b" + network = module.vpc-shared.self_link + subnetwork = module.vpc-shared.subnet_self_links.gke + secondary_range_pods = "pods" + secondary_range_services = "services" + default_max_pods_per_node = 32 + labels = { + environment = "test" + } + master_authorized_ranges = { + internal-vms = var.ip_ranges.gce + } + private_cluster_config = { + enable_private_nodes = true + enable_private_endpoint = true + master_ipv4_cidr_block = var.private_service_ranges.cluster-1 + } +} + +module "cluster-1-nodepool-1" { + source = "../../modules/gke-nodepool" + name = "nodepool-1" + project_id = module.project-svc-gke.project_id + location = module.cluster-1.location + cluster_name = module.cluster-1.name + node_config_service_account = module.service-account-gke-node.email +} + +# roles assigned via this module use non-authoritative IAM bindings at the +# project level, with no risk of conflicts with pre-existing roles + +module "service-account-gke-node" { + source = "../../modules/iam-service-accounts" + project_id = module.project-svc-gke.project_id + names = ["gke-node"] + iam_project_roles = { + (module.project-svc-gke.project_id) = [ + "roles/logging.logWriter", + "roles/monitoring.metricWriter", + ] + } +} diff --git a/infrastructure/shared-vpc-gke/outputs.tf b/infrastructure/shared-vpc-gke/outputs.tf new file mode 100644 index 0000000000..fccebe77ed --- /dev/null +++ b/infrastructure/shared-vpc-gke/outputs.tf @@ -0,0 +1,54 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +output "gke_clusters" { + description = "GKE clusters information." + value = { + cluster-1 = module.cluster-1.endpoint + } +} + +output "projects" { + description = "Project ids." + value = { + host = module.project-host.project_id + service-gce = module.project-svc-gce.project_id + service-gke = module.project-svc-gke.project_id + } +} + +output "service_accounts" { + description = "GCE and GKE service accounts." + value = { + bastion = module.vm-bastion.service_account_email + gke_node = module.service-account-gke-node.email + } +} + +output "vpc" { + description = "Shared VPC." + value = { + name = module.vpc-shared.name + subnets = module.vpc-shared.subnet_ips + } +} + +output "vms" { + description = "GCE VMs." + value = { + for instance in concat(module.vm-bastion.instances) : + instance.name => instance.network_interface.0.network_ip + } +} + diff --git a/infrastructure/shared-vpc-gke/variables.tf b/infrastructure/shared-vpc-gke/variables.tf new file mode 100644 index 0000000000..a69be5b6a7 --- /dev/null +++ b/infrastructure/shared-vpc-gke/variables.tf @@ -0,0 +1,87 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +variable "billing_account_id" { + description = "Billing account id used as default for new projects." + type = string +} + +variable "owners_gce" { + description = "GCE project owners, in IAM format." + type = list(string) + default = [] +} + +variable "owners_gke" { + description = "GKE project owners, in IAM format." + type = list(string) + default = [] +} + +variable "owners_host" { + description = "Host project owners, in IAM format." + type = list(string) + default = [] +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string +} + +variable "region" { + description = "Region used." + type = string + default = "europe-west1" +} + +variable "root_node" { + description = "Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'." + type = string +} + +variable "ip_ranges" { + description = "Subnet IP CIDR ranges." + type = map(string) + default = { + gce = "10.0.16.0/24" + gke = "10.0.32.0/24" + } +} + +variable "ip_secondary_ranges" { + description = "Secondary IP CIDR ranges." + type = map(string) + default = { + gke-pods = "10.128.0.0/18" + gke-services = "172.16.0.0/24" + } +} + +variable "private_service_ranges" { + description = "Private service IP CIDR ranges." + type = map(string) + default = { + cluster-1 = "192.168.0.0/28" + } +} + +variable "project_services" { + description = "Service APIs enabled by default in new projects." + type = list(string) + default = [ + "resourceviews.googleapis.com", + "stackdriver.googleapis.com", + ] +} diff --git a/foundations/business-units/modules/business-unit-folders/versions.tf b/infrastructure/shared-vpc-gke/versions.tf similarity index 100% rename from foundations/business-units/modules/business-unit-folders/versions.tf rename to infrastructure/shared-vpc-gke/versions.tf diff --git a/infrastructure/shared-vpc/.terraform.tfstate.lock.info b/infrastructure/shared-vpc/.terraform.tfstate.lock.info deleted file mode 100644 index b770415557..0000000000 --- a/infrastructure/shared-vpc/.terraform.tfstate.lock.info +++ /dev/null @@ -1 +0,0 @@ -{"ID":"0de052d8-e6ee-a792-20d2-9cafff96bd13","Operation":"OperationTypeApply","Info":"","Who":"ludomagno@lancre","Version":"0.12.16","Created":"2020-02-18T19:52:11.578559433Z","Path":"terraform.tfstate"} \ No newline at end of file diff --git a/infrastructure/shared-vpc/README.md b/infrastructure/shared-vpc/README.md deleted file mode 100644 index 9bec065a64..0000000000 --- a/infrastructure/shared-vpc/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Shared VPC sample - -This sample creates a basic [Shared VPC](https://cloud.google.com/vpc/docs/shared-vpc) infrastructure, where two service projects are connected to separate subnets, and the host project exposes Cloud DNS and Cloud KMS as centralized services. The service projects are slightly different, as they are meant to illustrate the IAM-level differences that need to be taken into account when sharing subnets for GCE or GKE. - -The purpose of this sample is showing how to wire different [Cloud Foundation Fabric](https://github.com/search?q=topic%3Acft-fabric+org%3Aterraform-google-modules&type=Repositories) modules to create Shared VPC infrastructures, and as such it is meant to be used for prototyping, or to experiment with networking configurations. Additional best practices and security considerations need to be taken into account for real world usage (eg removal of default service accounts, disabling of external IPs, firewall design, etc). - -![High-level diagram](diagram.png "High-level diagram") - -## Managed resources and services - -This sample creates several distinct groups of resources: - -- three projects (Shared VPC host and two service projects) -- VPC-level resources (VPC, subnets, firewall rules, etc.) in the host project -- one internal Cloud DNS zone in the host project -- one Cloud KMS keyring with one key in the host project -- IAM roles to wire all the above resource together -- one test instance in each project, with their associated DNS records - -## Test resources - -A set of test resources are included for convenience, as they facilitate experimenting with different networking configurations (firewall rules, external connectivity via VPN, etc.). They are encapsulated in the `test-resources.tf` file, and can be safely removed as a single unit. - -SSH access to instances is configured via [OS Login](https://cloud.google.com/compute/docs/oslogin/), except for the GKE project instance since [GKE nodes do not support OS Login](https://cloud.google.com/compute/docs/instances/managing-instance-access#limitations). To access the GKe instance, use a SSH key set at the project or instance level. External access is allowed via the default SSH rule created by the firewall module, and corresponding `ssh` tags on the instances. - -The GCE instance is somewhat special, as it's configured to run a containerized MySQL server using the [`cos-mysql` module](https://github.com/terraform-google-modules/terraform-google-container-vm/tree/master/modules/cos-mysql), to show a practical example of using this module with KMS encryption for its secret, and to demonstrate how to define a custom firewall rule in the firewall module. - -The networking and GKE instances have `dig` and the `mysql` client installed via startup scripts, so that tests can be run as soon as they are created. - -## Destroying - -There's a minor glitch that can surface running `terraform destroy`, with a simple workaround. The glitch is due to a delay between the API reporting service project removal from the Shared VPC as successful (`google_compute_shared_vpc_service_project` resources destroyed), and the Shared VPC resource being aligned with that event. This results in an error that prevents disabling the Shared VPC feature: `Error disabling Shared VPC Host [...] Cannot disable project as a shared VPC host because it has active service projects.`. The workaround is to run `terraform destroy` again after a few seconds, giving the Shared VPC resource time to be in sync with service project removal. - - -## Variables - -| name | description | type | required | default | -|---|---|:---: |:---:|:---:| -| billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| prefix | Prefix used for resources that need unique names. | string | ✓ | | -| root_node | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *kms_keyring_location* | Location used for the KMS keyring. | string | | europe | -| *kms_keyring_name* | Name used for the KMS keyring. | string | | svpc-example | -| *oslogin_admins_gce* | GCE project oslogin admin members, in IAM format. | list(string) | | [] | -| *oslogin_users_gce* | GCE project oslogin user members, in IAM format. | list(string) | | [] | -| *owners_gce* | GCE project owners, in IAM format. | list(string) | | [] | -| *owners_gke* | GKE project owners, in IAM format. | list(string) | | [] | -| *owners_host* | Host project owners, in IAM format. | list(string) | | [] | -| *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *subnet_secondary_ranges* | Shared VPC subnets secondary range definitions. | map(list(object({...}))) | | ... | -| *subnets* | Shared VPC subnet definitions. | list(object({...})) | | ... | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| host_project_id | VPC host project id. | | -| service_project_ids | Service project ids. | | -| vpc_name | Shared VPC name | | -| vpc_subnets | Shared VPC subnets. | | - diff --git a/infrastructure/shared-vpc/diagram.png b/infrastructure/shared-vpc/diagram.png deleted file mode 100644 index 0386795405b27e51d552ba32ca64a38ac56334dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159686 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

0a3@iO2uc%wCm6gZCAQCRU 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 diff --git a/infrastructure/shared-vpc/locals.tf b/infrastructure/shared-vpc/locals.tf deleted file mode 100644 index bf46122498..0000000000 --- a/infrastructure/shared-vpc/locals.tf +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -locals { - # GCE service project users that need the network user role assigned on host - net_gce_users = concat( - var.owners_gce, - ["serviceAccount:${module.project-service-gce.cloudsvc_service_account}"] - ) - # GKE service project users that need the network user role assigned on host - net_gke_users = concat( - var.owners_gke, - [ - "serviceAccount:${module.project-service-gke.gke_service_account}", - "serviceAccount:${module.project-service-gke.cloudsvc_service_account}" - ] - ) - # GKE subnet primary and secondary ranges, used in firewall rules - # use lookup to prevent failure on successive destroys - net_gke_ip_ranges = compact([ - lookup(local.net_subnet_ips, "gke", ""), - element([ - for range in lookup(var.subnet_secondary_ranges, "gke", []) : - range.ip_cidr_range if range.range_name == "pods" - ], 0) - ]) - # map of subnet names => addresses - net_subnet_ips = zipmap( - module.net-vpc-host.subnets_names, - module.net-vpc-host.subnets_ips - ) - # map of subnet names => links - net_subnet_links = zipmap( - module.net-vpc-host.subnets_names, - module.net-vpc-host.subnets_self_links - ) - # map of subnet names => regions - net_subnet_regions = zipmap( - module.net-vpc-host.subnets_names, - module.net-vpc-host.subnets_regions - ) - # use svpc access module outputs to create an implicit dependency on service project registration - service_projects = zipmap( - module.net-svpc-access.service_projects, - module.net-svpc-access.service_projects - ) -} diff --git a/infrastructure/shared-vpc/main.tf b/infrastructure/shared-vpc/main.tf deleted file mode 100644 index 8b55e9f036..0000000000 --- a/infrastructure/shared-vpc/main.tf +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -############################################################################### -# Host and service projects # -############################################################################### - -# host project - -module "project-svpc-host" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "7.0.0" - parent = var.root_node - prefix = var.prefix - name = "vpc-host" - billing_account = var.billing_account_id - owners = var.owners_host - activate_apis = concat( - var.project_services, - ["dns.googleapis.com", "cloudkms.googleapis.com"] - ) -} - -# service projects - -module "project-service-gce" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "7.0.0" - parent = var.root_node - prefix = var.prefix - name = "gce" - billing_account = var.billing_account_id - oslogin = "true" - owners = var.owners_gce - oslogin_admins = var.oslogin_admins_gce - oslogin_users = var.oslogin_users_gce - activate_apis = var.project_services -} - -module "project-service-gke" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "7.0.0" - parent = var.root_node - prefix = var.prefix - name = "gke" - billing_account = var.billing_account_id - owners = var.owners_gke - activate_apis = var.project_services -} - -################################################################################ -# Networking # -################################################################################ - -# Shared VPC - -module "net-vpc-host" { - source = "terraform-google-modules/network/google" - version = "2.1.1" - project_id = module.project-svpc-host.project_id - network_name = "vpc-shared" - shared_vpc_host = true - subnets = var.subnets - secondary_ranges = var.subnet_secondary_ranges - routes = [] -} - -# Shared VPC firewall - -module "net-vpc-firewall" { - source = "terraform-google-modules/network/google//modules/fabric-net-firewall" - version = "2.1.1" - project_id = module.project-svpc-host.project_id - network = module.net-vpc-host.network_name - admin_ranges_enabled = true - admin_ranges = compact([lookup(local.net_subnet_ips, "networking", "")]) - custom_rules = { - ingress-mysql = { - description = "Allow incoming connections on the MySQL port from GKE addresses." - direction = "INGRESS" - action = "allow" - ranges = local.net_gke_ip_ranges - sources = [] - targets = ["mysql"] - use_service_accounts = false - rules = [{ protocol = "tcp", ports = [3306] }] - extra_attributes = {} - } - } -} - -# Shared VPC access - -module "net-svpc-access" { - source = "terraform-google-modules/network/google//modules/fabric-net-svpc-access" - version = "2.1.1" - host_project_id = module.project-svpc-host.project_id - service_project_num = 2 - service_project_ids = [ - module.project-service-gce.project_id, - module.project-service-gke.project_id - ] - host_subnets = ["gce", "gke"] - host_subnet_regions = compact([ - lookup(local.net_subnet_regions, "gce", ""), - lookup(local.net_subnet_regions, "gke", "") - ]) - host_subnet_users = { - gce = join(",", local.net_gce_users) - gke = join(",", local.net_gke_users) - } - host_service_agent_role = true - host_service_agent_users = [ - "serviceAccount:${module.project-service-gke.gke_service_account}" - ] -} - -################################################################################ -# DNS # -################################################################################ - -module "host-dns" { - source = "terraform-google-modules/cloud-dns/google" - version = "3.0.0" - project_id = module.project-svpc-host.project_id - type = "private" - name = "svpc-fabric-example" - domain = "svpc.fabric." - private_visibility_config_networks = [module.net-vpc-host.network_self_link] - recordsets = [ - { name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] } - ] -} - -################################################################################ -# KMS # -################################################################################ - -module "host-kms" { - source = "terraform-google-modules/kms/google" - version = "1.1.0" - project_id = module.project-svpc-host.project_id - location = var.kms_keyring_location - keyring = var.kms_keyring_name - keys = ["mysql"] - set_decrypters_for = ["mysql"] - decrypters = ["serviceAccount:${module.project-service-gce.gce_service_account}"] - prevent_destroy = false -} diff --git a/infrastructure/shared-vpc/outputs.tf b/infrastructure/shared-vpc/outputs.tf deleted file mode 100644 index c7e021d073..0000000000 --- a/infrastructure/shared-vpc/outputs.tf +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -output "vpc_name" { - description = "Shared VPC name" - value = module.net-vpc-host.network_name -} - -output "vpc_subnets" { - description = "Shared VPC subnets." - value = local.net_subnet_ips -} - -output "host_project_id" { - description = "VPC host project id." - value = module.project-svpc-host.project_id -} - -output "service_project_ids" { - description = "Service project ids." - value = { - gce = module.project-service-gce.project_id - gke = module.project-service-gke.project_id - } -} diff --git a/infrastructure/shared-vpc/test-resources.tf b/infrastructure/shared-vpc/test-resources.tf deleted file mode 100644 index b24ce58c77..0000000000 --- a/infrastructure/shared-vpc/test-resources.tf +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -############################################################################### -# host test VM and DNS record # -############################################################################### - -resource "google_compute_instance" "test-net" { - project = module.project-svpc-host.project_id - name = "test-net" - machine_type = "f1-micro" - zone = "${local.net_subnet_regions.networking}-b" - tags = ["ssh"] - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - network_interface { - network = module.net-vpc-host.network_self_link - subnetwork = local.net_subnet_links.networking - access_config {} - } - metadata_startup_script = "apt update && apt install -y dnsutils mysql-client" -} - -resource "google_dns_record_set" "test_net" { - project = module.project-svpc-host.project_id - name = "test-net.${module.host-dns.domain}" - type = "A" - ttl = 300 - managed_zone = module.host-dns.name - rrdatas = [ - google_compute_instance.test-net.network_interface.0.network_ip - ] -} - -############################################################################### -# GKE project test VM and DNS record # -############################################################################### - -resource "google_compute_instance" "test-gke" { - depends_on = [module.net-svpc-access] - project = module.project-service-gke.project_id - name = "test-gke" - machine_type = "f1-micro" - zone = "${local.net_subnet_regions.gke}-b" - tags = ["ssh"] - boot_disk { - initialize_params { - image = "debian-cloud/debian-9" - } - } - network_interface { - network = module.net-vpc-host.network_self_link - subnetwork = local.net_subnet_links.gke - access_config {} - } - metadata_startup_script = "apt update && apt install -y dnsutils mysql-client" -} - -resource "google_dns_record_set" "test_gke" { - project = module.project-svpc-host.project_id - name = "test-gke.${module.host-dns.domain}" - type = "A" - ttl = 300 - managed_zone = module.host-dns.name - rrdatas = [ - google_compute_instance.test-gke.network_interface.0.network_ip - ] -} - -############################################################################### -# GCE project MySQL test VM and DNS record # -############################################################################### - -# random password for MySQL - -resource "random_pet" "mysql_password" {} - -# MySQL password encrypted via KMS key - -data "google_kms_secret_ciphertext" "mysql_password" { - crypto_key = module.host-kms.keys.mysql - plaintext = random_pet.mysql_password.id -} - -# work around the encrypted password always refreshing, taint to refresh - -resource "null_resource" "mysql_password" { - triggers = { - ciphertext = data.google_kms_secret_ciphertext.mysql_password.ciphertext - } - lifecycle { - ignore_changes = [triggers] - } -} - -# MySQL container on Container Optimized OS - -module "container-vm_cos-mysql" { - source = "terraform-google-modules/container-vm/google//modules/cos-mysql" - version = "1.0.4" - project_id = lookup(local.service_projects, module.project-service-gce.project_id, "") - region = "${lookup(local.net_subnet_regions, "gce", "")}" - zone = "${lookup(local.net_subnet_regions, "gce", "")}-b" - network = module.net-vpc-host.network_self_link - subnetwork = lookup(local.net_subnet_links, "gke", "") - instance_count = "1" - data_disk_size = "10" - vm_tags = ["ssh", "mysql"] - password = null_resource.mysql_password.triggers.ciphertext - # TODO(ludomagno): add a location output to the keyring module - kms_data = { - key = "mysql" - keyring = module.host-kms.keyring_name - location = var.kms_keyring_location - project_id = module.project-svpc-host.project_id - } -} - -resource "google_dns_record_set" "mysql" { - project = module.project-svpc-host.project_id - name = "mysql.${module.host-dns.domain}" - type = "A" - ttl = 300 - managed_zone = module.host-dns.name - rrdatas = [ - values(module.container-vm_cos-mysql.instances)[0] - ] -} - -############################################################################### -# test outputs # -############################################################################### - -output "test-instances" { - description = "Test instance names." - value = { - gke = map( - google_compute_instance.test-gke.name, - google_compute_instance.test-gke.network_interface.0.network_ip - ) - mysql = module.container-vm_cos-mysql.instances - networking = map( - google_compute_instance.test-net.name, - google_compute_instance.test-net.network_interface.0.network_ip - ) - } -} - -output "mysql-root-password" { - description = "Password for the test MySQL db root user." - sensitive = true - value = random_pet.mysql_password.id -} diff --git a/infrastructure/shared-vpc/variables.tf b/infrastructure/shared-vpc/variables.tf deleted file mode 100644 index 0f1f03fa30..0000000000 --- a/infrastructure/shared-vpc/variables.tf +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -variable "billing_account_id" { - description = "Billing account id used as default for new projects." - type = string -} - -variable "kms_keyring_location" { - description = "Location used for the KMS keyring." - type = string - default = "europe" -} - -variable "kms_keyring_name" { - description = "Name used for the KMS keyring." - type = string - default = "svpc-example" -} - -variable "oslogin_admins_gce" { - description = "GCE project oslogin admin members, in IAM format." - type = list(string) - default = [] -} - -variable "oslogin_users_gce" { - description = "GCE project oslogin user members, in IAM format." - type = list(string) - default = [] -} - -variable "owners_gce" { - description = "GCE project owners, in IAM format." - type = list(string) - default = [] -} - -variable "owners_gke" { - description = "GKE project owners, in IAM format." - type = list(string) - default = [] -} - -variable "owners_host" { - description = "Host project owners, in IAM format." - type = list(string) - default = [] -} - -variable "prefix" { - description = "Prefix used for resources that need unique names." - type = string -} - -variable "root_node" { - description = "Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'." - type = string -} - -variable "subnets" { - description = "Shared VPC subnet definitions." - type = list(object({ - subnet_name = string - subnet_ip = string - subnet_region = string - subnet_private_access = string - })) - default = [ - { - subnet_name = "networking" - subnet_ip = "10.0.0.0/24" - subnet_region = "europe-west1" - subnet_private_access = "true" - }, - { - subnet_name = "gce" - subnet_ip = "10.0.16.0/24" - subnet_region = "europe-west1" - subnet_private_access = "true" - }, - { - subnet_name = "gke" - subnet_ip = "10.0.32.0/24" - subnet_region = "europe-west1" - subnet_private_access = "true" - }, - ] -} - -variable "subnet_secondary_ranges" { - description = "Shared VPC subnets secondary range definitions." - type = map(list(object({ - range_name = string - ip_cidr_range = string - }))) - default = { - networking = [], - gce = [], - gke = [ - { - range_name = "services" - ip_cidr_range = "172.16.0.0/24" - }, - { - range_name = "pods" - ip_cidr_range = "10.128.0.0/18" - } - ] - } -} - -variable "project_services" { - description = "Service APIs enabled by default in new projects." - type = list(string) - default = [ - "resourceviews.googleapis.com", - "stackdriver.googleapis.com", - ] -} diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000000..b991613686 --- /dev/null +++ b/modules/README.md @@ -0,0 +1,47 @@ +# Terraform modules suite for Google Cloud + +The modules collected in this folder are designed as a suite: they are meant to be composed together, and are designed to be forked and modified where use of third party code and sources is not allowed. + +Modules try to stay close to the low level provider resources they encapsulate, and they all share a similar interface that combines management of one resource or set or resources, and the corresponding IAM bindings. + +Authoritative IAM bindings are primarily used (e.g. `google_storage_bucket_iam_binding` for GCS buckets) so that each module is authoritative for specific roles on the resources it manages, and can neutralize or reconcile IAM changes made elsewhere. + +Specific modules also offer support for non-authoritative bindings (e.g. `google_storage_bucket_iam_member` for service accounts), to allow granular permission management on resources that they don't manage directly. + +## Foundational modules + +- [folders](./modules/folders) +- [log sinks](./modules/logging-sinks) +- [project](./modules/project) +- [service accounts](./modules/iam-service-accounts) +- [ ] TODO: organization policies module + +## Networking modules + +- [address reservation](./modules/net-address) +- [Cloud DNS](./modules/dns) +- [Cloud NAT](./modules/net-cloudnat) +- [VPC](./modules/net-vpc) +- [VPC firewall](./modules/net-vpc-firewall) +- [VPC peering](./modules/net-vpc-peering) +- [VPN static](./modules/net-vpn-static) +- [VPN dynamic](./modules/net-vpn-dynamic) +- [VPN HA](./modules/net-vpn-ha)) +- [ ] TODO: xLB modules + +## Compute/Container + +- [COS container](./modules/compute-vm-cos-coredns) +- [GKE cluster](./modules/gke-cluster) +- [GKE nodepool](./modules/gke-nodepool) +- [VM/VM group](./modules/compute-vm) + +## Data + +- [BigQuery dataset](./modules/bigquery) +- [GCS](./modules/gcs) + +## Other + +- [Cloud KMS](./modules/kms) +- [on-premises in Docker](./modules/on-prem-in-a-box) diff --git a/modules/__experimental/cloud-function-scheduled/README.md b/modules/__experimental/cloud-function-scheduled/README.md new file mode 100644 index 0000000000..df1edce00d --- /dev/null +++ b/modules/__experimental/cloud-function-scheduled/README.md @@ -0,0 +1,42 @@ +# Scheduled Google Cloud Function Module + +This module manages a background Cloud Function scheduled via a recurring Cloud Scheduler job. It also manages the required dependencies: a service account for the cloud function with optional IAM bindings, the PubSub topic used for the function trigger, and optionally the GCS bucket used for the code bundle. + +## Example + +```hcl +module "function" { + source = "./modules/cloud-function-scheduled" + project_id = "myproject" + name = "myfunction" + bundle_config = { + source_dir = "../cf" + output_path = "../bundle.zip" + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| bundle_config | Cloud function code bundle configuration, output path is a zip file. | object({...}) | ✓ | | +| name | Name used for resources (schedule, topic, etc.). | string | ✓ | | +| project_id | Project id used for all resources. | string | ✓ | | +| *bucket_name* | Name of the bucket that will be used for the function code, leave null to create one. | string | | null | +| *function_config* | Cloud function configuration. | object({...}) | | ... | +| *prefixes* | Optional prefixes for resource ids, null prefixes will be ignored. | object({...}) | | null | +| *region* | Region used for all resources. | string | | us-central1 | +| *schedule_config* | Cloud function scheduler job configuration, leave data null to pass the name variable, set schedule to null to disable schedule. | object({...}) | | ... | +| *service_account_iam_roles* | IAM roles assigned to the service account at the project level. | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| bucket_name | Bucket name. | | +| function_name | Cloud function name. | | +| service_account_email | Service account email. | | +| topic_id | PubSub topic id. | | + diff --git a/modules/__experimental/cloud-function-scheduled/main.tf b/modules/__experimental/cloud-function-scheduled/main.tf new file mode 100644 index 0000000000..0d3f03ff5d --- /dev/null +++ b/modules/__experimental/cloud-function-scheduled/main.tf @@ -0,0 +1,133 @@ +/** + * Copyright 2020 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 { + bucket = ( + var.bucket_name != null + ? var.bucket_name + : google_storage_bucket.bucket[0].name + ) + job_data = ( + var.schedule_config.pubsub_data == null || var.schedule_config.pubsub_data == "" + ? var.name + : var.schedule_config.pubsub_data + ) + prefixes = ( + var.prefixes == null + ? {} + : { + for k, v in var.prefixes : + k => v != null && v != "" ? "${v}-${var.name}" : var.name + } + ) + service_account = "serviceAccount:${google_service_account.service_account.email}" +} + +############################################################################### +# Scheduler / PubSub # +############################################################################### + +resource "google_pubsub_topic" "topic" { + project = var.project_id + name = lookup(local.prefixes, "topic", var.name) +} + +resource "google_cloud_scheduler_job" "job" { + count = var.schedule_config.schedule == null ? 0 : 1 + project = var.project_id + region = var.region + name = lookup(local.prefixes, "job", var.name) + schedule = var.schedule_config.schedule + time_zone = var.schedule_config.time_zone + + pubsub_target { + attributes = {} + topic_name = google_pubsub_topic.topic.id + data = base64encode(local.job_data) + } +} + +############################################################################### +# Cloud Function service account and IAM # +############################################################################### + +resource "google_service_account" "service_account" { + project = var.project_id + account_id = lookup(local.prefixes, "service_account", var.name) + display_name = "Terraform-managed" +} + +resource "google_project_iam_member" "service_account" { + for_each = toset(var.service_account_iam_roles) + project = var.project_id + role = each.value + member = local.service_account +} + +############################################################################### +# Cloud Function and GCS code bundle # +############################################################################### + +resource "google_cloudfunctions_function" "function" { + project = var.project_id + region = var.region + name = lookup(local.prefixes, "function", var.name) + description = "Terraform managed." + runtime = var.function_config.runtime + available_memory_mb = var.function_config.memory + max_instances = var.function_config.instances + timeout = var.function_config.timeout + entry_point = var.function_config.entry_point + service_account_email = google_service_account.service_account.email + + # source_repository { + # url = var.source_repository_url + # } + + event_trigger { + event_type = "providers/cloud.pubsub/eventTypes/topic.publish" + resource = google_pubsub_topic.topic.id + } + + source_archive_bucket = local.bucket + source_archive_object = google_storage_bucket_object.bundle.name +} + +resource "google_storage_bucket" "bucket" { + count = var.bucket_name == null ? 1 : 0 + project = var.project_id + name = lookup(local.prefixes, "bucket", var.name) + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = "30" + } + } +} + +resource "google_storage_bucket_object" "bundle" { + name = "bundle-${data.archive_file.bundle.output_md5}.zip" + bucket = local.bucket + source = data.archive_file.bundle.output_path +} + +data "archive_file" "bundle" { + type = "zip" + source_dir = var.bundle_config.source_dir + output_path = var.bundle_config.output_path +} diff --git a/modules/__experimental/cloud-function-scheduled/outputs.tf b/modules/__experimental/cloud-function-scheduled/outputs.tf new file mode 100644 index 0000000000..2d47bd09c4 --- /dev/null +++ b/modules/__experimental/cloud-function-scheduled/outputs.tf @@ -0,0 +1,35 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "bucket_name" { + description = "Bucket name." + value = local.bucket +} + +output "function_name" { + description = "Cloud function name." + value = google_cloudfunctions_function.function.name +} + +output "service_account_email" { + description = "Service account email." + value = google_service_account.service_account.email +} + +output "topic_id" { + description = "PubSub topic id." + value = google_pubsub_topic.topic.id +} diff --git a/modules/__experimental/cloud-function-scheduled/variables.tf b/modules/__experimental/cloud-function-scheduled/variables.tf new file mode 100644 index 0000000000..78bb4b8cac --- /dev/null +++ b/modules/__experimental/cloud-function-scheduled/variables.tf @@ -0,0 +1,100 @@ +/** + * Copyright 2020 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 "bucket_name" { + description = "Name of the bucket that will be used for the function code, leave null to create one." + type = string + default = null +} + +variable "bundle_config" { + description = "Cloud function code bundle configuration, output path is a zip file." + type = object({ + source_dir = string + output_path = string + }) +} + +variable "function_config" { + description = "Cloud function configuration." + type = object({ + entry_point = string + instances = number + memory = number + runtime = string + timeout = number + }) + default = { + entry_point = "main" + instances = 1 + memory = 256 + runtime = "python37" + timeout = 180 + } +} + +variable "name" { + description = "Name used for resources (schedule, topic, etc.)." + type = string +} + +variable "prefixes" { + description = "Optional prefixes for resource ids, null prefixes will be ignored." + type = object({ + bucket = string + function = string + job = string + service_account = string + topic = string + }) + default = null +} + +variable "project_id" { + description = "Project id used for all resources." + type = string +} + +variable "region" { + description = "Region used for all resources." + type = string + default = "us-central1" +} + +variable "schedule_config" { + description = "Cloud function scheduler job configuration, leave data null to pass the name variable, set schedule to null to disable schedule." + type = object({ + pubsub_data = string + schedule = string + time_zone = string + }) + default = { + schedule = "*/10 * * * *" + pubsub_data = null + time_zone = "UTC" + } +} + +variable "service_account_iam_roles" { + description = "IAM roles assigned to the service account at the project level." + type = list(string) + default = [] +} + +# variable "source_repository_url" { +# type = string +# default = "" +# } diff --git a/modules/__experimental/cloud-function-scheduled/versions.tf b/modules/__experimental/cloud-function-scheduled/versions.tf new file mode 100644 index 0000000000..bc4c2a9d71 --- /dev/null +++ b/modules/__experimental/cloud-function-scheduled/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/bigquery/README.md b/modules/bigquery/README.md new file mode 100644 index 0000000000..b894f1ae0f --- /dev/null +++ b/modules/bigquery/README.md @@ -0,0 +1,69 @@ +# Google Cloud Bigquery Module + +Simple Bigquery module offering support for multiple dataset creation and access configuration. + +The module interface is designed to allow setting default values for dataset access configurations and options (eg table or partition expiration), and optionally override them for individual datasets. Common labels applied to all datasets can also be specified with a single variable, and overridden individually. + +Access configuration supports specifying different [identity types](https://www.terraform.io/docs/providers/google/r/bigquery_dataset.html#access) via the `identity_type` attribute in access variables. The supported identity types are: `domain`, `group_by_email`, `special_group` (eg `projectOwners`), `user_by_email`. + +## Example + +```hcl +module "bigquery-datasets" { + source = "./modules/bigquery" + project_id = "my-project + datasets = { + dataset_1 = { + name = "Dataset 1." + description = "Terraform managed." + location = "EU" + labels = null + }, + dataset_2 = { + name = "Dataset 2." + description = "Terraform managed." + location = "EU" + labels = null + }, + } + default_access = [ + { + role = "OWNER" + identity_type = "special_group" + identity = "projectOwners" + } + ] + default_labels = { + eggs = "spam", + bar = "baz + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| datasets | Map of datasets to create keyed by id. Labels and options can be null. | map(object({...})) | ✓ | | +| project_id | Id of the project where datasets will be created. | string | ✓ | | +| *dataset_access* | Optional map of dataset access rules by dataset id. | map(list(object({...}))) | | {} | +| *dataset_options* | Optional map of dataset option by dataset id. | map(object({...})) | | {} | +| *default_access* | Access rules applied to all dataset if no specific ones are defined. | list(object({...})) | | [] | +| *default_labels* | Labels set on all datasets. | map(string) | | {} | +| *default_options* | Options used for all dataset if no specific ones are defined. | object({...}) | | ... | +| *kms_key* | Self link of the KMS key that will be used to protect destination table. | string | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| datasets | Dataset resources. | | +| ids | Dataset ids. | | +| names | Dataset names. | | +| self_links | Dataset self links. | | + + +## TODO + +- [ ] add support for tables diff --git a/modules/bigquery/main.tf b/modules/bigquery/main.tf new file mode 100644 index 0000000000..9f42c231db --- /dev/null +++ b/modules/bigquery/main.tf @@ -0,0 +1,67 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + datasets = { + for id, data in var.datasets : + id => merge(data, { + options = lookup(var.dataset_options, id, var.default_options) + access = lookup(var.dataset_access, id, var.default_access) + labels = data.labels == null ? {} : data.labels + }) + } +} + +resource "google_bigquery_dataset" "datasets" { + for_each = local.datasets + project = var.project_id + dataset_id = each.key + friendly_name = each.value.name + description = each.value.description + labels = merge(var.default_labels, each.value.labels) + location = each.value.location + + delete_contents_on_destroy = each.value.options.delete_contents_on_destroy + default_table_expiration_ms = each.value.options.default_table_expiration_ms + default_partition_expiration_ms = each.value.options.default_partition_expiration_ms + + dynamic access { + for_each = each.value.access + iterator = config + content { + role = config.value.role + domain = each.value.identity_type == "domain" ? each.value.identity : null + group_by_email = each.value.identity_type == "group_by_email" ? each.value.identity : null + special_group = each.value.identity_type == "special_group" ? each.value.identity : null + user_by_email = each.value.identity_type == "user_by_email" ? each.value.identity : null + dynamic view { + for_each = each.value.identity_type == "view" ? [""] : [] + content { + project_id = view.value.project_id + dataset_id = view.value.dataset_id + table_id = view.value.table_id + } + } + } + } + + dynamic default_encryption_configuration { + for_each = var.kms_key == null ? [] : [""] + content { + kms_key_name = var.kms_key + } + } +} diff --git a/modules/bigquery/outputs.tf b/modules/bigquery/outputs.tf new file mode 100644 index 0000000000..bffb0b39a2 --- /dev/null +++ b/modules/bigquery/outputs.tf @@ -0,0 +1,47 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "datasets" { + description = "Dataset resources." + value = [ + for _, resource in google_bigquery_dataset.datasets : + resource + ] +} + +output "ids" { + description = "Dataset ids." + value = [ + for _, resource in google_bigquery_dataset.datasets : + resource.id + ] +} + +output "names" { + description = "Dataset names." + value = [ + for _, resource in google_bigquery_dataset.datasets : + resource.friendly_name + ] +} + +output "self_links" { + description = "Dataset self links." + value = [ + for _, resource in google_bigquery_dataset.datasets : + resource.self_link + ] +} diff --git a/modules/bigquery/variables.tf b/modules/bigquery/variables.tf new file mode 100644 index 0000000000..ad9ae4ae23 --- /dev/null +++ b/modules/bigquery/variables.tf @@ -0,0 +1,86 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "datasets" { + description = "Map of datasets to create keyed by id. Labels and options can be null." + type = map(object({ + description = string + location = string + name = string + labels = map(string) + })) +} + +variable "dataset_access" { + description = "Optional map of dataset access rules by dataset id." + type = map(list(object({ + role = string + identity_type = string + identity = any + }))) + default = {} +} + +variable "dataset_options" { + description = "Optional map of dataset option by dataset id." + type = map(object({ + default_table_expiration_ms = number + default_partition_expiration_ms = number + delete_contents_on_destroy = bool + })) + default = {} +} + +variable "default_access" { + description = "Access rules applied to all dataset if no specific ones are defined." + type = list(object({ + role = string + identity_type = string + identity = any + })) + default = [] +} + +variable "default_labels" { + description = "Labels set on all datasets." + type = map(string) + default = {} +} + +variable "default_options" { + description = "Options used for all dataset if no specific ones are defined." + type = object({ + default_table_expiration_ms = number + default_partition_expiration_ms = number + delete_contents_on_destroy = bool + }) + default = { + default_table_expiration_ms = null + default_partition_expiration_ms = null + delete_contents_on_destroy = false + } +} + +variable "kms_key" { + description = "Self link of the KMS key that will be used to protect destination table." + type = string + default = null +} + +variable "project_id" { + description = "Id of the project where datasets will be created." + type = string +} diff --git a/modules/bigquery/versions.tf b/modules/bigquery/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/bigquery/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md new file mode 100644 index 0000000000..e07680922c --- /dev/null +++ b/modules/compute-vm/README.md @@ -0,0 +1,113 @@ +# Google Compute Engine VM module + +This module allows creating one or multiple instances or an instance template for a specific configuration. A service account is optionally created and assigned if not specified. + +## TODO + +- [ ] add examples for instance groups + +## Examples + +### Instance leveraging defaults + +The simplest example leverages defaults for the boot disk image and size, and uses a service account created by the module. Multiple instances can be managed via the `instance_count` variable. + +```hcl +module "simple-vm-example" { + source = "../modules/compute-vm" + project_id = "my-project" + region = "europe-west1" + zone = "europe-west1-b" + name = "test" + network_interfaces = [{ + network = local.network_self_link, + subnetwork = local.subnet_self_link, + nat = false, + addresses = null + }] + service_account_create = true + instance_count = 1 +} +``` + +### Instance template + +This example shows how to use the module to manage an instance template that defines an additional attached disk for each instance, and overrides defaults for the boot disk image and service account. + +```hcl +module "debian-test" { + source = "../modules/compute-vm" + project_id = "my-project" + region = "europe-west1" + zone = "europe-west1-b" + name = "test" + network_interfaces = [{ + network = local.network_self_link, + subnetwork = local.subnet_self_link, + nat = false, + addresses = null + }] + instance_count = 1 + boot_disk = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } + attached_disks = [ + { name = "disk-1", size = 10, image = null, options = null } + ] + service_account = "vm-default@my-project.iam.gserviceaccount.com" + use_instance_template = true +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | Instances base name. | string | ✓ | | +| network_interfaces | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({...})) | ✓ | | +| project_id | Project id. | string | ✓ | | +| region | Compute region. | string | ✓ | | +| zone | Compute zone. | string | ✓ | | +| *attached_disk_defaults* | Defaults for attached disks options. | object({...}) | | ... | +| *attached_disks* | Additional disks, if options is null defaults will be used in its place. | list(object({...})) | | [] | +| *boot_disk* | Boot disk properties. | object({...}) | | ... | +| *group* | Instance group (for instance use). | object({...}) | | null | +| *group_manager* | Instance group manager (for template use). | object({...}) | | null | +| *hostname* | Instance FQDN name. | string | | null | +| *instance_count* | Number of instances to create (only for non-template usage). | number | | 1 | +| *instance_type* | Instance type. | string | | f1-micro | +| *labels* | Instance labels. | map(string) | | {} | +| *metadata* | Instance metadata. | map(string) | | {} | +| *min_cpu_platform* | Minimum CPU platform. | string | | null | +| *options* | Instance options. | object({...}) | | ... | +| *scratch_disks* | Scratch disks configuration. | object({...}) | | ... | +| *service_account* | Service account email. Unused if service account is auto-created. | string | | null | +| *service_account_create* | Auto-create service account. | bool | | false | +| *service_account_scopes* | Scopes applied to service account. | list(string) | | [] | +| *tags* | Instance tags. | list(string) | | [] | +| *use_instance_template* | Create instance template instead of instances. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| external_ips | Instance main interface external IP addresses. | | +| group | Instance group resource. | | +| group_manager | Instance group resource. | | +| instances | Instance resources. | | +| internal_ips | Instance main interface internal IP addresses. | | +| names | Instance names. | | +| self_links | Instance self links. | | +| service_account | Service account resource. | | +| service_account_email | Service account email. | | +| service_account_iam_email | Service account email. | | +| template | Template resource. | | +| template_name | Template name. | | + + +## TODO + +- [ ] add support for instance groups diff --git a/modules/compute-vm/instance_group.tf b/modules/compute-vm/instance_group.tf new file mode 100644 index 0000000000..17dcd44dbe --- /dev/null +++ b/modules/compute-vm/instance_group.tf @@ -0,0 +1,215 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_compute_instance_group" "unmanaged" { + count = ( + var.group != null && ! var.use_instance_template ? 1 : 0 + ) + project = var.project_id + network = ( + length(var.network_interfaces) > 0 + ? var.network_interfaces.0.network + : "" + ) + zone = var.zone + name = var.name + description = "Terraform-managed." + instances = [ + for name, instance in google_compute_instance.default : instance.self_link + ] + dynamic named_port { + for_each = var.group.named_ports != null ? var.group.named_ports : {} + iterator = config + content { + name = config.key + port = config.value + } + } +} + +resource "google_compute_instance_group_manager" "managed" { + count = ( + var.group_manager != null && var.use_instance_template + ? var.group_manager.regional ? 0 : 1 + : 0 + ) + project = var.project_id + zone = var.zone + name = var.name + base_instance_name = var.name + description = "Terraform-managed." + target_size = var.group_manager.target_size + target_pools = ( + var.group_manager.options == null + ? null + : var.group_manager.options.target_pools + ) + wait_for_instances = ( + var.group_manager.options == null + ? null + : var.group_manager.options.wait_for_instances + ) + dynamic auto_healing_policies { + for_each = ( + var.group_manager.auto_healing_policies == null + ? [] + : [var.group_manager.auto_healing_policies] + ) + iterator = config + content { + health_check = config.value.health_check + initial_delay_sec = config.value.initial_delay_sec + } + } + dynamic update_policy { + for_each = ( + var.group_manager.update_policy == null + ? [] + : [var.group_manager.update_policy] + ) + iterator = config + content { + type = config.value.type + minimal_action = config.value.minimal_action + min_ready_sec = config.value.min_ready_sec + max_surge_fixed = ( + config.value.max_surge_type == "fixed" ? config.value.max_surge : null + ) + max_surge_percent = ( + config.value.max_surge_type == "percent" ? config.value.max_surge : null + ) + max_unavailable_fixed = ( + config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null + ) + max_unavailable_percent = ( + config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null + ) + } + } + dynamic named_port { + for_each = var.group_manager.named_ports != null ? var.group_manager.named_ports : {} + iterator = config + content { + name = config.key + port = config.value + } + } + version { + name = "${var.name}-default" + instance_template = google_compute_instance_template.default.0.self_link + } + dynamic version { + for_each = ( + var.group_manager.versions == null ? [] : [var.group_manager.versions] + ) + iterator = config + content { + name = config.value.name + instance_template = config.value.instance_template + target_size { + fixed = config.value.target_type == "fixed" ? config.value.target_size : null + percent = config.value.target_type == "percent" ? config.value.target_size : null + } + } + } +} + +resource "google_compute_region_instance_group_manager" "managed" { + count = ( + var.group_manager != null && var.use_instance_template + ? var.group_manager.regional ? 1 : 0 + : 0 + ) + project = var.project_id + region = var.region + name = var.name + base_instance_name = var.name + description = "Terraform-managed." + target_size = var.group_manager.target_size + target_pools = ( + var.group_manager.options == null + ? null + : var.group_manager.options.target_pools + ) + wait_for_instances = ( + var.group_manager.options == null + ? null + : var.group_manager.options.wait_for_instances + ) + dynamic auto_healing_policies { + for_each = ( + var.group_manager.auto_healing_policies == null + ? [] + : [var.group_manager.auto_healing_policies] + ) + iterator = config + content { + health_check = config.value.health_check + initial_delay_sec = config.value.initial_delay_sec + } + } + dynamic update_policy { + for_each = ( + var.group_manager.update_policy == null + ? [] + : [var.group_manager.update_policy] + ) + iterator = config + content { + type = config.value.type + minimal_action = config.value.minimal_action + min_ready_sec = config.value.min_ready_sec + max_surge_fixed = ( + config.value.max_surge_type == "fixed" ? config.value.max_surge : null + ) + max_surge_percent = ( + config.value.max_surge_type == "percent" ? config.value.max_surge : null + ) + max_unavailable_fixed = ( + config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null + ) + max_unavailable_percent = ( + config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null + ) + } + } + dynamic named_port { + for_each = var.group.named_ports + iterator = config + content { + name = config.key + port = config.value + } + } + version { + name = "${var.name}-default" + instance_template = google_compute_instance_template.default.0.self_link + } + dynamic version { + for_each = ( + var.group_manager.versions == null ? [] : [var.group_manager.versions] + ) + iterator = config + content { + name = config.value.name + instance_template = config.value.instance_template + target_size { + fixed = config.value.target_type == "fixed" ? config.value.target_size : null + percent = config.value.target_type == "percent" ? config.value.target_size : null + } + } + } +} diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf new file mode 100644 index 0000000000..75f642dc8b --- /dev/null +++ b/modules/compute-vm/main.tf @@ -0,0 +1,226 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + attached_disks = { + for disk in var.attached_disks : + disk.name => merge(disk, { + options = disk.options == null ? var.attached_disk_defaults : disk.options + }) + } + attached_disks_pairs = { + for pair in setproduct(keys(local.names), keys(local.attached_disks)) : + "${pair[0]}-${pair[1]}" => { name = pair[0], disk_name = pair[1] } + } + names = ( + var.use_instance_template + ? { "${var.name}" = 0 } + : { for i in range(0, var.instance_count) : "${var.name}-${i + 1}" => i } + ) + service_account_email = ( + var.service_account_create + ? ( + length(google_service_account.service_account) > 0 + ? google_service_account.service_account[0].email + : null + ) + : var.service_account + ) + service_account_scopes = ( + length(var.service_account_scopes) > 0 + ? var.service_account_scopes + : ( + var.service_account_create + ? ["https://www.googleapis.com/auth/cloud-platform"] + : [ + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring.write" + ] + ) + ) +} + +resource "google_compute_disk" "disks" { + for_each = var.use_instance_template ? {} : local.attached_disks_pairs + project = var.project_id + zone = var.zone + name = each.key + type = local.attached_disks[each.value.disk_name].options.type + size = local.attached_disks[each.value.disk_name].size + labels = merge(var.labels, { + disk_name = local.attached_disks[each.value.disk_name].name + disk_type = local.attached_disks[each.value.disk_name].options.type + image = local.attached_disks[each.value.disk_name].image + }) +} + +resource "google_compute_instance" "default" { + for_each = var.use_instance_template ? {} : local.names + project = var.project_id + zone = var.zone + name = each.key + hostname = var.hostname + description = "Managed by the compute-vm Terraform module." + tags = var.tags + machine_type = var.instance_type + min_cpu_platform = var.min_cpu_platform + can_ip_forward = var.options.can_ip_forward + allow_stopping_for_update = var.options.allow_stopping_for_update + deletion_protection = var.options.deletion_protection + metadata = var.metadata + labels = var.labels + + dynamic attached_disk { + for_each = { + for resource_name, pair in local.attached_disks_pairs : + resource_name => local.attached_disks[pair.disk_name] if pair.name == each.key + } + iterator = config + content { + device_name = config.value.name + mode = config.value.options.mode + source = google_compute_disk.disks[config.key].name + } + } + + boot_disk { + initialize_params { + type = var.boot_disk.type + image = var.boot_disk.image + size = var.boot_disk.size + } + } + + dynamic network_interface { + for_each = var.network_interfaces + iterator = config + content { + network = config.value.network + subnetwork = config.value.subnetwork + network_ip = config.value.addresses == null ? null : ( + length(config.value.addresses.internal) == 0 + ? null + : config.value.addresses.internal[each.value] + ) + dynamic access_config { + for_each = config.value.nat ? [config.value.addresses] : [] + iterator = nat_addresses + content { + nat_ip = nat_addresses.value == null ? null : ( + length(nat_addresses.value) == 0 ? null : nat_addresses.value[each.value] + ) + } + } + } + } + + scheduling { + automatic_restart = ! var.options.preemptible + on_host_maintenance = var.options.preemptible ? "TERMINATE" : "MIGRATE" + preemptible = var.options.preemptible + } + + dynamic scratch_disk { + for_each = [ + for i in range(0, var.scratch_disks.count) : var.scratch_disks.interface + ] + iterator = config + content { + interface = config.value + } + } + + service_account { + email = local.service_account_email + scopes = local.service_account_scopes + } + + # guest_accelerator + # shielded_instance_config + +} + +resource "google_compute_instance_template" "default" { + count = var.use_instance_template ? 1 : 0 + project = var.project_id + region = var.region + name_prefix = "${var.name}-" + description = "Managed by the compute-vm Terraform module." + tags = var.tags + machine_type = var.instance_type + min_cpu_platform = var.min_cpu_platform + can_ip_forward = var.options.can_ip_forward + metadata = var.metadata + labels = var.labels + + disk { + source_image = var.boot_disk.image + disk_type = var.boot_disk.type + disk_size_gb = var.boot_disk.size + boot = true + } + + dynamic disk { + for_each = local.attached_disks + iterator = config + content { + auto_delete = config.value.options.auto_delete + device_name = config.value.name + disk_type = config.value.options.type + disk_size_gb = config.value.size + mode = config.value.options.mode + source_image = config.value.image + source = config.value.options.source + type = "PERSISTENT" + } + } + + dynamic network_interface { + for_each = var.network_interfaces + iterator = config + content { + network = config.value.network + subnetwork = config.value.subnetwork + dynamic access_config { + for_each = config.value.nat ? [""] : [] + content {} + } + } + } + + scheduling { + automatic_restart = ! var.options.preemptible + on_host_maintenance = var.options.preemptible ? "TERMINATE" : "MIGRATE" + preemptible = var.options.preemptible + } + + service_account { + email = local.service_account_email + scopes = local.service_account_scopes + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_service_account" "service_account" { + count = var.service_account_create ? 1 : 0 + project = var.project_id + account_id = "tf-vm-${var.name}" + display_name = "Terraform VM ${var.name}." +} diff --git a/modules/compute-vm/outputs.tf b/modules/compute-vm/outputs.tf new file mode 100644 index 0000000000..e927c15965 --- /dev/null +++ b/modules/compute-vm/outputs.tf @@ -0,0 +1,110 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "external_ips" { + description = "Instance main interface external IP addresses." + value = ( + var.network_interfaces[0].nat + ? [ + for name, instance in google_compute_instance.default : + instance.network_interface.0.network_ip + ] + : [] + ) +} + +output "group" { + description = "Instance group resource." + value = ( + length(google_compute_instance_group.unmanaged) > 0 + ? google_compute_instance_group.unmanaged.0 + : null + ) +} + +output "group_manager" { + description = "Instance group resource." + value = ( + length(google_compute_instance_group_manager.managed) > 0 + ? google_compute_instance_group_manager.managed.0 + : ( + length(google_compute_region_instance_group_manager.managed) > 0 + ? google_compute_region_instance_group_manager.managed.0 + : null + ) + ) +} + +output "instances" { + description = "Instance resources." + value = [for name, instance in google_compute_instance.default : instance] +} + +output "internal_ips" { + description = "Instance main interface internal IP addresses." + value = [ + for name, instance in google_compute_instance.default : + instance.network_interface.0.network_ip + ] +} + +output "names" { + description = "Instance names." + value = [for name, instance in google_compute_instance.default : instance.name] +} + +output "self_links" { + description = "Instance self links." + value = [for name, instance in google_compute_instance.default : instance.self_link] +} + +output "service_account" { + description = "Service account resource." + value = ( + var.service_account_create ? google_service_account.service_account[0] : null + ) +} + +output "service_account_email" { + description = "Service account email." + value = local.service_account_email +} + +output "service_account_iam_email" { + description = "Service account email." + value = join("", [ + "serviceAccount:", + local.service_account_email == null ? "" : local.service_account_email + ]) +} + +output "template" { + description = "Template resource." + value = ( + length(google_compute_instance_template.default) > 0 + ? google_compute_instance_template.default[0] + : null + ) +} + +output "template_name" { + description = "Template name." + value = ( + length(google_compute_instance_template.default) > 0 + ? google_compute_instance_template.default[0].name + : null + ) +} diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf new file mode 100644 index 0000000000..018e16d108 --- /dev/null +++ b/modules/compute-vm/variables.tf @@ -0,0 +1,231 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "attached_disks" { + description = "Additional disks, if options is null defaults will be used in its place." + type = list(object({ + name = string + image = string + size = string + options = object({ + auto_delete = bool + mode = string + source = string + type = string + }) + })) + default = [] +} + +variable "attached_disk_defaults" { + description = "Defaults for attached disks options." + type = object({ + auto_delete = bool + mode = string + type = string + source = string + }) + default = { + auto_delete = true + source = null + mode = "READ_WRITE" + type = "pd-ssd" + } +} + +variable "boot_disk" { + description = "Boot disk properties." + type = object({ + image = string + size = number + type = string + }) + default = { + image = "projects/debian-cloud/global/images/family/debian-10" + type = "pd-ssd" + size = 10 + } +} + +variable "group" { + description = "Instance group (for instance use)." + type = object({ + named_ports = map(number) + }) + default = null +} + +variable "group_manager" { + description = "Instance group manager (for template use)." + type = object({ + auto_healing_policies = object({ + health_check = string + initial_delay_sec = number + }) + named_ports = map(number) + options = object({ + target_pools = list(string) + wait_for_instances = bool + }) + regional = bool + target_size = number + update_policy = object({ + type = string # OPPORTUNISTIC | PROACTIVE + minimal_action = string # REPLACE | RESTART + min_ready_sec = number + max_surge_type = string # fixed | percent + max_surge = number + max_unavailable_type = string + max_unavailable = number + }) + versions = list(object({ + name = string + instance_template = string + target_type = string # fixed | percent + target_size = number + })) + }) + default = null +} + +variable "hostname" { + description = "Instance FQDN name." + type = string + default = null +} + +variable "instance_count" { + description = "Number of instances to create (only for non-template usage)." + type = number + default = 1 +} + +variable "instance_type" { + description = "Instance type." + type = string + default = "f1-micro" +} + +variable "labels" { + description = "Instance labels." + type = map(string) + default = {} +} + +variable "metadata" { + description = "Instance metadata." + type = map(string) + default = {} +} + +variable "min_cpu_platform" { + description = "Minimum CPU platform." + type = string + default = null +} + +variable "name" { + description = "Instances base name." + type = string +} + +variable "network_interfaces" { + description = "Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed." + type = list(object({ + nat = bool + network = string + subnetwork = string + addresses = object({ + internal = list(string) + external = list(string) + }) + })) +} + +variable "options" { + description = "Instance options." + type = object({ + allow_stopping_for_update = bool + can_ip_forward = bool + deletion_protection = bool + preemptible = bool + }) + default = { + allow_stopping_for_update = true + can_ip_forward = false + deletion_protection = false + preemptible = false + } +} + +variable "project_id" { + description = "Project id." + type = string +} + +variable "region" { + description = "Compute region." + type = string +} + +variable "scratch_disks" { + description = "Scratch disks configuration." + type = object({ + count = number + interface = string + }) + default = { + count = 0 + interface = "NVME" + } +} + +variable "service_account" { + description = "Service account email. Unused if service account is auto-created." + type = string + default = null +} + +variable "service_account_create" { + description = "Auto-create service account." + type = bool + default = false +} + +# scopes and scope aliases list +# https://cloud.google.com/sdk/gcloud/reference/compute/instances/create#--scopes +variable "service_account_scopes" { + description = "Scopes applied to service account." + type = list(string) + default = [] +} + +variable "tags" { + description = "Instance tags." + type = list(string) + default = [] +} + +variable "use_instance_template" { + description = "Create instance template instead of instances." + type = bool + default = false +} + +variable "zone" { + description = "Compute zone." + type = string +} diff --git a/modules/cos-container/.gitignore b/modules/cos-container/.gitignore new file mode 100644 index 0000000000..bbd801c73d --- /dev/null +++ b/modules/cos-container/.gitignore @@ -0,0 +1 @@ +**/test.tf diff --git a/modules/cos-container/README.md b/modules/cos-container/README.md new file mode 100644 index 0000000000..793e52a5c1 --- /dev/null +++ b/modules/cos-container/README.md @@ -0,0 +1,25 @@ +# Container Optimized OS modules + +This set of modules creates specialized [cloud-config](https://cloud.google.com/container-optimized-os/docs/how-to/run-container-instance#starting_a_docker_container_via_cloud-config) configurations for [Container Optimized OS](https://cloud.google.com/container-optimized-os/docs), that are used to quickly spin up containerized services for DNS, HTTP, or databases. + +It's meant to fullfill different use cases: + +- when designing, to quickly prototype specialized services (eg MySQL access or HTTP serving) +- when planning migrations, to emulate production services for core infrastructure or perfomance testing +- in production, to easily add glue components for services like DNS (eg to work around inbound/outbound forwarding limitations) +- as a basis to implement cloud-native production deployments that leverage cloud-init for configuration management + +## Available modules + +- [CoreDNS](./coredns) +- [MySQL](./mysql) +- [ ] Nginx +- [ ] Squid forward proxy + +## Using the modules + +All modules are designed to be as lightweight as possible, so that specialized modules like [compute-vm](../compute-vm) can be leveraged to manage instances or instance templates, and to allow simple forking to create custom derivatives. + +To use the modules with instances or instance templates, simply set use their `cloud_config` output for the `user-data` metadata. When updating the metadata after a variable change remember to manually restart the instances that use a module's output, or the changes won't effect the running system. + +For convenience when developing or prototyping infrastructure, an optional test instance is included in all modules. If it's not needed, the linked `*instance.tf` files can be removed from the modules without harm. diff --git a/modules/cos-container/coredns/Corefile b/modules/cos-container/coredns/Corefile new file mode 100644 index 0000000000..e5a7674faa --- /dev/null +++ b/modules/cos-container/coredns/Corefile @@ -0,0 +1,6 @@ +. { + forward . /etc/resolv.conf + reload + log + errors +} \ No newline at end of file diff --git a/modules/cos-container/coredns/Corefile-hosts b/modules/cos-container/coredns/Corefile-hosts new file mode 100644 index 0000000000..1baa581a66 --- /dev/null +++ b/modules/cos-container/coredns/Corefile-hosts @@ -0,0 +1,9 @@ +. { + hosts /etc/coredns/example.hosts example.org { + 127.0.0.1 localhost.example.org localhost + } + forward . /etc/resolv.conf + reload + log + errors +} diff --git a/modules/cos-container/coredns/README.md b/modules/cos-container/coredns/README.md new file mode 100644 index 0000000000..87fda8f47c --- /dev/null +++ b/modules/cos-container/coredns/README.md @@ -0,0 +1,93 @@ +# Containerized CoreDNS on Container Optimized OS + +This module manages a `cloud-config` configuration that starts a containerized [CoreDNS](https://coredns.io/) service on Container Optimized OS, using the [official image](https://hub.docker.com/r/coredns/coredns/). + +The resulting `cloud-config` can be customized in a number of ways: + +- a custom CoreDNS configuration can be set using the `coredns_config` variable +- additional files (eg for hosts or zone files) can be passed in via the `files` variable +- a completely custom `cloud-config` can be passed in via the `cloud_config` variable, and additional template variables can be passed in via `config_variables` + +The default instance configuration inserts iptables rules to allow traffic on the DNS TCP and UDP ports, and the 8080 port for the optional HTTP health check that can be enabled via the CoreDNS [health plugin](https://coredns.io/plugins/health/). + +Logging and monitoring are enabled via the [Google Cloud Logging driver](https://docs.docker.com/config/containers/logging/gcplogs/) configured for the CoreDNS container, and the [Node Problem Detector](https://cloud.google.com/container-optimized-os/docs/how-to/monitoring) service started by default on boot. + +The module renders the generated cloud config in the `cloud_config` output, to be used in instances or instance templates via the `user-data` metadata. + +For convenience during development or for simple use cases, the module can optionally manage a single instance via the `test_instance` variable. If the instance is not needed the `instance*tf` files can be safely removed. Refer to the [top-level README](../README.md) for more details on the included instance. + +## Examples + +### Default CoreDNS configuration + +This example will create a `cloud-config` that uses the module's defaults, creating a simple DNS forwarder. + +```hcl +module "cos-coredns" { + source = "./modules/cos-container/coredns" +} + +# use it as metadata in a compute instance or template +resource "google_compute_instance" "default" { + metadata = { + user-data = module.cos-coredns.cloud_config + } +``` + +### Custom CoreDNS configuration + +This example will create a `cloud-config` using a custom CoreDNS configuration, that leverages the [CoreDNS hosts plugin]() to serve a single zone via an included `hosts` format file. + +```hcl +module "cos-coredns" { + source = "./modules/cos-container/coredns" + coredns_config = "./modules/cos-container/coredns/Corefile-hosts" + files = { + "/etc/coredns/example.hosts" = { + content = "127.0.0.2 foo.example.org foo" + owner = null + permissions = "0644" + } +} +``` + +### CoreDNS instance + +This example shows how to create the single instance optionally managed by the module, providing all required attributes in the `test_instance` variable. The instance is purposefully kept simple and should only be used in development, or when designing infrastructures. + +```hcl +module "cos-coredns" { + source = "./modules/cos-container/coredns" + test_instance = { + project_id = "my-project" + zone = "europe-west1-b" + name = "cos-coredns" + type = "f1-micro" + tags = ["ssh"] + metadata = {} + network = "default" + subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet" + disks = [] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | +| *config_variables* | Additional variables used to render the cloud-config template. | map(any) | | {} | +| *coredns_config* | CoreDNS configuration path, if null default will be used. | string | | null | +| *file_defaults* | Default owner and permissions for files. | object({...}) | | ... | +| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | map(object({...})) | | {} | +| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | +| test_instance | Optional test instance name and address | | + diff --git a/modules/cos-container/coredns/cloud-config.yaml b/modules/cos-container/coredns/cloud-config.yaml new file mode 100644 index 0000000000..b8796c5271 --- /dev/null +++ b/modules/cos-container/coredns/cloud-config.yaml @@ -0,0 +1,83 @@ +#cloud-config + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# https://hub.docker.com/r/coredns/coredns/ +# https://coredns.io/manual/toc/#installation + +# TODO: switch to the gcplogs logging driver, and set driver labels + +write_files: + - path: /var/lib/docker/daemon.json + permissions: 0644 + owner: root + content: | + { + "live-restore": true, + "storage-driver": "overlay2", + "log-opts": { + "max-size": "1024m" + } + } + + # disable systemd-resolved to free port 53 on the loopback interface + - path: /etc/systemd/resolved.conf + permissions: 0644 + owner: root + content: | + [Resolve] + LLMNR=no + DNSStubListener=no + + - path: /etc/coredns/Corefile + permissions: 0644 + owner: root + content: | + ${indent(6, corefile)} + + # coredns container service + - path: /etc/systemd/system/coredns.service + permissions: 0644 + owner: root + content: | + [Unit] + Description=Start CoreDNS container + After=gcr-online.target docker.socket + Wants=gcr-online.target docker.socket docker-events-collector.service + [Service] + ExecStart=/usr/bin/docker run --rm --name=coredns \ + --log-driver=gcplogs --network host \ + -v /etc/coredns:/etc/coredns \ + coredns/coredns -conf /etc/coredns/Corefile + ExecStop=/usr/bin/docker stop coredns + + %{ for path, data in files } + - path: ${path} + owner: ${lookup(data, "owner", "root")} + permissions: ${lookup(data, "permissions", "0644")} + content: | + ${indent(4, data.content)} + %{ endfor } + +bootcmd: + - systemctl start node-problem-detector + +runcmd: + - iptables -I INPUT 1 -p tcp -m tcp --dport 8080 -m state --state NEW,ESTABLISHED -j ACCEPT + - iptables -I INPUT 1 -p tcp -m tcp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT + - iptables -I INPUT 1 -p udp -m udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT + - systemctl daemon-reload + - systemctl restart systemd-resolved.service + - systemctl start coredns \ No newline at end of file diff --git a/modules/cos-container/coredns/instance.tf b/modules/cos-container/coredns/instance.tf new file mode 120000 index 0000000000..bdef596b6d --- /dev/null +++ b/modules/cos-container/coredns/instance.tf @@ -0,0 +1 @@ +../instance.tf \ No newline at end of file diff --git a/modules/cos-container/coredns/main.tf b/modules/cos-container/coredns/main.tf new file mode 100644 index 0000000000..c793557718 --- /dev/null +++ b/modules/cos-container/coredns/main.tf @@ -0,0 +1,41 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + cloud_config = templatefile(local.template, merge(var.config_variables, { + corefile = local.corefile + files = local.files + })) + corefile = file( + var.coredns_config == null ? "${path.module}/Corefile" : var.coredns_config + ) + files = { + for path, attrs in var.files : path => { + content = attrs.content, + owner = attrs.owner == null ? var.file_defaults.owner : attrs.owner, + permissions = ( + attrs.permissions == null + ? var.file_defaults.permissions + : attrs.permissions + ) + } + } + template = ( + var.cloud_config == null + ? "${path.module}/cloud-config.yaml" + : var.cloud_config + ) +} diff --git a/modules/cos-container/coredns/outputs-instance.tf b/modules/cos-container/coredns/outputs-instance.tf new file mode 120000 index 0000000000..ea9e240458 --- /dev/null +++ b/modules/cos-container/coredns/outputs-instance.tf @@ -0,0 +1 @@ +../outputs-instance.tf \ No newline at end of file diff --git a/modules/cos-container/coredns/outputs.tf b/modules/cos-container/coredns/outputs.tf new file mode 100644 index 0000000000..205a557165 --- /dev/null +++ b/modules/cos-container/coredns/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cloud_config" { + description = "Rendered cloud-config file to be passed as user-data instance metadata." + value = local.cloud_config +} diff --git a/modules/cos-container/coredns/variables-instance.tf b/modules/cos-container/coredns/variables-instance.tf new file mode 120000 index 0000000000..94af61e4dd --- /dev/null +++ b/modules/cos-container/coredns/variables-instance.tf @@ -0,0 +1 @@ +../variables-instance.tf \ No newline at end of file diff --git a/modules/cos-container/coredns/variables.tf b/modules/cos-container/coredns/variables.tf new file mode 100644 index 0000000000..f50d85826a --- /dev/null +++ b/modules/cos-container/coredns/variables.tf @@ -0,0 +1,55 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "cloud_config" { + description = "Cloud config template path. If null default will be used." + type = string + default = null +} + +variable "config_variables" { + description = "Additional variables used to render the cloud-config template." + type = map(any) + default = {} +} + +variable "coredns_config" { + description = "CoreDNS configuration path, if null default will be used." + type = string + default = null +} + +variable "file_defaults" { + description = "Default owner and permissions for files." + type = object({ + owner = string + permissions = string + }) + default = { + owner = "root" + permissions = "0644" + } +} + +variable "files" { + description = "Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null." + type = map(object({ + content = string + owner = string + permissions = string + })) + default = {} +} diff --git a/modules/cos-container/instance.tf b/modules/cos-container/instance.tf new file mode 100644 index 0000000000..294167c473 --- /dev/null +++ b/modules/cos-container/instance.tf @@ -0,0 +1,87 @@ +/** + * Copyright 2020 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 { + disks = var.test_instance == null ? {} : var.test_instance.disks + sa_roles = ["roles/logging.logWriter", "roles/monitoring.metricWriter"] +} + +resource "google_service_account" "default" { + count = var.test_instance == null ? 0 : 1 + project = var.test_instance.project_id + account_id = "cos-test-${var.test_instance.name}" + display_name = "Managed by the cos Terraform module." +} + +resource "google_project_iam_member" "default" { + for_each = var.test_instance == null ? toset([]) : toset(local.sa_roles) + project = var.test_instance.project_id + role = each.value + member = "serviceAccount:${google_service_account.default[0].email}" +} + +resource "google_compute_disk" "disks" { + for_each = local.disks + project = var.test_instance.project_id + zone = var.test_instance.zone + name = each.key + type = "pd-ssd" + size = each.value.size +} + +resource "google_compute_instance" "default" { + count = var.test_instance == null ? 0 : 1 + project = var.test_instance.project_id + zone = var.test_instance.zone + name = var.test_instance.name + description = "Managed by the cos Terraform module." + tags = var.test_instance.tags + machine_type = ( + var.test_instance.type == null ? "f1-micro" : var.test_instance.type + ) + metadata = merge(var.test_instance.metadata, { + user-data = local.cloud_config + }) + + dynamic attached_disk { + for_each = local.disks + iterator = disk + content { + device_name = disk.key + mode = disk.value.read_only ? "READ_ONLY" : "READ_WRITE" + source = google_compute_disk.disks[disk.key].name + } + } + + boot_disk { + initialize_params { + type = "pd-ssd" + image = "projects/cos-cloud/global/images/family/cos-stable" + size = 10 + } + } + + network_interface { + network = var.test_instance.network + subnetwork = var.test_instance.subnetwork + } + + service_account { + email = google_service_account.default[0].email + scopes = ["https://www.googleapis.com/auth/cloud-platform"] + } + +} diff --git a/modules/cos-container/mysql/.gitignore b/modules/cos-container/mysql/.gitignore new file mode 100644 index 0000000000..95ea22d21f --- /dev/null +++ b/modules/cos-container/mysql/.gitignore @@ -0,0 +1,2 @@ +kms.tf +kms.tf.sample diff --git a/modules/cos-container/mysql/README.md b/modules/cos-container/mysql/README.md new file mode 100644 index 0000000000..cfcfca3a93 --- /dev/null +++ b/modules/cos-container/mysql/README.md @@ -0,0 +1,100 @@ +# Containerized MySQL on Container Optimized OS + +This module manages a `cloud-config` configuration that starts a containerized [MySQL](https://www.mysql.com/) service on Container Optimized OS, using the [official image](https://hub.docker.com/_/mysql). + +The resulting `cloud-config` can be customized in a number of ways: + +- a custom MySQL configuration can be set using the `mysql_config` variable +- the container image can be changed via the `image` variable +- a data disk can be specified via the `mysql_data_disk` variable, the configuration will optionally format and mount it for container use +- a KMS encrypted root password can be passed to the container image, and decrypted at runtime on the instance using the attributes in the `kms_config` variable +- a completely custom `cloud-config` can be passed in via the `cloud_config` variable, and additional template variables can be passed in via `config_variables` + +The default instance configuration inserts a sngle iptables rule to allow traffic on the default MySQL port. + +Logging and monitoring are enabled via the [Google Cloud Logging driver](https://docs.docker.com/config/containers/logging/gcplogs/) configured for the CoreDNS container, and the [Node Problem Detector](https://cloud.google.com/container-optimized-os/docs/how-to/monitoring) service started by default on boot. + +The module renders the generated cloud config in the `cloud_config` output, to be used in instances or instance templates via the `user-data` metadata. + +For convenience during development or for simple use cases, the module can optionally manage a single instance via the `test_instance` variable. Please note that an `f1-micro` instance is too small to run MySQL. If the instance is not needed the `instance*tf` files can be safely removed. Refer to the [top-level README](../README.md) for more details on the included instance. + +## Examples + +### Default MySQL configuration + +This example will create a `cloud-config` that uses the container's default configuration, and a plaintext password for the MySQL root user. + +```hcl +module "cos-mysql" { + source = "./modules/cos-container/mysql" + mysql_password = "foo" +} + +# use it as metadata in a compute instance or template +resource "google_compute_instance" "default" { + metadata = { + user-data = module.cos-mysql.cloud_config + } +``` + +### Custom MySQL configuration and KMS encrypted password + +This example will create a `cloud-config` that uses a custom MySQL configuration, and passes in an encrypted password and the KMS attributes required to decrypt it. Please note that the instance service account needs the `roles/cloudkms.cryptoKeyDecrypter` on the specified KMS key. + +```hcl +module "cos-mysql" { + source = "./modules/cos-container/mysql" + mysql_config = "./my.cnf" + mysql_password = "CiQAsd7WY==" + kms_config = { + project_id = "my-project" + keyring = "test-cos" + location = "europe-west1" + key = "mysql" + } +} +``` + +### MySQL instance + +This example shows how to create the single instance optionally managed by the module, providing all required attributes in the `test_instance` variable. The instance is purposefully kept simple and should only be used in development, or when designing infrastructures. + +```hcl +module "cos-mysql" { + source = "./modules/cos-container/mysql" + mysql_password = "foo" + test_instance = { + project_id = "my-project" + zone = "europe-west1-b" + name = "cos-mysql" + type = "n1-standard-1" + tags = ["ssh"] + metadata = {} + network = "default" + subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/my-subnet" + disks = [] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| mysql_password | MySQL root password. If an encrypted password is set, use the kms_config variable to specify KMS configuration. | string | ✓ | | +| *cloud_config* | Cloud config template path. If null default will be used. | string | | null | +| *config_variables* | Additional variables used to render the cloud-config template. | map(any) | | {} | +| *image* | MySQL container image. | string | | mysql:5.7 | +| *kms_config* | Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used. | object({...}) | | null | +| *mysql_config* | MySQL configuration file content, if null container default will be used. | string | | null | +| *mysql_data_disk* | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | string | | null | +| *test_instance* | Test/development instance attributes, leave null to skip creation. | object({...}) | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| cloud_config | Rendered cloud-config file to be passed as user-data instance metadata. | | +| test_instance | Optional test instance name and address | | + diff --git a/modules/cos-container/mysql/cloud-config.yaml b/modules/cos-container/mysql/cloud-config.yaml new file mode 100644 index 0000000000..ba60bd099f --- /dev/null +++ b/modules/cos-container/mysql/cloud-config.yaml @@ -0,0 +1,117 @@ +#cloud-config + +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +users: + - name: mysql + uid: 2000 + +write_files: + - path: /var/lib/docker/daemon.json + permissions: 0644 + owner: root + content: | + { + "live-restore": true, + "storage-driver": "overlay2", + "log-opts": { + "max-size": "1024m" + } + } + - path: /run/mysql/secrets/mysql-passwd${kms_config == null ? "" : "-cipher"}.txt + permissions: 0600 + owner: root + content: | + ${password} + %{~ if kms_config != null ~} + - path: /run/mysql/passwd.sh + permissions: 0700 + owner: root + content: | + #!/bin/bash + base64 -d /run/mysql/secrets/mysql-passwd-cipher.txt | docker run \ + --rm -i -v /run/mysql/secrets:/data google/cloud-sdk:alpine \ + gcloud kms decrypt --ciphertext-file - \ + --plaintext-file /data/mysql-passwd.txt \ + --keyring ${kms_config.keyring} \ + --key ${kms_config.key} \ + --project ${kms_config.project_id} \ + --location ${kms_config.location} + %{~ endif ~} + %{~ if mysql_config != null ~} + - path: /run/mysql/etc/my.cnf + permissions: 0644 + owner: mysql + content: | + ${indent(6, mysql_config)} + %{~ endif ~} + %{~ if mysql_data_disk != null ~} + - path: /etc/systemd/system/mysql-data.service + permissions: 0644 + owner: root + content: | + [Unit] + Description=MySQL data disk + ConditionPathExists=/dev/disk/by-id/${mysql_data_disk} + Before=mysql.service + [Service] + Type=oneshot + ExecStart=/bin/mkdir -p /run/mysql/data + ExecStart=/bin/bash -c \ + "/bin/lsblk -fn -o FSTYPE \ + /dev/disk/by-id/${mysql_data_disk} |grep ext4 \ + || mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard \ + /dev/disk/by-id/${mysql_data_disk}" + ExecStart=/bin/bash -c \ + "mount |grep /run/mysql/data \ + || mount -t ext4 /dev/disk/by-id/${mysql_data_disk} /run/mysql/data" + ExecStart=/sbin/resize2fs /dev/disk/by-id/${mysql_data_disk} + RemainAfterExit=true + %{~ endif ~} + - path: /etc/systemd/system/mysql.service + permissions: 0644 + owner: root + content: | + [Unit] + Description=MySQL service + After=%{~ if mysql_data_disk != null ~}mysql-data.service %{ endif ~}gcr-online.target docker.socket docker-events-collector.service + Wants=%{~ if mysql_data_disk != null ~}mysql-data.service %{ endif ~}gcr-online.target docker.socket + [Service] + %{~ if kms_config != null ~} + ExecStartPre=/run/mysql/passwd.sh + %{~ endif ~} + ExecStartPre=/bin/mkdir -p /run/mysql/data + ExecStartPre=/bin/chown -R 2000 /run/mysql/secrets /run/mysql/data + ExecStart=/usr/bin/docker run --rm --name=mysql \ + --user 2000:2000 \ + --log-driver=gcplogs \ + --network host \ + -e MYSQL_ROOT_PASSWORD_FILE=/etc/secrets/mysql-passwd.txt \ + -v /run/mysql/secrets:/etc/secrets \ + -v /run/mysql/data:/var/lib/mysql \ + %{~ if mysql_config != null ~} + -v /run/mysql/etc:/etc/mysql \ + %{~ endif ~} + ${image} \ + --ignore-db-dir=lost+found + ExecStop=/usr/bin/docker stop mysql + +bootcmd: + - systemctl start node-problem-detector + +runcmd: + - iptables -I INPUT 1 -p tcp -m tcp --dport 3306 -m state --state NEW,ESTABLISHED -j ACCEPT + - systemctl daemon-reload + - systemctl start mysql \ No newline at end of file diff --git a/modules/cos-container/mysql/instance.tf b/modules/cos-container/mysql/instance.tf new file mode 120000 index 0000000000..bdef596b6d --- /dev/null +++ b/modules/cos-container/mysql/instance.tf @@ -0,0 +1 @@ +../instance.tf \ No newline at end of file diff --git a/modules/cos-container/mysql/main.tf b/modules/cos-container/mysql/main.tf new file mode 100644 index 0000000000..dc352deac1 --- /dev/null +++ b/modules/cos-container/mysql/main.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + cloud_config = templatefile(local.template, merge(var.config_variables, { + image = var.image + kms_config = var.kms_config + mysql_config = var.mysql_config + mysql_data_disk = var.mysql_data_disk + password = var.mysql_password + })) + template = ( + var.cloud_config == null + ? "${path.module}/cloud-config.yaml" + : var.cloud_config + ) +} diff --git a/modules/cos-container/mysql/outputs-instance.tf b/modules/cos-container/mysql/outputs-instance.tf new file mode 120000 index 0000000000..ea9e240458 --- /dev/null +++ b/modules/cos-container/mysql/outputs-instance.tf @@ -0,0 +1 @@ +../outputs-instance.tf \ No newline at end of file diff --git a/modules/cos-container/mysql/outputs.tf b/modules/cos-container/mysql/outputs.tf new file mode 100644 index 0000000000..205a557165 --- /dev/null +++ b/modules/cos-container/mysql/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cloud_config" { + description = "Rendered cloud-config file to be passed as user-data instance metadata." + value = local.cloud_config +} diff --git a/modules/cos-container/mysql/variables-instance.tf b/modules/cos-container/mysql/variables-instance.tf new file mode 120000 index 0000000000..94af61e4dd --- /dev/null +++ b/modules/cos-container/mysql/variables-instance.tf @@ -0,0 +1 @@ +../variables-instance.tf \ No newline at end of file diff --git a/modules/cos-container/mysql/variables.tf b/modules/cos-container/mysql/variables.tf new file mode 100644 index 0000000000..3ec88259ed --- /dev/null +++ b/modules/cos-container/mysql/variables.tf @@ -0,0 +1,61 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "cloud_config" { + description = "Cloud config template path. If null default will be used." + type = string + default = null +} + +variable "config_variables" { + description = "Additional variables used to render the cloud-config template." + type = map(any) + default = {} +} + +variable "image" { + description = "MySQL container image." + type = string + default = "mysql:5.7" +} + +variable "kms_config" { + description = "Optional KMS configuration to decrypt passed-in password. Leave null if a plaintext password is used." + type = object({ + project_id = string + keyring = string + location = string + key = string + }) + default = null +} + +variable "mysql_config" { + description = "MySQL configuration file content, if null container default will be used." + type = string + default = null +} + +variable "mysql_data_disk" { + description = "MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data." + type = string + default = null +} + +variable "mysql_password" { + description = "MySQL root password. If an encrypted password is set, use the kms_config variable to specify KMS configuration." + type = string +} diff --git a/modules/cos-container/outputs-instance.tf b/modules/cos-container/outputs-instance.tf new file mode 100644 index 0000000000..0524baac2e --- /dev/null +++ b/modules/cos-container/outputs-instance.tf @@ -0,0 +1,24 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "test_instance" { + description = "Optional test instance name and address" + value = (var.test_instance == null ? {} : { + address = google_compute_instance.default[0].network_interface.0.network_ip + name = google_compute_instance.default[0].name + service_account = google_service_account.default[0].email + }) +} diff --git a/modules/cos-container/variables-instance.tf b/modules/cos-container/variables-instance.tf new file mode 100644 index 0000000000..e8ee5415be --- /dev/null +++ b/modules/cos-container/variables-instance.tf @@ -0,0 +1,34 @@ +/** + * Copyright 2020 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 "test_instance" { + description = "Test/development instance attributes, leave null to skip creation." + type = object({ + project_id = string + zone = string + name = string + type = string + tags = list(string) + metadata = map(string) + network = string + subnetwork = string + disks = map(object({ + read_only = bool + size = number + })) + }) + default = null +} diff --git a/modules/dns/README.md b/modules/dns/README.md new file mode 100644 index 0000000000..6993e56958 --- /dev/null +++ b/modules/dns/README.md @@ -0,0 +1,48 @@ +# Google Cloud DNS Module + +This module allows simple management of Google Cloud DNS zones and records. It supports creating public, private, forwarding, and peering zones. For DNSSEC configuration, refer to the [`dns_managed_zone` documentation](https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html#dnssec_config). + +## Example + +```hcl +module "private-dns" { + source = "./modules/dns" + project_id = "myproject" + type = "private" + name = "test-example" + domain = "test.example." + client_networks = [var.vpc_self_link] + recordsets = [ + { name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] } + ] +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| domain | Zone domain, must end with a period. | string | ✓ | | +| name | Zone name, must be unique within the project. | string | ✓ | | +| project_id | Project id for the zone. | string | ✓ | | +| *client_networks* | List of VPC self links that can see this zone. | list(string) | | [] | +| *default_key_specs_key* | DNSSEC default key signing specifications: algorithm, key_length, key_type, kind. | any | | {} | +| *default_key_specs_zone* | DNSSEC default zone signing specifications: algorithm, key_length, key_type, kind. | any | | {} | +| *description* | Domain description. | string | | Terraform managed. | +| *dnssec_config* | DNSSEC configuration: kind, non_existence, state. | any | | {} | +| *forwarders* | List of target name servers, only valid for 'forwarding' zone types. | list(string) | | [] | +| *peer_network* | Peering network self link, only valid for 'peering' zone types. | string | | | +| *recordsets* | List of DNS record objects to manage. | list(object({...})) | | [] | +| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering'. | string | | private | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| domain | The DNS zone domain. | | +| name | The DNS zone name. | | +| name_servers | The DNS zone name servers. | | +| type | The DNS zone type. | | +| zone | DNS zone resource. | | + diff --git a/modules/dns/main.tf b/modules/dns/main.tf new file mode 100644 index 0000000000..4c9dfae214 --- /dev/null +++ b/modules/dns/main.tf @@ -0,0 +1,129 @@ +/** + * Copyright 2018 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 { + is_static_zone = var.type == "public" || var.type == "private" + recordsets = var.recordsets == null ? {} : { + for record in var.recordsets : + join("/", [record.name, record.type]) => record + } + zone = element(concat( + google_dns_managed_zone.non-public, google_dns_managed_zone.public + ), 0) +} + +resource "google_dns_managed_zone" "non-public" { + count = var.type != "public" ? 1 : 0 + provider = google-beta + project = var.project_id + name = var.name + dns_name = var.domain + description = "Terraform-managed zone." + visibility = "private" + + dynamic forwarding_config { + for_each = ( + var.type == "forwarding" && var.forwarders != null + ? { config = var.forwarders } + : {} + ) + iterator = config + content { + dynamic "target_name_servers" { + for_each = config.value + iterator = address + content { + ipv4_address = address.value + } + } + } + } + + dynamic peering_config { + for_each = ( + var.type == "peering" && var.peer_network != null + ? { config = var.peer_network } + : {} + ) + iterator = config + content { + target_network { + network_url = config.value + } + } + } + + private_visibility_config { + dynamic "networks" { + for_each = var.client_networks + iterator = network + content { + network_url = network.value + } + } + } + +} + +resource "google_dns_managed_zone" "public" { + count = var.type == "public" ? 1 : 0 + project = var.project_id + name = var.name + dns_name = var.domain + description = var.description + visibility = "public" + + dynamic "dnssec_config" { + for_each = var.dnssec_config == {} ? [] : list(var.dnssec_config) + iterator = config + content { + kind = lookup(config.value, "kind", "dns#managedZoneDnsSecConfig") + non_existence = lookup(config.value, "non_existence", "nsec3") + state = lookup(config.value, "state", "off") + + default_key_specs { + algorithm = lookup(var.default_key_specs_key, "algorithm", "rsasha256") + key_length = lookup(var.default_key_specs_key, "key_length", 2048) + key_type = lookup(var.default_key_specs_key, "key_type", "keySigning") + kind = lookup(var.default_key_specs_key, "kind", "dns#dnsKeySpec") + } + default_key_specs { + algorithm = lookup(var.default_key_specs_zone, "algorithm", "rsasha256") + key_length = lookup(var.default_key_specs_zone, "key_length", 1024) + key_type = lookup(var.default_key_specs_zone, "key_type", "zoneSigning") + kind = lookup(var.default_key_specs_zone, "kind", "dns#dnsKeySpec") + } + } + } + +} + +resource "google_dns_record_set" "cloud-static-records" { + for_each = ( + var.type == "public" || var.type == "private" + ? local.recordsets + : {} + ) + project = var.project_id + managed_zone = var.name + name = each.value.name != "" ? "${each.value.name}.${var.domain}" : var.domain + type = each.value.type + ttl = each.value.ttl + rrdatas = each.value.records + depends_on = [ + google_dns_managed_zone.non-public, google_dns_managed_zone.public + ] +} diff --git a/modules/dns/outputs.tf b/modules/dns/outputs.tf new file mode 100644 index 0000000000..df35628a21 --- /dev/null +++ b/modules/dns/outputs.tf @@ -0,0 +1,40 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "type" { + description = "The DNS zone type." + value = var.type +} + +output "zone" { + description = "DNS zone resource." + value = local.zone +} + +output "name" { + description = "The DNS zone name." + value = local.zone.name +} + +output "domain" { + description = "The DNS zone domain." + value = local.zone.dns_name +} + +output "name_servers" { + description = "The DNS zone name servers." + value = local.zone.name_servers +} diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf new file mode 100644 index 0000000000..0991038c0e --- /dev/null +++ b/modules/dns/variables.tf @@ -0,0 +1,97 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ + +############################################################################### +# zone variables # +############################################################################### + +variable "client_networks" { + description = "List of VPC self links that can see this zone." + type = list(string) + default = [] +} + +variable "description" { + description = "Domain description." + type = string + default = "Terraform managed." +} + +# TODO(ludoo): add link to DNSSEC documentation in README +# https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html#dnssec_config + +variable "default_key_specs_key" { + description = "DNSSEC default key signing specifications: algorithm, key_length, key_type, kind." + type = any + default = {} +} + +variable "default_key_specs_zone" { + description = "DNSSEC default zone signing specifications: algorithm, key_length, key_type, kind." + type = any + default = {} +} + +variable "dnssec_config" { + description = "DNSSEC configuration: kind, non_existence, state." + type = any + default = {} +} + +variable "domain" { + description = "Zone domain, must end with a period." + type = string +} + +# TODO(ludoo): add support for forwarding path attribute +variable "forwarders" { + description = "List of target name servers, only valid for 'forwarding' zone types." + type = list(string) + default = [] +} + +variable "name" { + description = "Zone name, must be unique within the project." + type = string +} + +variable "peer_network" { + description = "Peering network self link, only valid for 'peering' zone types." + type = string + default = "" +} + +variable "project_id" { + description = "Project id for the zone." + type = string +} + +variable "recordsets" { + type = list(object({ + name = string + type = string + ttl = number + records = list(string) + })) + description = "List of DNS record objects to manage." + default = [] +} + +variable "type" { + description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering'." + type = string + default = "private" +} diff --git a/modules/dns/versions.tf b/modules/dns/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/dns/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md new file mode 100644 index 0000000000..fafc4c0fd6 --- /dev/null +++ b/modules/folders-unit/README.md @@ -0,0 +1,56 @@ +# Google Cloud Unit Folders Module + +This module allows creation and management of an organizational hierarchy "unit" composed of a parent folder (usually mapped to a business unit or team), and a set of child folders (usually mapped to environments) each with a corresponding set of service accounts, IAM bindings and GCS buckets. + +## Example + +```hcl +module "folders-unit" { + source = "./modules/folders-unit" + name = "Business Intelligence" + short_name = "bi" + automation_project_id = "automation-project-394yr923811" + billing_account_id = "015617-16GHBC-AF02D9" + organization_id = "506128240800" + root_node = "folders/93469270123701" + prefix = "unique-prefix" + environments = { + dev = "Development", + test = "Testing", + prod = "Production" + } + service_account_keys = true +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| automation_project_id | Project id used for automation service accounts. | string | ✓ | | +| billing_account_id | Country billing account account. | string | ✓ | | +| name | Top folder name. | string | ✓ | | +| organization_id | Organization id. | string | ✓ | | +| root_node | Root node in folders/folder_id or organizations/org_id format. | string | ✓ | | +| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | string | ✓ | | +| *environments* | Unit environments short names. | map(string) | | ... | +| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | +| *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | object({...}) | | ... | +| *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | list(string) | | ... | +| *iam_members* | IAM members for roles applied on the unit folder. | map(list(string)) | | null | +| *iam_roles* | IAM roles applied on the unit folder. | list(string) | | null | +| *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | object({...}) | | ... | +| *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | string | | null | +| *service_account_keys* | Generate and store service account keys in the state file. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| env_folders | Unit environments folders. | | +| env_gcs_buckets | Unit environments tfstate gcs buckets. | | +| env_sa_keys | Unit environments service account keys. | ✓ | +| env_service_accounts | Unit environments service accounts. | | +| unit_folder | Unit top level folder. | | + diff --git a/modules/folders-unit/locals.tf b/modules/folders-unit/locals.tf new file mode 100644 index 0000000000..1ad80700b9 --- /dev/null +++ b/modules/folders-unit/locals.tf @@ -0,0 +1,63 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + folder_roles = concat(var.iam_enviroment_roles, local.sa_xpn_folder_roles) + iam_members = var.iam_members == null ? {} : var.iam_members + iam_roles = var.iam_roles == null ? [] : var.iam_roles + unit_iam_bindings = { + for role in local.iam_roles : + role => lookup(local.iam_members, role, []) + } + folder_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), local.folder_roles) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + org_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), concat( + local.sa_xpn_org_roles, + local.sa_billing_org_roles, + local.sa_billing_org_roles)) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + billing_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), local.sa_billing_account_roles) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + service_accounts = { + for key, sa in google_service_account.environment : + key => "serviceAccount:${sa.email}" + } + sa_billing_account_roles = ( + var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_billing_org_roles = ( + ! var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_xpn_folder_roles = ( + local.sa_xpn_target_org ? [] : ["roles/compute.xpnAdmin"] + ) + sa_xpn_org_roles = ( + local.sa_xpn_target_org + ? ["roles/compute.xpnAdmin", "roles/resourcemanager.organizationViewer"] + : ["roles/resourcemanager.organizationViewer"] + ) + sa_xpn_target_org = ( + var.iam_xpn_config.target_org + || + substr(var.root_node, 0, 13) == "organizations" + ) +} diff --git a/modules/folders-unit/main.tf b/modules/folders-unit/main.tf new file mode 100644 index 0000000000..33e68b131b --- /dev/null +++ b/modules/folders-unit/main.tf @@ -0,0 +1,109 @@ +/** + * Copyright 2020 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 { + organization_id = element(split("/", var.organization_id), 1) +} + +############################################################################### +# Folders and folder IAM # +############################################################################### + +resource "google_folder" "unit" { + display_name = var.name + parent = var.root_node +} + +resource "google_folder" "environment" { + for_each = var.environments + display_name = each.value + parent = google_folder.unit.name +} + +resource "google_folder_iam_binding" "unit" { + for_each = local.unit_iam_bindings + folder = google_folder.unit.name + role = each.key + members = each.value +} + +resource "google_folder_iam_binding" "environment" { + for_each = local.folder_iam_service_account_bindings + folder = google_folder.environment[each.value.environment].name + role = each.value.role + members = [local.service_accounts[each.value.environment]] +} + +############################################################################### +# Billing account and org IAM # +############################################################################### + +resource "google_organization_iam_member" "org_iam_member" { + for_each = local.org_iam_service_account_bindings + org_id = local.organization_id + role = each.value.role + member = local.service_accounts[each.value.environment] +} + +resource "google_billing_account_iam_member" "billing_iam_member" { + for_each = local.billing_iam_service_account_bindings + billing_account_id = var.billing_account_id + role = each.value.role + member = local.service_accounts[each.value.environment] +} + +################################################################################ +# Service Accounts # +################################################################################ + +resource "google_service_account" "environment" { + for_each = var.environments + project = var.automation_project_id + account_id = "${var.short_name}-${each.key}" + display_name = "${var.short_name} ${each.key} (Terraform managed)." +} + +resource "google_service_account_key" "keys" { + for_each = var.service_account_keys ? var.environments : {} + service_account_id = google_service_account.environment[each.key].email +} + +################################################################################ +# GCS and GCS IAM # +################################################################################ + +resource "google_storage_bucket" "tfstate" { + for_each = var.environments + project = var.automation_project_id + name = join("", [ + var.prefix == null ? "" : "${var.prefix}-", + "${var.short_name}-${each.key}-tf" + ]) + location = var.gcs_defaults.location + storage_class = var.gcs_defaults.storage_class + force_destroy = false + bucket_policy_only = true + versioning { + enabled = true + } +} + +resource "google_storage_bucket_iam_binding" "bindings" { + for_each = var.environments + bucket = google_storage_bucket.tfstate[each.key].name + role = "roles/storage.objectAdmin" + members = [local.service_accounts[each.key]] +} diff --git a/modules/folders-unit/outputs.tf b/modules/folders-unit/outputs.tf new file mode 100644 index 0000000000..520712f50a --- /dev/null +++ b/modules/folders-unit/outputs.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "unit_folder" { + description = "Unit top level folder." + value = { + id = google_folder.unit.name, + name = google_folder.unit.display_name + } +} + +output "env_gcs_buckets" { + description = "Unit environments tfstate gcs buckets." + value = { + for key, bucket in google_storage_bucket.tfstate + : key => bucket.name + } +} + +output "env_folders" { + description = "Unit environments folders." + value = { + for key, folder in google_folder.environment + : key => { + id = folder.name, + name = folder.display_name + } + } +} + +output "env_service_accounts" { + description = "Unit environments service accounts." + value = { + for key, sa in google_service_account.environment + : key => sa.email + } +} + +output "env_sa_keys" { + description = "Unit environments service account keys." + sensitive = true + value = { + for key, sa_key in google_service_account_key.keys : + key => sa_key.private_key + } +} diff --git a/modules/folders-unit/variables.tf b/modules/folders-unit/variables.tf new file mode 100644 index 0000000000..f7a8df3fa0 --- /dev/null +++ b/modules/folders-unit/variables.tf @@ -0,0 +1,122 @@ +/** + * Copyright 2020 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 "automation_project_id" { + description = "Project id used for automation service accounts." + type = string +} + +variable "billing_account_id" { + description = "Country billing account account." + type = string +} + +variable "environments" { + description = "Unit environments short names." + type = map(string) + default = { + non-prod = "Non production" + prod = "Production" + } +} + +variable "gcs_defaults" { + description = "Defaults use for the state GCS buckets." + type = map(string) + default = { + location = "EU" + storage_class = "MULTI_REGIONAL" + } +} + +variable "iam_billing_config" { + description = "Grant billing user role to service accounts, defaults to granting on the billing account." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "iam_enviroment_roles" { + description = "IAM roles granted to the environment service account on the environment sub-folder." + type = list(string) + default = [ + "roles/compute.networkAdmin", + "roles/owner", + "roles/resourcemanager.folderAdmin", + "roles/resourcemanager.projectCreator", + ] +} + +variable "iam_members" { + description = "IAM members for roles applied on the unit folder." + type = map(list(string)) + default = null +} + +variable "iam_roles" { + description = "IAM roles applied on the unit folder." + type = list(string) + default = null +} + +variable "iam_xpn_config" { + description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "name" { + description = "Top folder name." + type = string +} + +variable "organization_id" { + description = "Organization id in organizations/nnnnnn format." + type = string +} + +variable "prefix" { + description = "Optional prefix used for GCS bucket names to ensure uniqueness." + type = string + default = null +} + +variable "root_node" { + description = "Root node in folders/folder_id or organizations/org_id format." + type = string +} + +variable "service_account_keys" { + description = "Generate and store service account keys in the state file." + type = bool + default = false +} + +variable "short_name" { + description = "Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces." + type = string +} diff --git a/modules/folders-unit/versions.tf b/modules/folders-unit/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/folders-unit/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/folders/README.md b/modules/folders/README.md new file mode 100644 index 0000000000..e12947a453 --- /dev/null +++ b/modules/folders/README.md @@ -0,0 +1,45 @@ +# Google Cloud Folder Module + +This module allow creation and management of sets of folders sharing a common parent, and their individual IAM bindings. + +## Example + +```hcl +module "folder" { + source = "./modules/folder" + parent = "organizations/1234567890" + names = ["Folder one", "Folder two] + iam_members = { + "Folder one" = { + "roles/owner" => ["group:users@example.com"] + } + } + iam_roles = { + "Folder one" = ["roles/owner"] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| parent | Parent in folders/folder_id or organizations/org_id format. | string | ✓ | | +| *iam_members* | List of IAM members keyed by folder name and role. | map(map(list(string))) | | null | +| *iam_roles* | List of IAM roles keyed by folder name. | map(list(string)) | | null | +| *names* | Folder names. | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| folder | Folder resource (for single use). | | +| folders | Folder resources. | | +| id | Folder id (for single use). | | +| ids | Folder ids. | | +| ids_list | List of folder ids. | | +| name | Folder name (for single use). | | +| names | Folder names. | | +| names_list | List of folder names. | | + diff --git a/modules/folders/main.tf b/modules/folders/main.tf new file mode 100644 index 0000000000..228a195bf7 --- /dev/null +++ b/modules/folders/main.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2018 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 { + folders = ( + local.has_folders + ? [for name in var.names : google_folder.folders[name]] + : [] + ) + # needed when destroying + has_folders = length(google_folder.folders) > 0 + iam_pairs = var.iam_roles == null ? [] : flatten([ + for name, roles in var.iam_roles : + [for role in roles : { name = name, role = role }] + ]) + iam_keypairs = { + for pair in local.iam_pairs : + "${pair.name}-${pair.role}" => pair + } + iam_members = var.iam_members == null ? {} : var.iam_members +} + +resource "google_folder" "folders" { + for_each = toset(var.names) + display_name = each.value + parent = var.parent +} + +resource "google_folder_iam_binding" "authoritative" { + for_each = local.iam_keypairs + folder = google_folder.folders[each.value.name].name + role = each.value.role + members = lookup( + lookup(local.iam_members, each.value.name, {}), each.value.role, [] + ) +} + diff --git a/modules/folders/outputs.tf b/modules/folders/outputs.tf new file mode 100644 index 0000000000..c7d061500e --- /dev/null +++ b/modules/folders/outputs.tf @@ -0,0 +1,63 @@ +/** + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "folder" { + description = "Folder resource (for single use)." + value = local.has_folders ? local.folders[0] : null +} + +output "id" { + description = "Folder id (for single use)." + value = local.has_folders ? local.folders[0].name : null +} + +output "name" { + description = "Folder name (for single use)." + value = local.has_folders ? local.folders[0].display_name : null +} + +output "folders" { + description = "Folder resources." + value = local.folders +} + +output "ids" { + description = "Folder ids." + value = ( + local.has_folders + ? zipmap(var.names, [for f in local.folders : f.name]) + : {} + ) +} + +output "names" { + description = "Folder names." + value = ( + local.has_folders + ? zipmap(var.names, [for f in local.folders : f.display_name]) + : {} + ) +} + +output "ids_list" { + description = "List of folder ids." + value = [for f in local.folders : f.name] +} + +output "names_list" { + description = "List of folder names." + value = [for f in local.folders : f.display_name] +} diff --git a/modules/folders/variables.tf b/modules/folders/variables.tf new file mode 100644 index 0000000000..742031633c --- /dev/null +++ b/modules/folders/variables.tf @@ -0,0 +1,38 @@ +/** + * Copyright 2018 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 "iam_members" { + description = "List of IAM members keyed by folder name and role." + type = map(map(list(string))) + default = null +} + +variable "iam_roles" { + description = "List of IAM roles keyed by folder name." + type = map(list(string)) + default = null +} + +variable "names" { + description = "Folder names." + type = list(string) + default = [] +} + +variable "parent" { + description = "Parent in folders/folder_id or organizations/org_id format." + type = string +} diff --git a/modules/folders/versions.tf b/modules/folders/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/folders/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/gcs/README.md b/modules/gcs/README.md new file mode 100644 index 0000000000..2058365b8e --- /dev/null +++ b/modules/gcs/README.md @@ -0,0 +1,54 @@ +# Google Cloud Storage Module + +## Example + +```hcl +module "buckets" { + source = "./modules/gcs" + project_id = "myproject" + prefix = "test" + names = ["bucket-one", "bucket-two"] + bucket_policy_only = { + bucket-one = false + } + iam_members = { + bucket-two = { + "roles/storage.admin" = ["group:storage@example.com"] + } + } + iam_roles = { + bucket-two = ["roles/storage.admin"] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| names | Bucket name suffixes. | list(string) | ✓ | | +| project_id | Bucket project id. | string | ✓ | | +| *bucket_policy_only* | Optional map to disable object ACLS keyed by name, defaults to true. | map(bool) | | {} | +| *force_destroy* | Optional map to set force destroy keyed by name, defaults to false. | map(bool) | | {} | +| *iam_members* | IAM members keyed by bucket name and role. | map(map(list(string))) | | null | +| *iam_roles* | IAM roles keyed by bucket name. | map(list(string)) | | null | +| *labels* | Labels to be attached to all buckets. | map(string) | | {} | +| *location* | Bucket location. | string | | EU | +| *prefix* | Prefix used to generate the bucket name. | string | | | +| *storage_class* | Bucket storage class. | string | | MULTI_REGIONAL | +| *versioning* | Optional map to set versioning keyed by name, defaults to false. | map(bool) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| bucket | Bucket resource (for single use). | | +| buckets | Bucket resources. | | +| name | Bucket name (for single use). | | +| names | Bucket names. | | +| names_list | List of bucket names. | | +| url | Bucket URL (for single use). | | +| urls | Bucket URLs. | | +| urls_list | List of bucket URLs. | | + diff --git a/modules/gcs/main.tf b/modules/gcs/main.tf new file mode 100644 index 0000000000..baff11d9e4 --- /dev/null +++ b/modules/gcs/main.tf @@ -0,0 +1,62 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + buckets = ( + local.has_buckets + ? [for name in var.names : google_storage_bucket.buckets[name]] + : [] + ) + # needed when destroying + has_buckets = length(google_storage_bucket.buckets) > 0 + iam_pairs = var.iam_roles == null ? [] : flatten([ + for name, roles in var.iam_roles : + [for role in roles : { name = name, role = role }] + ]) + iam_keypairs = { + for pair in local.iam_pairs : + "${pair.name}-${pair.role}" => pair + } + iam_members = var.iam_members == null ? {} : var.iam_members + prefix = var.prefix == "" ? "" : join("-", [var.prefix, lower(var.location), ""]) +} + +resource "google_storage_bucket" "buckets" { + for_each = toset(var.names) + name = "${local.prefix}${lower(each.key)}" + project = var.project_id + location = var.location + storage_class = var.storage_class + force_destroy = lookup(var.force_destroy, each.key, false) + bucket_policy_only = lookup(var.bucket_policy_only, each.key, true) + versioning { + enabled = lookup(var.versioning, each.key, false) + } + labels = merge(var.labels, { + location = lower(var.location) + name = lower(each.key) + storage_class = lower(var.storage_class) + }) +} + +resource "google_storage_bucket_iam_binding" "bindings" { + for_each = local.iam_keypairs + bucket = google_storage_bucket.buckets[each.value.name].name + role = each.value.role + members = lookup( + lookup(local.iam_members, each.value.name, {}), each.value.role, [] + ) +} diff --git a/modules/gcs/outputs.tf b/modules/gcs/outputs.tf new file mode 100644 index 0000000000..9a8f8dfae8 --- /dev/null +++ b/modules/gcs/outputs.tf @@ -0,0 +1,63 @@ +/** + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "bucket" { + description = "Bucket resource (for single use)." + value = local.has_buckets ? local.buckets[0] : null +} + +output "name" { + description = "Bucket name (for single use)." + value = local.has_buckets ? local.buckets[0].name : null +} + +output "url" { + description = "Bucket URL (for single use)." + value = local.has_buckets ? local.buckets[0].url : null +} + +output "buckets" { + description = "Bucket resources." + value = local.buckets +} + +output "names" { + description = "Bucket names." + value = ( + local.has_buckets + ? zipmap(var.names, [for b in local.buckets : lookup(b, "name", null)]) + : {} + ) +} + +output "urls" { + description = "Bucket URLs." + value = ( + local.has_buckets + ? zipmap(var.names, [for b in local.buckets : b.url]) + : {} + ) +} + +output "names_list" { + description = "List of bucket names." + value = [for b in local.buckets : b.name] +} + +output "urls_list" { + description = "List of bucket URLs." + value = [for b in local.buckets : b.name] +} diff --git a/modules/gcs/variables.tf b/modules/gcs/variables.tf new file mode 100644 index 0000000000..6cc712ebc9 --- /dev/null +++ b/modules/gcs/variables.tf @@ -0,0 +1,79 @@ +/** + * Copyright 2018 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 "bucket_policy_only" { + description = "Optional map to disable object ACLS keyed by name, defaults to true." + type = map(bool) + default = {} +} + +variable "force_destroy" { + description = "Optional map to set force destroy keyed by name, defaults to false." + type = map(bool) + default = {} +} + +variable "iam_members" { + description = "IAM members keyed by bucket name and role." + type = map(map(list(string))) + default = null +} + +variable "iam_roles" { + description = "IAM roles keyed by bucket name." + type = map(list(string)) + default = null +} + +variable "labels" { + description = "Labels to be attached to all buckets." + type = map(string) + default = {} +} + +variable "location" { + description = "Bucket location." + type = string + default = "EU" +} + +variable "names" { + description = "Bucket name suffixes." + type = list(string) +} + +variable "prefix" { + description = "Prefix used to generate the bucket name." + type = string + default = "" +} + +variable "project_id" { + description = "Bucket project id." + type = string +} + +variable "storage_class" { + description = "Bucket storage class." + type = string + default = "MULTI_REGIONAL" +} + +variable "versioning" { + description = "Optional map to set versioning keyed by name, defaults to false." + type = map(bool) + default = {} +} diff --git a/modules/gcs/versions.tf b/modules/gcs/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/gcs/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md new file mode 100644 index 0000000000..fb0c95afbe --- /dev/null +++ b/modules/gke-cluster/README.md @@ -0,0 +1,77 @@ +# GKE cluster module + +This module allows simplified creation and management of GKE clusters and should be used together with the GKE nodepool module, as the default nodepool is turned off here and cannot be re-enabled. Some sensible defaults are set initially, in order to allow less verbose usage for most use cases. + +## Example + +```hcl +module "cluster-1" { + source = "./modules/gke-cluster" + project_id = "myproject" + name = "cluster-1" + location = "europe-west1-b" + network = var.network_self_link + subnetwork = var.subnet_self_link + secondary_range_pods = "pods" + secondary_range_services = "services" + default_max_pods_per_node = 32 + master_authorized_ranges = { + internal-vms = "10.0.0.0/8" + } + private_cluster_config = { + enable_private_nodes = true + enable_private_endpoint = true + master_ipv4_cidr_block = "192.168.0.0/28" + } + labels = { + environment = "dev" + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| location | Cluster zone or region. | string | ✓ | | +| name | Cluster name. | string | ✓ | | +| network | Name or self link of the VPC used for the cluster. Use the self link for Shared VPC. | string | ✓ | | +| project_id | Cluster project id. | string | ✓ | | +| secondary_range_pods | Subnet secondary range name used for pods. | string | ✓ | | +| secondary_range_services | Subnet secondary range name used for services. | string | ✓ | | +| subnetwork | VPC subnetwork name or self link. | string | ✓ | | +| *addons* | Addons enabled in the cluster (true means enabled). | object({...}) | | ... | +| *authenticator_security_group* | RBAC security group for Google Groups for GKE, format is gke-security-groups@yourdomain.com. | string | | null | +| *cluster_autoscaling* | Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler. | object({...}) | | ... | +| *database_encryption* | Enable and configure GKE application-layer secrets encryption. | object({...}) | | ... | +| *default_max_pods_per_node* | Maximum number of pods per node in this cluster. | number | | 110 | +| *description* | Cluster description. | string | | null | +| *enable_binary_authorization* | Enable Google Binary Authorization. | bool | | null | +| *enable_intranode_visibility* | Enable intra-node visibility to make same node pod to pod traffic visible. | bool | | null | +| *enable_shielded_nodes* | Enable Shielded Nodes features on all nodes in this cluster. | bool | | null | +| *enable_tpu* | Enable Cloud TPU resources in this cluster. | bool | | null | +| *labels* | Cluster resource labels. | map(string) | | null | +| *logging_service* | Logging service (disable with an empty string). | string | | logging.googleapis.com/kubernetes | +| *maintenance_start_time* | Maintenance start time in RFC3339 format 'HH:MM', where HH is [00-23] and MM is [00-59] GMT. | string | | 03:00 | +| *master_authorized_ranges* | External Ip address ranges that can access the Kubernetes cluster master through HTTPS. | map(string) | | {} | +| *min_master_version* | Minimum version of the master, defaults to the version of the most recent official release. | string | | null | +| *monitoring_service* | Monitoring service (disable with an empty string). | string | | monitoring.googleapis.com/kubernetes | +| *node_locations* | Zones in which the cluster's nodes are located. | list(string) | | [] | +| *pod_security_policy* | Enable the PodSecurityPolicy feature. | bool | | null | +| *private_cluster_config* | Enable and configure private cluster. | object({...}) | | null | +| *release_channel* | Release channel for GKE upgrades. | string | | null | +| *resource_usage_export_config* | Configure the ResourceUsageExportConfig feature. | object({...}) | | ... | +| *vertical_pod_autoscaling* | Enable the Vertical Pod Autoscaling feature. | bool | | null | +| *workload_identity* | Enable the Workload Identity feature. | bool | | true | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| cluster | Cluster resource. | ✓ | +| endpoint | Cluster endpoint. | | +| location | Cluster location. | | +| master_version | Master version. | | +| name | Cluster name. | | + diff --git a/modules/gke-cluster/main.tf b/modules/gke-cluster/main.tf new file mode 100644 index 0000000000..67e95dd3c7 --- /dev/null +++ b/modules/gke-cluster/main.tf @@ -0,0 +1,197 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_container_cluster" "cluster" { + provider = google-beta + project = var.project_id + name = var.name + description = var.description + location = var.location + node_locations = length(var.node_locations) == 0 ? null : var.node_locations + min_master_version = var.min_master_version + network = var.network + subnetwork = var.subnetwork + logging_service = var.logging_service + monitoring_service = var.monitoring_service + resource_labels = var.labels + default_max_pods_per_node = var.default_max_pods_per_node + enable_binary_authorization = var.enable_binary_authorization + enable_intranode_visibility = var.enable_intranode_visibility + enable_shielded_nodes = var.enable_shielded_nodes + enable_tpu = var.enable_tpu + initial_node_count = 1 + remove_default_node_pool = true + + # node_config + + addons_config { + http_load_balancing { + disabled = ! var.addons.http_load_balancing + } + horizontal_pod_autoscaling { + disabled = ! var.addons.horizontal_pod_autoscaling + } + network_policy_config { + disabled = ! var.addons.network_policy_config + } + # beta addons + # cloudrun is dynamic as it tends to trigger cluster recreation on change + dynamic cloudrun_config { + for_each = var.addons.istio_config.enabled && var.addons.cloudrun_config ? [""] : [] + content { + disabled = false + } + } + istio_config { + disabled = ! var.addons.istio_config.enabled + auth = var.addons.istio_config.tls ? "AUTH_MUTUAL_TLS" : "AUTH_NONE" + } + } + + # TODO(ludomagno): support setting address ranges instead of range names + # https://www.terraform.io/docs/providers/google/r/container_cluster.html#cluster_ipv4_cidr_block + ip_allocation_policy { + cluster_secondary_range_name = var.secondary_range_pods + services_secondary_range_name = var.secondary_range_services + } + + # TODO(ludomagno): make optional, and support beta feature + # https://www.terraform.io/docs/providers/google/r/container_cluster.html#daily_maintenance_window + maintenance_policy { + daily_maintenance_window { + start_time = var.maintenance_start_time + } + } + + master_auth { + client_certificate_config { + issue_client_certificate = false + } + } + + dynamic master_authorized_networks_config { + for_each = length(var.master_authorized_ranges) == 0 ? [] : list(var.master_authorized_ranges) + iterator = ranges + content { + dynamic cidr_blocks { + for_each = ranges.value + iterator = range + content { + cidr_block = range.value + display_name = range.key + } + } + } + } + + dynamic network_policy { + for_each = var.addons.network_policy_config ? [""] : [] + content { + enabled = true + provider = "CALICO" + } + } + + dynamic private_cluster_config { + for_each = var.private_cluster_config != null ? [var.private_cluster_config] : [] + iterator = config + content { + enable_private_nodes = config.value.enable_private_nodes + enable_private_endpoint = config.value.enable_private_endpoint + master_ipv4_cidr_block = config.value.master_ipv4_cidr_block + } + } + + # beta features + + dynamic authenticator_groups_config { + for_each = var.authenticator_security_group == null ? [] : [""] + content { + security_group = var.authenticator_security_group + } + } + + dynamic cluster_autoscaling { + for_each = var.cluster_autoscaling.enabled ? [var.cluster_autoscaling] : [] + iterator = config + content { + enabled = true + resource_limits { + resource_type = "cpu" + minimum = config.cpu_min + maximum = config.cpu_max + } + resource_limits { + resource_type = "memory" + minimum = config.memory_min + maximum = config.memory_max + } + } + } + + dynamic database_encryption { + for_each = var.database_encryption.enabled ? [var.database_encryption] : [] + iterator = config + content { + state = config.value.state + key_name = config.value.key_name + } + } + + dynamic pod_security_policy_config { + for_each = var.pod_security_policy != null ? [""] : [] + content { + enabled = var.pod_security_policy + } + } + + dynamic release_channel { + for_each = var.release_channel != null ? [""] : [] + content { + channel = var.release_channel + } + } + + dynamic resource_usage_export_config { + for_each = ( + var.resource_usage_export_config.enabled != null + && + var.resource_usage_export_config.dataset != null + ? [""] : [] + ) + content { + enable_network_egress_metering = var.resource_usage_export_config.enabled + bigquery_destination { + dataset_id = var.resource_usage_export_config.dataset + } + } + } + + dynamic vertical_pod_autoscaling { + for_each = var.vertical_pod_autoscaling == null ? [] : [""] + content { + enabled = var.vertical_pod_autoscaling + } + } + + dynamic workload_identity_config { + for_each = var.workload_identity ? [""] : [] + content { + identity_namespace = "${var.project_id}.svc.id.goog" + } + } + +} diff --git a/modules/gke-cluster/outputs.tf b/modules/gke-cluster/outputs.tf new file mode 100644 index 0000000000..6937ee966b --- /dev/null +++ b/modules/gke-cluster/outputs.tf @@ -0,0 +1,41 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cluster" { + description = "Cluster resource." + sensitive = true + value = google_container_cluster.cluster +} + +output "endpoint" { + description = "Cluster endpoint." + value = google_container_cluster.cluster.endpoint +} + +output "location" { + description = "Cluster location." + value = google_container_cluster.cluster.location +} + +output "master_version" { + description = "Master version." + value = google_container_cluster.cluster.master_version +} + +output "name" { + description = "Cluster name." + value = google_container_cluster.cluster.name +} diff --git a/modules/gke-cluster/variables.tf b/modules/gke-cluster/variables.tf new file mode 100644 index 0000000000..e39667711b --- /dev/null +++ b/modules/gke-cluster/variables.tf @@ -0,0 +1,236 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "addons" { + description = "Addons enabled in the cluster (true means enabled)." + type = object({ + horizontal_pod_autoscaling = bool + http_load_balancing = bool + network_policy_config = bool + cloudrun_config = bool + istio_config = object({ + enabled = bool + tls = bool + }) + }) + default = { + horizontal_pod_autoscaling = true + http_load_balancing = true + network_policy_config = false + cloudrun_config = false + istio_config = { + enabled = false + tls = false + } + } +} + +variable "authenticator_security_group" { + description = "RBAC security group for Google Groups for GKE, format is gke-security-groups@yourdomain.com." + type = string + default = null +} + +variable "cluster_autoscaling" { + description = "Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler." + type = object({ + enabled = bool + cpu_min = number + cpu_max = number + memory_min = number + memory_max = number + }) + default = { + enabled = false + cpu_min = 0 + cpu_max = 0 + memory_min = 0 + memory_max = 0 + } +} + +variable "database_encryption" { + description = "Enable and configure GKE application-layer secrets encryption." + type = object({ + enabled = bool + state = string + key_name = string + }) + default = { + enabled = false + state = "DECRYPTED" + key_name = null + } +} + +variable "default_max_pods_per_node" { + description = "Maximum number of pods per node in this cluster." + type = number + default = 110 +} + +variable "description" { + description = "Cluster description." + type = string + default = null +} + +variable "enable_binary_authorization" { + description = "Enable Google Binary Authorization." + type = bool + default = null +} + +variable "enable_intranode_visibility" { + description = "Enable intra-node visibility to make same node pod to pod traffic visible." + type = bool + default = null +} + +variable "enable_shielded_nodes" { + description = "Enable Shielded Nodes features on all nodes in this cluster." + type = bool + default = null +} + +variable "enable_tpu" { + description = "Enable Cloud TPU resources in this cluster." + type = bool + default = null +} + +variable "labels" { + description = "Cluster resource labels." + type = map(string) + default = null +} + +variable "location" { + description = "Cluster zone or region." + type = string +} + +variable "logging_service" { + description = "Logging service (disable with an empty string)." + type = string + default = "logging.googleapis.com/kubernetes" +} + +variable "maintenance_start_time" { + description = "Maintenance start time in RFC3339 format 'HH:MM', where HH is [00-23] and MM is [00-59] GMT." + type = string + default = "03:00" +} + +variable "master_authorized_ranges" { + description = "External Ip address ranges that can access the Kubernetes cluster master through HTTPS." + type = map(string) + default = {} +} + +variable "min_master_version" { + description = "Minimum version of the master, defaults to the version of the most recent official release." + type = string + default = null +} + +variable "monitoring_service" { + description = "Monitoring service (disable with an empty string)." + type = string + default = "monitoring.googleapis.com/kubernetes" +} + +variable "name" { + description = "Cluster name." + type = string +} + +variable "network" { + description = "Name or self link of the VPC used for the cluster. Use the self link for Shared VPC." + type = string +} + +variable "node_locations" { + description = "Zones in which the cluster's nodes are located." + type = list(string) + default = [] +} + +variable "pod_security_policy" { + description = "Enable the PodSecurityPolicy feature." + type = bool + default = null +} + +variable "private_cluster_config" { + description = "Enable and configure private cluster." + type = object({ + enable_private_nodes = bool + enable_private_endpoint = bool + master_ipv4_cidr_block = string + }) + default = null +} + +variable "project_id" { + description = "Cluster project id." + type = string +} + +variable "release_channel" { + description = "Release channel for GKE upgrades." + type = string + default = null +} + +variable "resource_usage_export_config" { + description = "Configure the ResourceUsageExportConfig feature." + type = object({ + enabled = bool + dataset = string + }) + default = { + enabled = null + dataset = null + } +} + +variable "secondary_range_pods" { + description = "Subnet secondary range name used for pods." + type = string +} + +variable "secondary_range_services" { + description = "Subnet secondary range name used for services." + type = string +} + +variable "subnetwork" { + description = "VPC subnetwork name or self link." + type = string +} + +variable "vertical_pod_autoscaling" { + description = "Enable the Vertical Pod Autoscaling feature." + type = bool + default = null +} + +variable "workload_identity" { + description = "Enable the Workload Identity feature." + type = bool + default = true +} diff --git a/modules/gke-cluster/versions.tf b/modules/gke-cluster/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/gke-cluster/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md new file mode 100644 index 0000000000..fcaa4bae59 --- /dev/null +++ b/modules/gke-nodepool/README.md @@ -0,0 +1,56 @@ +# GKE nodepool module + +This module allows simplified creation and management of individual GKE nodepools, setting sensible defaults (eg a service account is created for nodes if none is set) and allowing for less verbose usage in most use cases. + +## Example usage + +```hcl +module "cluster-1-nodepool-1" { + source = "../modules/gke-nodepool" + project_id = "myproject" + cluster_name = "cluster-1" + location = "europe-west1-b" + name = "nodepool-1" +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| cluster_name | Cluster name. | string | ✓ | | +| location | Cluster location. | string | ✓ | | +| project_id | Cluster project id. | string | ✓ | | +| *autoscaling_config* | Optional autoscaling configuration. | object({...}) | | null | +| *gke_version* | Kubernetes nodes version. Ignored if auto_upgrade is set in management_config. | string | | null | +| *initial_node_count* | Initial number of nodes for the pool. | number | | 1 | +| *management_config* | Optional node management configuration. | object({...}) | | null | +| *max_pods_per_node* | Maximum number of pods per node. | number | | null | +| *name* | Optional nodepool name. | string | | null | +| *node_config_disk_size* | Node disk size, defaults to 100GB. | number | | 100 | +| *node_config_disk_type* | Node disk type, defaults to pd-standard. | string | | pd-standard | +| *node_config_guest_accelerator* | Map of type and count of attached accelerator cards. | map(number) | | {} | +| *node_config_image_type* | Nodes image type. | string | | null | +| *node_config_labels* | Kubernetes labels attached to nodes. | map(string) | | {} | +| *node_config_local_ssd_count* | Number of local SSDs attached to nodes. | number | | 0 | +| *node_config_machine_type* | Nodes machine type. | string | | n1-standard-1 | +| *node_config_metadata* | Metadata key/value pairs assigned to nodes. Set disable-legacy-endpoints to true when using this variable. | map(string) | | null | +| *node_config_min_cpu_platform* | Minimum CPU platform for nodes. | string | | null | +| *node_config_oauth_scopes* | Set of Google API scopes for the nodes service account. Include logging-write, monitoring, and storage-ro when using this variable. | list(string) | | ["logging-write", "monitoring", "monitoring-write", "storage-ro"] | +| *node_config_preemptible* | Use preemptible VMs for nodes. | bool | | null | +| *node_config_sandbox_config* | GKE Sandbox configuration. Needs image_type set to COS_CONTAINERD and node_version set to 1.12.7-gke.17 when using this variable. | string | | null | +| *node_config_service_account* | Service account used for nodes. | string | | null | +| *node_config_shielded_instance_config* | Shielded instance options. | object({...}) | | null | +| *node_config_tags* | Network tags applied to nodes. | list(string) | | null | +| *node_config_workload_metadata_config* | Metadata configuration to expose to workloads on the node pool. | string | | SECURE | +| *node_count* | Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set. | number | | null | +| *node_locations* | Optional list of zones in which nodes should be located. Uses cluster locations if unset. | list(string) | | null | +| *upgrade_config* | Optional node upgrade configuration. | object({...}) | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| name | Nodepool name. | | + diff --git a/modules/gke-nodepool/main.tf b/modules/gke-nodepool/main.tf new file mode 100644 index 0000000000..40019e09c4 --- /dev/null +++ b/modules/gke-nodepool/main.tf @@ -0,0 +1,111 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_container_node_pool" "nodepool" { + provider = google-beta + + project = var.project_id + cluster = var.cluster_name + location = var.location + name = var.name + + initial_node_count = var.initial_node_count + max_pods_per_node = var.max_pods_per_node + node_count = var.autoscaling_config == null ? var.node_count : null + node_locations = var.node_locations + version = var.gke_version + + node_config { + disk_size_gb = var.node_config_disk_size + disk_type = var.node_config_disk_type + image_type = var.node_config_image_type + labels = var.node_config_labels + local_ssd_count = var.node_config_local_ssd_count + machine_type = var.node_config_machine_type + metadata = var.node_config_metadata + min_cpu_platform = var.node_config_min_cpu_platform + oauth_scopes = var.node_config_oauth_scopes + preemptible = var.node_config_preemptible + service_account = var.node_config_service_account + tags = var.node_config_tags + + dynamic guest_accelerator { + for_each = var.node_config_guest_accelerator + iterator = config + content { + type = config.key + count = config.value + } + } + + dynamic sandbox_config { + for_each = ( + var.node_config_sandbox_config != null + ? [var.node_config_sandbox_config] + : [] + ) + iterator = config + content { + sandbox_type = config.value + } + } + + dynamic shielded_instance_config { + for_each = ( + var.node_config_shielded_instance_config != null + ? [var.node_config_shielded_instance_config] + : [] + ) + iterator = config + content { + enable_secure_boot = config.value.enable_secure_boot + enable_integrity_monitoring = config.value.enable_integrity_monitoring + } + } + + workload_metadata_config { + node_metadata = var.node_config_workload_metadata_config + } + + } + + dynamic autoscaling { + for_each = var.autoscaling_config != null ? [var.autoscaling_config] : [] + iterator = config + content { + min_node_count = config.min_node_count + max_node_count = config.max_node_count + } + } + + dynamic management { + for_each = var.management_config != null ? [var.management_config] : [] + iterator = config + content { + auto_repair = config.auto_repair + auto_upgrade = config.auto_upgrade + } + } + + dynamic upgrade_settings { + for_each = var.upgrade_config != null ? [var.upgrade_config] : [] + iterator = config + content { + max_surge = config.max_surge + max_unavailable = config.max_unavailable + } + } +} diff --git a/modules/gke-nodepool/outputs.tf b/modules/gke-nodepool/outputs.tf new file mode 100644 index 0000000000..8645a91da2 --- /dev/null +++ b/modules/gke-nodepool/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "name" { + description = "Nodepool name." + value = google_container_node_pool.nodepool.name +} diff --git a/modules/gke-nodepool/variables.tf b/modules/gke-nodepool/variables.tf new file mode 100644 index 0000000000..049cfb3ede --- /dev/null +++ b/modules/gke-nodepool/variables.tf @@ -0,0 +1,198 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "autoscaling_config" { + description = "Optional autoscaling configuration." + type = object({ + min_node_count = number + max_node_count = number + }) + default = null +} + +variable "cluster_name" { + description = "Cluster name." + type = string +} + +variable "gke_version" { + description = "Kubernetes nodes version. Ignored if auto_upgrade is set in management_config." + type = string + default = null +} + +variable "initial_node_count" { + description = "Initial number of nodes for the pool." + type = number + default = 1 +} + +variable "location" { + description = "Cluster location." + type = string +} + +variable "management_config" { + description = "Optional node management configuration." + type = object({ + auto_repair = bool + auto_upgrade = bool + }) + default = null +} + +variable "max_pods_per_node" { + description = "Maximum number of pods per node." + type = number + default = null +} + +variable "name" { + description = "Optional nodepool name." + type = string + default = null +} + +variable "node_config_disk_size" { + description = "Node disk size, defaults to 100GB." + type = number + default = 100 +} + +variable "node_config_disk_type" { + description = "Node disk type, defaults to pd-standard." + type = string + default = "pd-standard" +} + +variable "node_config_guest_accelerator" { + description = "Map of type and count of attached accelerator cards." + type = map(number) + default = {} +} + +variable "node_config_image_type" { + description = "Nodes image type." + type = string + default = null +} + +variable "node_config_labels" { + description = "Kubernetes labels attached to nodes." + type = map(string) + default = {} +} + +variable "node_config_local_ssd_count" { + description = "Number of local SSDs attached to nodes." + type = number + default = 0 +} + +variable "node_config_machine_type" { + description = "Nodes machine type." + type = string + default = "n1-standard-1" +} + +variable "node_config_metadata" { + description = "Metadata key/value pairs assigned to nodes. Set disable-legacy-endpoints to true when using this variable." + type = map(string) + default = null +} + +variable "node_config_min_cpu_platform" { + description = "Minimum CPU platform for nodes." + type = string + default = null +} + +variable "node_config_oauth_scopes" { + description = "Set of Google API scopes for the nodes service account. Include logging-write, monitoring, and storage-ro when using this variable." + type = list(string) + default = ["logging-write", "monitoring", "monitoring-write", "storage-ro"] +} + +variable "node_config_preemptible" { + description = "Use preemptible VMs for nodes." + type = bool + default = null +} + +variable "node_config_sandbox_config" { + description = "GKE Sandbox configuration. Needs image_type set to COS_CONTAINERD and node_version set to 1.12.7-gke.17 when using this variable." + type = string + default = null +} + +variable "node_config_service_account" { + description = "Service account used for nodes." + type = string + default = null +} + +variable "node_config_shielded_instance_config" { + description = "Shielded instance options." + type = object({ + enable_secure_boot = bool + enable_integrity_monitoring = bool + }) + default = null +} + +variable "node_config_tags" { + description = "Network tags applied to nodes." + type = list(string) + default = null +} + +# variable "node_config_taint" { +# description = "Kubernetes taints applied to nodes." +# type = string +# default = null +# } + +variable "node_config_workload_metadata_config" { + description = "Metadata configuration to expose to workloads on the node pool." + type = string + default = "SECURE" +} + +variable "node_count" { + description = "Number of nodes per instance group, can be updated after creation. Ignored when autoscaling is set." + type = number + default = null +} + +variable "node_locations" { + description = "Optional list of zones in which nodes should be located. Uses cluster locations if unset." + type = list(string) + default = null +} + +variable "project_id" { + description = "Cluster project id." + type = string +} + +variable "upgrade_config" { + description = "Optional node upgrade configuration." + type = object({ + max_surge = number + max_unavailable = number + }) + default = null +} diff --git a/modules/gke-nodepool/versions.tf b/modules/gke-nodepool/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/gke-nodepool/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/iam-service-accounts/README.md b/modules/iam-service-accounts/README.md new file mode 100644 index 0000000000..00be1075db --- /dev/null +++ b/modules/iam-service-accounts/README.md @@ -0,0 +1,59 @@ +# Google Service Accounts Module + +This module allows simplified creation and management of one or more service accounts and their IAM bindings. Keys can optionally be generated and will be stored in Terraform state. To use them create a sensitive output in your root modules referencing the `keys` or `key` outputs, then extract the private key from the JSON formatted outputs. + +## Example + +```hcl +module "myproject-default-service-accounts" { + source = "./modules/iam-service-accounts" + project_id = "myproject" + names = ["vm-default", "gke-node-default"] + generate_keys = true + # authoritative roles granted *on* the service accounts to other identities + iam_roles = ["roles/iam.serviceAccountUser"] + iam_members = { + "roles/iam.serviceAccountUser" => ["user:foo@example.com"] + } + # non-authoritative roles granted *to* the service accounts on other resources + iam_project_roles = { + "myproject" = [ + "roles/logging.logWriter", + "roles/monitoring.metricWriter", + ] + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id where service account will be created. | string | ✓ | | +| *generate_keys* | Generate keys for service accounts. | bool | | false | +| *iam_billing_roles* | Project roles granted to all service accounts, by billing account id. | map(list(string)) | | {} | +| *iam_folder_roles* | Project roles granted to all service accounts, by folder id. | map(list(string)) | | {} | +| *iam_members* | Map of member lists which are granted authoritative roles on the service accounts, keyed by role. | map(list(string)) | | {} | +| *iam_organization_roles* | Project roles granted to all service accounts, by organization id. | map(list(string)) | | {} | +| *iam_project_roles* | Project roles granted to all service accounts, by project id. | map(list(string)) | | {} | +| *iam_roles* | List of authoritative roles granted on the service accounts. | list(string) | | [] | +| *iam_storage_roles* | Storage roles granted to all service accounts, by bucket name. | map(list(string)) | | {} | +| *names* | Names of the service accounts to create. | list(string) | | [] | +| *prefix* | Prefix applied to service account names. | string | | | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| email | Service account email (for single use). | | +| emails | Service account emails. | | +| emails_list | Service account emails. | | +| iam_email | IAM-format service account email (for single use). | | +| iam_emails | IAM-format service account emails. | | +| iam_emails_list | IAM-format service account emails. | | +| key | Service account key (for single use). | | +| keys | Map of service account keys. | ✓ | +| service_account | Service account resource (for single use). | | +| service_accounts | Service account resources. | | + diff --git a/modules/iam-service-accounts/main.tf b/modules/iam-service-accounts/main.tf new file mode 100644 index 0000000000..21524fa34d --- /dev/null +++ b/modules/iam-service-accounts/main.tf @@ -0,0 +1,155 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + iam_pairs = { + for pair in setproduct(var.names, var.iam_roles) : + "${pair.0}-${pair.1}" => { name = pair.0, role = pair.1 } + } + iam_billing_pairs = flatten([ + for entity, roles in var.iam_billing_roles : [ + for role in roles : [ + for name in var.names : { entity = entity, role = role, name = name } + ] + ] + ]) + iam_folder_pairs = flatten([ + for entity, roles in var.iam_folder_roles : [ + for role in roles : [ + for name in var.names : { entity = entity, role = role, name = name } + ] + ] + ]) + iam_organization_pairs = flatten([ + for entity, roles in var.iam_organization_roles : [ + for role in roles : [ + for name in var.names : { entity = entity, role = role, name = name } + ] + ] + ]) + iam_project_pairs = flatten([ + for entity, roles in var.iam_project_roles : [ + for role in roles : [ + for name in var.names : { entity = entity, role = role, name = name } + ] + ] + ]) + iam_storage_pairs = flatten([ + for entity, roles in var.iam_storage_roles : [ + for role in roles : [ + for name in var.names : { entity = entity, role = role, name = name } + ] + ] + ]) + keys = ( + var.generate_keys + ? { + for name in var.names : + name => lookup(google_service_account_key.keys, name, null) + } + : {} + ) + prefix = ( + var.prefix != "" + ? "${var.prefix}-" + : "" + ) + resource = ( + length(var.names) > 0 + ? lookup(local.resources, var.names[0], null) + : null + ) + resource_iam_emails = { + for name, resource in local.resources : + name => "serviceAccount:${resource.email}" + } + resources = { + for name in var.names : + name => lookup(google_service_account.service_accounts, name, null) + } +} + +resource "google_service_account" "service_accounts" { + for_each = toset(var.names) + project = var.project_id + account_id = "${local.prefix}${lower(each.value)}" + display_name = "Terraform-managed." +} + +resource "google_service_account_key" "keys" { + for_each = var.generate_keys ? toset(var.names) : toset([]) + service_account_id = google_service_account.service_accounts[each.value].email +} + +resource "google_service_account_iam_binding" "sa-roles" { + for_each = local.iam_pairs + service_account_id = google_service_account.service_accounts[each.value.name].name + role = each.value.role + members = lookup(var.iam_members, each.value.role, []) +} + +resource "google_billing_account_iam_member" "roles" { + for_each = { + for pair in local.iam_billing_pairs : + "${pair.name}-${pair.entity}-${pair.role}" => pair + } + billing_account_id = each.value.entity + role = each.value.role + member = local.resource_iam_emails[each.value.name] +} + +resource "google_folder_iam_member" "roles" { + for_each = { + for pair in local.iam_folder_pairs : + "${pair.name}-${pair.entity}-${pair.role}" => pair + } + folder = each.value.entity + role = each.value.role + member = local.resource_iam_emails[each.value.name] +} + +resource "google_organization_iam_member" "roles" { + for_each = { + for pair in local.iam_organization_pairs : + "${pair.name}-${pair.entity}-${pair.role}" => pair + } + org_id = each.value.entity + role = each.value.role + member = local.resource_iam_emails[each.value.name] +} + +resource "google_project_iam_member" "project-roles" { + for_each = { + for pair in local.iam_project_pairs : + "${pair.name}-${pair.entity}-${pair.role}" => pair + } + project = each.value.entity + role = each.value.role + member = local.resource_iam_emails[each.value.name] +} + +resource "google_storage_bucket_iam_member" "bucket-roles" { + for_each = { + for pair in local.iam_storage_pairs : + "${pair.name}-${pair.entity}-${pair.role}" => pair + } + bucket = each.value.entity + role = each.value.role + member = local.resource_iam_emails[each.value.name] +} + +# TODO(ludoo): link from README +# ref: https://cloud.google.com/vpc/docs/shared-vpc diff --git a/modules/iam-service-accounts/outputs.tf b/modules/iam-service-accounts/outputs.tf new file mode 100644 index 0000000000..9901675ef9 --- /dev/null +++ b/modules/iam-service-accounts/outputs.tf @@ -0,0 +1,66 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "service_account" { + description = "Service account resource (for single use)." + value = local.resource +} + +output "service_accounts" { + description = "Service account resources." + value = local.resources +} + +output "email" { + description = "Service account email (for single use)." + value = local.resource == null ? null : local.resource.email +} + +output "iam_email" { + description = "IAM-format service account email (for single use)." + value = local.resource == null ? null : "serviceAccount:${local.resource.email}" +} + +output "key" { + description = "Service account key (for single use)." + value = lookup(local.keys, var.names[0], null) +} + +output "emails" { + description = "Service account emails." + value = { for name, resource in local.resources : name => resource.email } +} + +output "iam_emails" { + description = "IAM-format service account emails." + value = local.resource_iam_emails +} + +output "emails_list" { + description = "Service account emails." + value = [for name, resource in local.resources : resource.email] +} + +output "iam_emails_list" { + description = "IAM-format service account emails." + value = [for name, resource in local.resources : "serviceAccount:${resource.email}"] +} + +output "keys" { + description = "Map of service account keys." + sensitive = true + value = local.keys +} diff --git a/modules/iam-service-accounts/variables.tf b/modules/iam-service-accounts/variables.tf new file mode 100644 index 0000000000..60e0ed5fb0 --- /dev/null +++ b/modules/iam-service-accounts/variables.tf @@ -0,0 +1,80 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "generate_keys" { + description = "Generate keys for service accounts." + type = bool + default = false +} + +variable "names" { + description = "Names of the service accounts to create." + type = list(string) + default = [] +} + +variable "prefix" { + description = "Prefix applied to service account names." + type = string + default = "" +} + +variable "project_id" { + description = "Project id where service account will be created." + type = string +} + +variable "iam_members" { + description = "Map of member lists which are granted authoritative roles on the service accounts, keyed by role." + type = map(list(string)) + default = {} +} + +variable "iam_roles" { + description = "List of authoritative roles granted on the service accounts." + type = list(string) + default = [] +} + +variable "iam_billing_roles" { + description = "Project roles granted to all service accounts, by billing account id." + type = map(list(string)) + default = {} +} + +variable "iam_folder_roles" { + description = "Project roles granted to all service accounts, by folder id." + type = map(list(string)) + default = {} +} + +variable "iam_organization_roles" { + description = "Project roles granted to all service accounts, by organization id." + type = map(list(string)) + default = {} +} + +variable "iam_project_roles" { + description = "Project roles granted to all service accounts, by project id." + type = map(list(string)) + default = {} +} + +variable "iam_storage_roles" { + description = "Storage roles granted to all service accounts, by bucket name." + type = map(list(string)) + default = {} +} diff --git a/modules/iam-service-accounts/versions.tf b/modules/iam-service-accounts/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/iam-service-accounts/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/kms/README.md b/modules/kms/README.md new file mode 100644 index 0000000000..55a0e944ec --- /dev/null +++ b/modules/kms/README.md @@ -0,0 +1,83 @@ +# Google KMS Module + +Simple Cloud KMS module that allows managing a keyring, zero or more keys in the keyring, and IAM role bindings on individual keys. + +The `protected` flag in the `key_attributes` variable sets the `prevent_destroy` lifecycle argument on an a per-key basis. + +## Examples + +### Minimal example + +```hcl +module "kms" { + source = "../modules/kms" + project_id = "my-project" + keyring = "test" + location = "europe" + keys = ["key-a", "key-b"] +} +``` + +### Granting access to keys via IAM + +```hcl +module "kms" { + source = "../modules/kms" + project_id = "my-project" + keyring = "test" + location = "europe" + keys = ["key-a", "key-b"] + iam_roles = { + key-a = ["roles/cloudkms.cryptoKeyDecrypter"] + } + iam_members = { + key-a = { + "roles/cloudkms.cryptoKeyDecrypter" = ["user:me@example.org"] + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| keyring | Keyring name. | string | ✓ | | +| location | Location for the keyring. | string | ✓ | | +| project_id | Project id where the keyring will be created. | string | ✓ | | +| *iam_members* | IAM members keyed by key name and role. | map(map(list(string))) | | {} | +| *iam_roles* | IAM roles keyed by key name. | map(list(string)) | | {} | +| *key_attributes* | Optional key attributes per key. | map(object({...})) | | {} | +| *key_defaults* | Key attribute defaults. | object({...}) | | ... | +| *keys* | Key names. | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| key_self_links | Key self links. | | +| keyring | Keyring resource. | | +| keys | Key resources. | | +| location | Keyring self link. | | +| name | Keyring self link. | | +| self_link | Keyring self link. | | + + +## Requirements + +These sections describe requirements for using this module. + +### IAM + +The following roles must be used to provision the resources of this module: + +- Cloud KMS Admin: `roles/cloudkms.admin` or +- Owner: `roles/owner` + +### APIs + +A project with the following APIs enabled must be used to host the +resources of this module: + +- Google Cloud Key Management Service: `cloudkms.googleapis.com` diff --git a/modules/kms/main.tf b/modules/kms/main.tf new file mode 100644 index 0000000000..18c14eff97 --- /dev/null +++ b/modules/kms/main.tf @@ -0,0 +1,73 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + # distinct is needed to make the expanding function argument work + iam_pairs = flatten([ + for name, roles in var.iam_roles : + [for role in roles : { name = name, role = role }] + ]) + iam_keypairs = { + for pair in local.iam_pairs : + "${pair.name}-${pair.role}" => pair + } + key_attributes = { + for name in var.keys : + name => lookup(var.key_attributes, name, var.key_defaults) + } + keys = merge( + { for name, resource in google_kms_crypto_key.keys : name => resource }, + { for name, resource in google_kms_crypto_key.keys-ephemeral : name => resource } + ) +} + +resource "google_kms_key_ring" "key_ring" { + name = var.keyring + project = var.project_id + location = var.location +} + +resource "google_kms_crypto_key" "keys" { + for_each = { + for name, attrs in local.key_attributes : + name => attrs if attrs.protected + } + name = each.key + key_ring = google_kms_key_ring.key_ring.self_link + rotation_period = each.value.rotation_period + lifecycle { + prevent_destroy = true + } +} + +resource "google_kms_crypto_key" "keys-ephemeral" { + for_each = { + for name, attrs in local.key_attributes : + name => attrs if ! attrs.protected + } + name = each.key + key_ring = google_kms_key_ring.key_ring.self_link + rotation_period = each.value.rotation_period +} + +resource "google_kms_crypto_key_iam_binding" "bindings" { + for_each = local.iam_keypairs + role = each.value.role + crypto_key_id = local.keys[each.value.name].self_link + members = lookup( + lookup(var.iam_members, each.value.name, {}), each.value.role, [] + ) +} diff --git a/modules/kms/outputs.tf b/modules/kms/outputs.tf new file mode 100644 index 0000000000..de30ea172c --- /dev/null +++ b/modules/kms/outputs.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "keyring" { + description = "Keyring resource." + value = google_kms_key_ring.key_ring +} + +output "location" { + description = "Keyring self link." + value = google_kms_key_ring.key_ring.location +} + +output "name" { + description = "Keyring self link." + value = google_kms_key_ring.key_ring.name +} + +output "self_link" { + description = "Keyring self link." + value = google_kms_key_ring.key_ring.self_link +} + +output "keys" { + description = "Key resources." + value = local.keys +} + +output "key_self_links" { + description = "Key self links." + value = { for name, resource in local.keys : name => resource.self_link } +} diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf new file mode 100644 index 0000000000..e976b72b21 --- /dev/null +++ b/modules/kms/variables.tf @@ -0,0 +1,70 @@ +/** + * Copyright 2018 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 "iam_members" { + description = "IAM members keyed by key name and role." + type = map(map(list(string))) + default = {} +} + +variable "iam_roles" { + description = "IAM roles keyed by key name." + type = map(list(string)) + default = {} +} + +variable "keyring" { + description = "Keyring name." + type = string +} + +variable "key_attributes" { + description = "Optional key attributes per key." + type = map(object({ + protected = bool + rotation_period = string + })) + default = {} +} + +variable "key_defaults" { + description = "Key attribute defaults." + type = object({ + protected = bool + rotation_period = string + }) + default = { + protected = true + rotation_period = "100000s" + } +} + +variable "keys" { + description = "Key names." + type = list(string) + default = [] +} + +# cf https://cloud.google.com/kms/docs/locations +variable "location" { + description = "Location for the keyring." + type = string +} + +variable "project_id" { + description = "Project id where the keyring will be created." + type = string +} diff --git a/modules/kms/versions.tf b/modules/kms/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/kms/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/logging-sinks/README.md b/modules/logging-sinks/README.md new file mode 100644 index 0000000000..c136e97d5e --- /dev/null +++ b/modules/logging-sinks/README.md @@ -0,0 +1,31 @@ +# Terraform Logging Sinks Module + +This module allows easy creation of one or more logging sinks. + +## Example + +```hcl +module "sinks" { +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| destinations | Map of destinations by sink name. | map(string) | ✓ | | +| parent | Resource where the sink will be created, eg 'organizations/nnnnnnnn'. | string | ✓ | | +| sinks | Map of sink name / sink filter. | map(string) | ✓ | | +| *default_options* | Default options used for sinks where no specific options are set. | object({...}) | | ... | +| *sink_options* | Optional map of sink name / sink options. If no options are specified for a sink defaults will be used. | map(object({...})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| names | Log sink names. | | +| sinks | Log sink resources. | | +| writer_identities | Log sink writer identities. | | + + diff --git a/modules/logging-sinks/main.tf b/modules/logging-sinks/main.tf new file mode 100644 index 0000000000..cc9d831768 --- /dev/null +++ b/modules/logging-sinks/main.tf @@ -0,0 +1,97 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + bigquery_destinations = { + for name, destination in var.destinations : + name => substr(destination, 0, 8) == "bigquery" + } + resource_type = element(split("/", var.parent), 0) + resource_id = element(split("/", var.parent), 1) + sink_options = { + for name, _ in var.sinks : + name => lookup(var.sink_options, name, var.default_options) + } + sink_resources = concat( + [for _, sink in google_logging_organization_sink.sinks : sink], + [for _, sink in google_logging_billing_account_sink.sinks : sink], + [for _, sink in google_logging_folder_sink.sinks : sink], + [for _, sink in google_logging_project_sink.sinks : sink], + ) +} + +resource "google_logging_organization_sink" "sinks" { + for_each = local.resource_type == "organizations" ? var.sinks : {} + name = each.key + org_id = local.resource_id + filter = each.value + destination = var.destinations[each.key] + include_children = local.sink_options[each.key].include_children + dynamic bigquery_options { + for_each = local.bigquery_destinations[each.key] ? ["1"] : [] + iterator = config + content { + use_partitioned_tables = local.sink_options[each.key].bigquery_partitioned_tables + } + } +} + +resource "google_logging_billing_account_sink" "sinks" { + for_each = local.resource_type == "billing_accounts" ? var.sinks : {} + name = each.key + billing_account = local.resource_id + filter = each.value + destination = var.destinations[each.key] + dynamic bigquery_options { + for_each = local.bigquery_destinations[each.key] ? ["1"] : [] + iterator = config + content { + use_partitioned_tables = local.sink_options[each.key].bigquery_partitioned_tables + } + } +} + +resource "google_logging_folder_sink" "sinks" { + for_each = local.resource_type == "folders" ? var.sinks : {} + name = each.key + folder = var.parent + filter = each.value + destination = var.destinations[each.key] + include_children = local.sink_options[each.key].include_children + dynamic bigquery_options { + for_each = local.bigquery_destinations[each.key] ? ["1"] : [] + iterator = config + content { + use_partitioned_tables = local.sink_options[each.key].bigquery_partitioned_tables + } + } +} + +resource "google_logging_project_sink" "sinks" { + for_each = local.resource_type == "projects" ? var.sinks : {} + name = each.key + project = local.resource_id + filter = each.value + destination = var.destinations[each.key] + unique_writer_identity = local.sink_options[each.key].unique_writer_identity + dynamic bigquery_options { + for_each = local.bigquery_destinations[each.key] ? ["1"] : [] + iterator = config + content { + use_partitioned_tables = local.sink_options[each.key].bigquery_partitioned_tables + } + } +} diff --git a/modules/logging-sinks/outputs.tf b/modules/logging-sinks/outputs.tf new file mode 100644 index 0000000000..dd8fc8dba7 --- /dev/null +++ b/modules/logging-sinks/outputs.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "sinks" { + description = "Log sink resources." + value = local.sink_resources +} + +output "names" { + description = "Log sink names." + value = [for sink in local.sink_resources : sink.name] +} + +output "writer_identities" { + description = "Log sink writer identities." + value = [for sink in local.sink_resources : sink.writer_identity] +} diff --git a/modules/logging-sinks/variables.tf b/modules/logging-sinks/variables.tf new file mode 100644 index 0000000000..24294e7d1d --- /dev/null +++ b/modules/logging-sinks/variables.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "default_options" { + description = "Default options used for sinks where no specific options are set." + type = object({ + bigquery_partitioned_tables = bool + include_children = bool + unique_writer_identity = bool + }) + default = { + bigquery_partitioned_tables = true + include_children = true + unique_writer_identity = false + } +} + +variable "destinations" { + description = "Map of destinations by sink name." + type = map(string) +} + +variable "parent" { + description = "Resource where the sink will be created, eg 'organizations/nnnnnnnn'." + type = string +} + +variable "sink_options" { + description = "Optional map of sink name / sink options. If no options are specified for a sink defaults will be used." + type = map(object({ + bigquery_partitioned_tables = bool + include_children = bool + unique_writer_identity = bool + })) + default = {} +} + +variable "sinks" { + description = "Map of sink name / sink filter." + type = map(string) +} diff --git a/modules/logging-sinks/versions.tf b/modules/logging-sinks/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/logging-sinks/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-address/README.md b/modules/net-address/README.md new file mode 100644 index 0000000000..9c1169b5b6 --- /dev/null +++ b/modules/net-address/README.md @@ -0,0 +1,35 @@ +# Net Address Reservation Module + +## Example + +```hcl +module "addresses" { + source = "./modules/net-address" + project_id = local.projects.host + external_addresses = { + nat-1 = module.vpc.subnet_regions["default"], + vpn-remote = module.vpc.subnet_regions["default"], + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project where the addresses will be created. | string | ✓ | | +| *external_addresses* | Map of external address regions, keyed by name. | map(string) | | {} | +| *global_addresses* | List of global addresses to create. | list(string) | | [] | +| *internal_address_addresses* | Optional explicit addresses for internal addresses, keyed by name. | map(string) | | {} | +| *internal_address_tiers* | Optional network tiers for internal addresses, keyed by name. | map(string) | | {} | +| *internal_addresses* | Map of internal addresses to create, keyed by name. | map(object({...})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| external_addresses | None | | +| global_addresses | None | | +| internal_addresses | None | | + \ No newline at end of file diff --git a/modules/net-address/main.tf b/modules/net-address/main.tf new file mode 100644 index 0000000000..b752f2aa90 --- /dev/null +++ b/modules/net-address/main.tf @@ -0,0 +1,44 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_compute_global_address" "global" { + for_each = toset(var.global_addresses) + project = var.project_id + name = each.value +} + +resource "google_compute_address" "external" { + for_each = var.external_addresses + project = var.project_id + name = each.key + description = "Terraform managed." + address_type = "EXTERNAL" + region = each.value + # labels = lookup(var.external_address_labels, each.key, {}) +} + +resource "google_compute_address" "internal" { + for_each = var.internal_addresses + project = var.project_id + name = each.key + description = "Terraform managed." + address_type = "INTERNAL" + region = each.value.region + subnetwork = each.value.subnetwork + address = lookup(var.internal_address_addresses, each.key, null) + network_tier = lookup(var.internal_address_tiers, each.key, null) + # labels = lookup(var.internal_address_labels, each.key, {}) +} diff --git a/modules/net-address/outputs.tf b/modules/net-address/outputs.tf new file mode 100644 index 0000000000..7d26158a62 --- /dev/null +++ b/modules/net-address/outputs.tf @@ -0,0 +1,48 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "external_addresses" { + value = { + for address in google_compute_address.external : + address.name => { + address = address.address + self_link = address.self_link + users = address.users + } + } +} + +output "global_addresses" { + value = { + for address in google_compute_global_address.global : + address.name => { + address = address.address + self_link = address.self_link + status = address.status + } + } +} + +output "internal_addresses" { + value = { + for address in google_compute_address.internal : + address.name => { + address = address.address + self_link = address.self_link + users = address.users + } + } +} diff --git a/modules/net-address/variables.tf b/modules/net-address/variables.tf new file mode 100644 index 0000000000..02b85f68b4 --- /dev/null +++ b/modules/net-address/variables.tf @@ -0,0 +1,65 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "external_addresses" { + description = "Map of external address regions, keyed by name." + type = map(string) + default = {} +} + +# variable "external_address_labels" { +# description = "Optional labels for external addresses, keyed by address name." +# type = map(map(string)) +# default = {} +# } + +variable "global_addresses" { + description = "List of global addresses to create." + type = list(string) + default = [] +} + +variable "internal_addresses" { + description = "Map of internal addresses to create, keyed by name." + type = map(object({ + region = string + subnetwork = string + })) + default = {} +} + +variable "internal_address_addresses" { + description = "Optional explicit addresses for internal addresses, keyed by name." + type = map(string) + default = {} +} + +variable "internal_address_tiers" { + description = "Optional network tiers for internal addresses, keyed by name." + type = map(string) + default = {} +} + +# variable "internal_address_labels" { +# description = "Optional labels for internal addresses, keyed by address name." +# type = map(map(string)) +# default = {} +# } + +variable "project_id" { + description = "Project where the addresses will be created." + type = string +} diff --git a/modules/net-address/versions.tf b/modules/net-address/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-address/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md new file mode 100644 index 0000000000..b8bb17ce24 --- /dev/null +++ b/modules/net-cloudnat/README.md @@ -0,0 +1,44 @@ +# Cloud NAT Module + +Simple Cloud NAT management, with optional router creation. + +## Example + +```hcl +module "nat" { + source = "../modules/net-cloudnat" + project_id = "my-project" + region = "europe-west1" + name = "default" + router_network = "my-vpc" +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | Name of the Cloud NAT resource. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region where resources will be created. | string | ✓ | | +| *addresses* | Optional list of external address self links. | list(string) | | [] | +| *config_min_ports_per_vm* | Minimum number of ports allocated to a VM from this NAT config. | number | | 64 | +| *config_source_subnets* | Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS). | string | | ALL_SUBNETWORKS_ALL_IP_RANGES | +| *config_timeouts* | Timeout configurations. | object({...}) | | ... | +| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | +| *router_create* | Create router. | bool | | true | +| *router_name* | Router name, leave blank if router will be created to use auto generated name. | string | | | +| *router_network* | Name of the VPC used for auto-created router. | string | | | +| *subnetworks* | Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS. | list(object({...})) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| name | Name of the Cloud NAT. | | +| nat_ip_allocate_option | NAT IP allocation mode. | | +| region | Cloud NAT region. | | +| router | Cloud NAT router resources (if auto created). | | +| router_name | Cloud NAT router name. | | + diff --git a/modules/net-cloudnat/main.tf b/modules/net-cloudnat/main.tf new file mode 100644 index 0000000000..fa652ff036 --- /dev/null +++ b/modules/net-cloudnat/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + router_name = ( + var.router_create ? google_compute_router.router[0].name : var.router_name + ) +} + +resource "google_compute_router" "router" { + count = var.router_create ? 1 : 0 + name = var.router_name == "" ? "${var.name}-nat" : var.router_name + project = var.project_id + region = var.region + network = var.router_network + bgp { + asn = var.router_asn + } +} + +resource "google_compute_router_nat" "nat" { + project = var.project_id + region = var.region + name = var.name + router = local.router_name + nat_ips = var.addresses + nat_ip_allocate_option = length(var.addresses) > 0 ? "MANUAL_ONLY" : "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = var.config_source_subnets + min_ports_per_vm = var.config_min_ports_per_vm + icmp_idle_timeout_sec = var.config_timeouts.icmp + udp_idle_timeout_sec = var.config_timeouts.udp + tcp_established_idle_timeout_sec = var.config_timeouts.tcp_established + tcp_transitory_idle_timeout_sec = var.config_timeouts.tcp_transitory + + dynamic "subnetwork" { + for_each = var.subnetworks + content { + name = subnetwork.value.self_link + source_ip_ranges_to_nat = subnetwork.value.config_source_ranges + secondary_ip_range_names = subnetwork.value.secondary_ranges + } + } +} + diff --git a/modules/net-cloudnat/outputs.tf b/modules/net-cloudnat/outputs.tf new file mode 100644 index 0000000000..8dad023498 --- /dev/null +++ b/modules/net-cloudnat/outputs.tf @@ -0,0 +1,40 @@ +/** + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "name" { + description = "Name of the Cloud NAT." + value = google_compute_router_nat.nat.name +} + +output "nat_ip_allocate_option" { + description = "NAT IP allocation mode." + value = google_compute_router_nat.nat.nat_ip_allocate_option +} + +output "region" { + description = "Cloud NAT region." + value = google_compute_router_nat.nat.region +} + +output "router" { + description = "Cloud NAT router resources (if auto created)." + value = var.router_create ? google_compute_router.router[0] : null +} + +output "router_name" { + description = "Cloud NAT router name." + value = local.router_name +} diff --git a/modules/net-cloudnat/variables.tf b/modules/net-cloudnat/variables.tf new file mode 100644 index 0000000000..5b987cf565 --- /dev/null +++ b/modules/net-cloudnat/variables.tf @@ -0,0 +1,98 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "addresses" { + description = "Optional list of external address self links." + type = list(string) + default = [] +} + +variable "config_min_ports_per_vm" { + description = "Minimum number of ports allocated to a VM from this NAT config." + type = number + default = 64 +} + +variable "config_source_subnets" { + description = "Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS)." + type = string + default = "ALL_SUBNETWORKS_ALL_IP_RANGES" +} + +variable "config_timeouts" { + description = "Timeout configurations." + type = object({ + icmp = number + tcp_established = number + tcp_transitory = number + udp = number + }) + default = { + icmp = 30 + tcp_established = 1200 + tcp_transitory = 30 + udp = 30 + } +} + +variable "name" { + description = "Name of the Cloud NAT resource." + type = string +} + +variable "project_id" { + description = "Project where resources will be created." + type = string +} + +variable "region" { + description = "Region where resources will be created." + type = string +} + +variable "router_asn" { + description = "Router ASN used for auto-created router." + type = number + default = 64514 +} + +variable "router_create" { + description = "Create router." + type = bool + default = true +} + +variable "router_name" { + description = "Router name, leave blank if router will be created to use auto generated name." + type = string + default = "" +} + +variable "router_network" { + description = "Name of the VPC used for auto-created router." + type = string + default = "" +} + +variable "subnetworks" { + description = "Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS." + type = list(object({ + self_link = string, + config_source_ranges = list(string) + secondary_ranges = list(string) + })) + default = [] +} diff --git a/modules/net-cloudnat/versions.tf b/modules/net-cloudnat/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-cloudnat/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md new file mode 100644 index 0000000000..23b24eded7 --- /dev/null +++ b/modules/net-vpc-firewall/README.md @@ -0,0 +1,77 @@ +# Google Cloud VPC Firewall + +This module allows creation and management of different types of firewall rules for a single VPC network: + +- blanket ingress rules based on IP ranges that allow all traffic via the `admin_ranges` variable +- simplified tag-based ingress rules for the HTTP, HTTPS and SSH protocols via the `xxx_source_ranges` variables; HTTP and HTTPS tags match those set by the console via the "Allow HTTP(S) traffic" instance flags +- custom rules via the `custom_rules` variables + +The simplified tag-based rules are enabled by default, set to the ranges of the GCP health checkers for HTTP/HTTPS, and the IAP forwarders for SSH. To disable them set the corresponding variables to empty lists. + +## Examples + +### Minimal open firewall + +This is often useful for prototyping or testing infrastructure, allowing open ingress from the private range, enabling SSH to private addresses from IAP, and HTTP/HTTPS from the health checkers. + +```hcl +module "firewall" { + source = "./modules/net-vpc-firewall" + project_id = "my-project" + network = "my-network" + admin_ranges_enabled = true + admin_ranges = ["10.0.0.0/8"] +} +``` + +### Custom rules + +This is an example of how to define custom rules, with a sample rule allowing open ingress for the NTP protocol to instances with the `ntp-svc` tag. + +```hcl +module "firewall" { + source = "../modules/net-vpc-firewall" + project_id = "my-project" + network = "my-network" + admin_ranges_enabled = true + admin_ranges = ["10.0.0.0/8"] + custom_rules = { + ntp-svc = { + description = "NTP service." + direction = "INGRESS" + action = "allow" + sources = [] + ranges = ["0.0.0.0/0"] + targets = ["ntp-svc"] + use_service_accounts = false + rules = [{ protocol = "udp", ports = [123] }] + extra_attributes = {} + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| network | Name of the network this set of firewall rules applies to. | string | ✓ | | +| project_id | Project id of the project that holds the network. | string | ✓ | | +| *admin_ranges* | IP CIDR ranges that have complete access to all subnets. | list(string) | | [] | +| *admin_ranges_enabled* | Enable admin ranges-based rules. | bool | | false | +| *custom_rules* | List of custom rule definitions (refer to variables file for syntax). | map(object({...})) | | {} | +| *http_source_ranges* | List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22"] | +| *https_source_ranges* | List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22"] | +| *ssh_source_ranges* | List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range. | list(string) | | ["35.235.240.0/20"] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| admin_ranges | Admin ranges data. | | +| custom_egress_allow_rules | Custom egress rules with allow blocks. | | +| custom_egress_deny_rules | Custom egress rules with allow blocks. | | +| custom_ingress_allow_rules | Custom ingress rules with allow blocks. | | +| custom_ingress_deny_rules | Custom ingress rules with deny blocks. | | + diff --git a/modules/net-vpc-firewall/main.tf b/modules/net-vpc-firewall/main.tf new file mode 100644 index 0000000000..7252e3a8a8 --- /dev/null +++ b/modules/net-vpc-firewall/main.tf @@ -0,0 +1,145 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + rules-allow = { + for name, attrs in var.custom_rules : name => attrs if attrs.action == "allow" + } + rules-deny = { + for name, attrs in var.custom_rules : name => attrs if attrs.action == "deny" + } +} + +############################################################################### +# rules based on IP ranges +############################################################################### + +resource "google_compute_firewall" "allow-admins" { + count = var.admin_ranges_enabled == true ? 1 : 0 + name = "${var.network}-ingress-admins" + description = "Access from the admin subnet to all subnets" + network = var.network + project = var.project_id + source_ranges = var.admin_ranges + allow { protocol = "icmp" } + allow { protocol = "tcp" } + allow { protocol = "udp" } +} + +############################################################################### +# rules based on tags +############################################################################### + +resource "google_compute_firewall" "allow-tag-ssh" { + count = length(var.ssh_source_ranges) > 0 ? 1 : 0 + name = "${var.network}-ingress-tag-ssh" + description = "Allow SSH to machines with the 'ssh' tag" + network = var.network + project = var.project_id + source_ranges = var.ssh_source_ranges + target_tags = ["ssh"] + allow { + protocol = "tcp" + ports = ["22"] + } +} + +resource "google_compute_firewall" "allow-tag-http" { + count = length(var.http_source_ranges) > 0 ? 1 : 0 + name = "${var.network}-ingress-tag-http" + description = "Allow HTTP to machines with the 'http-server' tag" + network = var.network + project = var.project_id + source_ranges = var.http_source_ranges + target_tags = ["http-server"] + allow { + protocol = "tcp" + ports = ["80"] + } +} + +resource "google_compute_firewall" "allow-tag-https" { + count = length(var.https_source_ranges) > 0 ? 1 : 0 + name = "${var.network}-ingress-tag-https" + description = "Allow HTTPS to machines with the 'https' tag" + network = var.network + project = var.project_id + source_ranges = var.https_source_ranges + target_tags = ["https-server"] + allow { + protocol = "tcp" + ports = ["443"] + } +} + +################################################################################ +# dynamic rules # +################################################################################ + +resource "google_compute_firewall" "custom_allow" { + # provider = "google-beta" + for_each = local.rules-allow + name = each.key + description = each.value.description + direction = each.value.direction + network = var.network + project = var.project_id + source_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null + destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null + source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources + source_service_accounts = each.value.use_service_accounts && each.value.direction == "INGRESS" ? each.value.sources : null + target_tags = each.value.use_service_accounts ? null : each.value.targets + target_service_accounts = each.value.use_service_accounts ? each.value.targets : null + disabled = lookup(each.value.extra_attributes, "disabled", false) + priority = lookup(each.value.extra_attributes, "priority", 1000) + # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) + dynamic "allow" { + for_each = each.value.rules + iterator = rule + content { + protocol = rule.value.protocol + ports = rule.value.ports + } + } +} + +resource "google_compute_firewall" "custom_deny" { + # provider = "google-beta" + for_each = local.rules-deny + name = each.key + description = each.value.description + direction = each.value.direction + network = var.network + project = var.project_id + source_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null + destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null + source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources + source_service_accounts = each.value.use_service_accounts && each.value.direction == "INGRESS" ? each.value.sources : null + target_tags = each.value.use_service_accounts ? null : each.value.targets + target_service_accounts = each.value.use_service_accounts ? each.value.targets : null + disabled = lookup(each.value.extra_attributes, "disabled", false) + priority = lookup(each.value.extra_attributes, "priority", 1000) + # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) + + dynamic "deny" { + for_each = each.value.rules + iterator = rule + content { + protocol = rule.value.protocol + ports = rule.value.ports + } + } +} diff --git a/modules/net-vpc-firewall/outputs.tf b/modules/net-vpc-firewall/outputs.tf new file mode 100644 index 0000000000..0819e9d118 --- /dev/null +++ b/modules/net-vpc-firewall/outputs.tf @@ -0,0 +1,56 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "admin_ranges" { + description = "Admin ranges data." + + value = { + enabled = var.admin_ranges_enabled + ranges = var.admin_ranges_enabled ? join(",", var.admin_ranges) : "" + } +} + +output "custom_ingress_allow_rules" { + description = "Custom ingress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom_allow : + rule.name if rule.direction == "INGRESS" + ] +} + +output "custom_ingress_deny_rules" { + description = "Custom ingress rules with deny blocks." + value = [ + for rule in google_compute_firewall.custom_deny : + rule.name if rule.direction == "INGRESS" + ] +} + +output "custom_egress_allow_rules" { + description = "Custom egress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom_allow : + rule.name if rule.direction == "EGRESS" + ] +} + +output "custom_egress_deny_rules" { + description = "Custom egress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom_deny : + rule.name if rule.direction == "EGRESS" + ] +} diff --git a/modules/net-vpc-firewall/variables.tf b/modules/net-vpc-firewall/variables.tf new file mode 100644 index 0000000000..4c8f4fb5b8 --- /dev/null +++ b/modules/net-vpc-firewall/variables.tf @@ -0,0 +1,74 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "network" { + description = "Name of the network this set of firewall rules applies to." + type = string +} + +variable "project_id" { + description = "Project id of the project that holds the network." + type = string +} + +variable "admin_ranges_enabled" { + description = "Enable admin ranges-based rules." + type = bool + default = false +} + +variable "admin_ranges" { + description = "IP CIDR ranges that have complete access to all subnets." + type = list(string) + default = [] +} + +variable "ssh_source_ranges" { + description = "List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range." + type = list(string) + default = ["35.235.240.0/20"] +} + +variable "http_source_ranges" { + description = "List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges." + type = list(string) + default = ["35.191.0.0/16", "130.211.0.0/22"] +} + +variable "https_source_ranges" { + description = "List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges." + type = list(string) + default = ["35.191.0.0/16", "130.211.0.0/22"] +} + +variable "custom_rules" { + description = "List of custom rule definitions (refer to variables file for syntax)." + type = map(object({ + description = string + direction = string + action = string # (allow|deny) + ranges = list(string) + sources = list(string) + targets = list(string) + use_service_accounts = bool + rules = list(object({ + protocol = string + ports = list(string) + })) + extra_attributes = map(string) + })) + default = {} +} diff --git a/modules/net-vpc-firewall/versions.tf b/modules/net-vpc-firewall/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpc-firewall/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpc-peering/README.md b/modules/net-vpc-peering/README.md new file mode 100644 index 0000000000..f10994383f --- /dev/null +++ b/modules/net-vpc-peering/README.md @@ -0,0 +1,65 @@ +# Google Network Peering + +This module allows creation of a [VPC Network Peering](https://cloud.google.com/vpc/docs/vpc-peering) between two networks. + +The resources created/managed by this module are: + +- one network peering from `local network` to `peer network` +- one network peering from `peer network` to `local network` + +## Usage + +Basic usage of this module is as follows: + +```hcl +module "peering" { + source = "modules/net-vpc-peering" + + prefix = "name-prefix" + local_network = "" + peer_network = "" +} +``` + +If you need to create more than one peering for the same VPC Network `(A -> B, A -> C)` you have to use output from the first module as a dependency for the second one to keep order of peering creation (It is not currently possible to create more than one peering connection for a VPC Network at the same time). + +```hcl +module "peering-a-b" { + source = "modules/net-vpc-peering" + + prefix = "name-prefix" + local_network = "" + peer_network = "" +} + +module "peering-a-c" { + source = "modules/net-vpc-peering" + + prefix = "name-prefix" + local_network = "" + peer_network = "" + + module_depends_on = [module.peering-a-b.complete] +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| local_network | Resource link of the network to add a peering to. | string | ✓ | | +| peer_network | Resource link of the peer network. | string | ✓ | | +| *export_local_custom_routes* | Export custom routes to peer network from local network. | bool | | false | +| *export_peer_custom_routes* | Export custom routes to local network from peer network. | bool | | false | +| *module_depends_on* | List of modules or resources this module depends on. | list | | [] | +| *prefix* | Name prefix for the network peerings | string | | network-peering | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| complete | Output to be used as a module dependency. | | +| local_network_peering | Network peering resource. | | +| peer_network_peering | Peer network peering resource. | | + \ No newline at end of file diff --git a/modules/net-vpc-peering/main.tf b/modules/net-vpc-peering/main.tf new file mode 100644 index 0000000000..3090505a11 --- /dev/null +++ b/modules/net-vpc-peering/main.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + local_network_name = element(reverse(split("/", var.local_network)), 0) + peer_network_name = element(reverse(split("/", var.peer_network)), 0) +} + +resource "google_compute_network_peering" "local_network_peering" { + name = "${var.prefix}-${local.local_network_name}-${local.peer_network_name}" + network = var.local_network + peer_network = var.peer_network + export_custom_routes = var.export_local_custom_routes + import_custom_routes = var.export_peer_custom_routes + + depends_on = [null_resource.module_depends_on] +} + +resource "google_compute_network_peering" "peer_network_peering" { + name = "${var.prefix}-${local.peer_network_name}-${local.local_network_name}" + network = var.peer_network + peer_network = var.local_network + export_custom_routes = var.export_peer_custom_routes + import_custom_routes = var.export_local_custom_routes + + depends_on = [null_resource.module_depends_on, google_compute_network_peering.local_network_peering] +} + +resource "null_resource" "module_depends_on" { + triggers = { + value = length(var.module_depends_on) + } +} + +resource "null_resource" "complete" { + depends_on = [google_compute_network_peering.local_network_peering, google_compute_network_peering.peer_network_peering] +} diff --git a/modules/net-vpc-peering/outputs.tf b/modules/net-vpc-peering/outputs.tf new file mode 100644 index 0000000000..92bdc536ab --- /dev/null +++ b/modules/net-vpc-peering/outputs.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "local_network_peering" { + description = "Network peering resource." + value = google_compute_network_peering.local_network_peering +} + +output "peer_network_peering" { + description = "Peer network peering resource." + value = google_compute_network_peering.peer_network_peering +} + +output "complete" { + description = "Output to be used as a module dependency." + value = null_resource.complete.id +} diff --git a/modules/net-vpc-peering/variables.tf b/modules/net-vpc-peering/variables.tf new file mode 100644 index 0000000000..4cd255a9d3 --- /dev/null +++ b/modules/net-vpc-peering/variables.tf @@ -0,0 +1,49 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "prefix" { + description = "Name prefix for the network peerings" + type = string + default = "network-peering" +} + +variable "local_network" { + description = "Resource link of the network to add a peering to." + type = string +} + +variable "peer_network" { + description = "Resource link of the peer network." + type = string +} + +variable "export_peer_custom_routes" { + description = "Export custom routes to local network from peer network." + type = bool + default = false +} + +variable "export_local_custom_routes" { + description = "Export custom routes to peer network from local network." + type = bool + default = false +} + +variable "module_depends_on" { + description = "List of modules or resources this module depends on." + type = list + default = [] +} diff --git a/modules/net-vpc-peering/versions.tf b/modules/net-vpc-peering/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpc-peering/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md new file mode 100644 index 0000000000..0826502527 --- /dev/null +++ b/modules/net-vpc/README.md @@ -0,0 +1,141 @@ +# Minimalistic VPC module + +This module allows creation and management of VPC networks including subnetworks and subnetwork IAM bindings, Shared VPC activation and service project registration, and one-to-one peering. + +## Examples + +The module allows for several different VPC configurations, some of the most common are shown below. + +### Simple VPC + +```hcl +module "vpc" { + source = "../modules/net-vpc" + project_id = "my-project" + name = "my-network" + subnets = { + subnet-1 = { + ip_cidr_range = "10.0.0.0/24" + region = "europe-west1" + secondary_ip_range = { + pods = "172.16.0.0/20" + services = "192.168.0.0/24" + } + } + subnet-2 = { + ip_cidr_range = "10.0.16.0/24" + region = "europe-west1" + secondary_ip_range = {} + } + } +} +``` + +### Peering + +A single peering can be configured for the VPC, so as to allow management of simple scenarios, and more complex configurations like hub and spoke by defining the peering configuration on the spoke VPCs. Care must be taken so as a single peering is created/changed/destroyed at a time, due to the specific behaviour of the peering API calls. + +```hcl +module "vpc-spoke-1" { + source = "../modules/net-vpc" + project_id = "my-project" + name = "my-network" + subnets = { + subnet-1 = { + ip_cidr_range = "10.0.0.0/24" + region = "europe-west1" + secondary_ip_range = { + pods = "172.16.0.0/20" + services = "192.168.0.0/24" + } + } + } + peering_config = { + peer_vpc_self_link = module.vpc-hub.self_link + export_routes = false + import_routes = true + } +} +``` + +### Shared VPC + +```hcl +module "vpc-host" { + source = "../modules/net-vpc" + project_id = "my-project" + name = "my-host-network" + subnets = { + subnet-1 = { + ip_cidr_range = "10.0.0.0/24" + region = "europe-west1" + secondary_ip_range = { + pods = "172.16.0.0/20" + services = "192.168.0.0/24" + } + } + } + shared_vpc_host = true + shared_vpc_service_projects = [ + local.service_project_1.project_id, + local.service_project_2.project_id + ] + iam_roles = { + subnet-1 = [ + "roles/compute.networkUser", + "roles/compute.securityAdmin" + ] + } + iam_members = { + subnet-1 = { + "roles/compute.networkUser" = [ + local.service_project_1.cloudsvc_sa, + local.service_project_1.gke_sa + ] + "roles/compute.securityAdmin" = [ + local.service_project_1.gke_sa + ] + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | The name of the network being created | string | ✓ | | +| project_id | The ID of the project where this VPC will be created | string | ✓ | | +| *auto_create_subnetworks* | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | +| *description* | An optional description of this resource (triggers recreation on change). | string | | Terraform-managed. | +| *iam_members* | List of IAM members keyed by subnet and role. | map(map(list(string))) | | null | +| *iam_roles* | List of IAM roles keyed by subnet. | map(list(string)) | | null | +| *log_config_defaults* | Default configuration for flow logs when enabled. | object({...}) | | ... | +| *log_configs* | Map of per-subnet optional configurations for flow logs when enabled. | map(map(string)) | | null | +| *peering_config* | VPC peering configuration. | object({...}) | | null | +| *routes* | Network routes, keyed by name. | map(object({...})) | | null | +| *routing_mode* | The network routing mode (default 'GLOBAL') | string | | GLOBAL | +| *shared_vpc_host* | Enable shared VPC for this project. | bool | | false | +| *shared_vpc_service_projects* | Shared VPC service projects to register with this host | list(string) | | [] | +| *subnet_descriptions* | Optional map of subnet descriptions, keyed by subnet name. | map(string) | | {} | +| *subnet_flow_logs* | Optional map of boolean to control flow logs (default is disabled), keyed by subnet name. | map(bool) | | {} | +| *subnet_private_access* | Optional map of boolean to control private Google access (default is enabled), keyed by subnet name. | map(bool) | | {} | +| *subnets* | The list of subnets being created | map(object({...})) | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| bindings | Subnet IAM bindings. | | +| name | The name of the VPC being created. | | +| network | Network resource. | | +| project_id | Shared VPC host project id. | | +| self_link | The URI of the VPC being created. | | +| subnet_ips | Map of subnet address ranges keyed by name. | | +| subnet_regions | Map of subnet regions keyed by name. | | +| subnet_secondary_ranges | Map of subnet secondary ranges keyed by name. | | +| subnet_self_links | Map of subnet self links keyed by name. | | +| subnets | Subnet resources. | | + + diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf new file mode 100644 index 0000000000..f94cc59546 --- /dev/null +++ b/modules/net-vpc/main.tf @@ -0,0 +1,207 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + iam_members = var.iam_members == null ? {} : var.iam_members + iam_pairs = var.iam_roles == null ? [] : flatten([ + for subnet, roles in var.iam_roles : + [for role in roles : { subnet = subnet, role = role }] + ]) + iam_keypairs = { + for pair in local.iam_pairs : + "${pair.subnet}-${pair.role}" => pair + } + log_configs = var.log_configs == null ? {} : var.log_configs + peer_network = ( + var.peering_config == null + ? null + : element(reverse(split("/", var.peering_config.peer_vpc_self_link)), 0) + ) + routes = var.routes == null ? {} : var.routes + routes_gateway = { + for name, data in local.routes : + name => data if data.next_hop_type == "gateway" + } + routes_ilb = { + for name, data in local.routes : + name => data if data.next_hop_type == "ilb" + } + routes_instance = { + for name, data in local.routes : + name => data if data.next_hop_type == "instance" + } + routes_ip = { + for name, data in local.routes : + name => data if data.next_hop_type == "ip" + } + routes_vpn_tunnel = { + for name, data in local.routes : + name => data if data.next_hop_type == "vpn_tunnel" + } + subnet_log_configs = { + for name, attrs in local.subnets : name => ( + lookup(var.subnet_flow_logs, name, false) + ? [{ + for key, value in var.log_config_defaults : key => lookup( + lookup(local.log_configs, name, {}), key, value + ) + }] + : [] + ) + } + subnets = var.subnets == null ? {} : var.subnets +} + +resource "google_compute_network" "network" { + project = var.project_id + name = var.name + description = var.description + auto_create_subnetworks = var.auto_create_subnetworks + routing_mode = var.routing_mode +} + +resource "google_compute_network_peering" "local" { + provider = google-beta + count = var.peering_config == null ? 0 : 1 + name = "${google_compute_network.network.name}-${local.peer_network}" + network = google_compute_network.network.self_link + peer_network = var.peering_config.peer_vpc_self_link + export_custom_routes = var.peering_config.export_routes + import_custom_routes = var.peering_config.import_routes +} + +resource "google_compute_network_peering" "remote" { + provider = google-beta + count = var.peering_config == null ? 0 : 1 + name = "${local.peer_network}-${google_compute_network.network.name}" + network = var.peering_config.peer_vpc_self_link + peer_network = google_compute_network.network.self_link + export_custom_routes = var.peering_config.import_routes + import_custom_routes = var.peering_config.export_routes + depends_on = [google_compute_network_peering.local] +} + +resource "google_compute_shared_vpc_host_project" "shared_vpc_host" { + count = var.shared_vpc_host ? 1 : 0 + project = var.project_id + depends_on = [google_compute_network.network] +} + +resource "google_compute_shared_vpc_service_project" "service_projects" { + for_each = ( + var.shared_vpc_host && var.shared_vpc_service_projects != null + ? toset(var.shared_vpc_service_projects) + : toset([]) + ) + host_project = var.project_id + service_project = each.value + depends_on = [google_compute_shared_vpc_host_project.shared_vpc_host] +} + +resource "google_compute_subnetwork" "subnetwork" { + for_each = local.subnets + project = var.project_id + network = google_compute_network.network.name + region = each.value.region + name = "${var.name}-${each.key}" + ip_cidr_range = each.value.ip_cidr_range + secondary_ip_range = each.value.secondary_ip_range == null ? [] : [ + for name, range in each.value.secondary_ip_range : + { range_name = name, ip_cidr_range = range } + ] + description = lookup(var.subnet_descriptions, each.key, "Terraform-managed.") + private_ip_google_access = lookup(var.subnet_private_access, each.key, true) + dynamic "log_config" { + for_each = local.subnet_log_configs[each.key] + iterator = config + content { + aggregation_interval = config.value.aggregation_interval + flow_sampling = config.value.flow_sampling + metadata = config.value.metadata + } + } +} + +resource "google_compute_subnetwork_iam_binding" "binding" { + for_each = local.iam_keypairs + project = var.project_id + subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name + region = google_compute_subnetwork.subnetwork[each.value.subnet].region + role = each.value.role + members = lookup( + lookup(local.iam_members, each.value.subnet, {}), each.value.role, [] + ) +} + +resource "google_compute_route" "gateway" { + for_each = local.routes_gateway + project = var.project_id + network = google_compute_network.network.name + name = each.key + description = "Terraform-managed." + dest_range = each.value.dest_range + priority = each.value.priority + tags = each.value.tags + next_hop_gateway = each.value.next_hop +} + +resource "google_compute_route" "ilb" { + for_each = local.routes_ilb + project = var.project_id + network = google_compute_network.network.name + name = each.key + description = "Terraform-managed." + dest_range = each.value.dest_range + priority = each.value.priority + tags = each.value.tags + next_hop_ilb = each.value.next_hop +} + +resource "google_compute_route" "instance" { + for_each = local.routes_instance + project = var.project_id + network = google_compute_network.network.name + name = each.key + description = "Terraform-managed." + dest_range = each.value.dest_range + priority = each.value.priority + tags = each.value.tags + next_hop_instance = each.value.next_hop +} + +resource "google_compute_route" "ip" { + for_each = local.routes_ip + project = var.project_id + network = google_compute_network.network.name + name = each.key + description = "Terraform-managed." + dest_range = each.value.dest_range + priority = each.value.priority + tags = each.value.tags + next_hop_ip = each.value.next_hop +} + +resource "google_compute_route" "vpn_tunnel" { + for_each = local.routes_vpn_tunnel + project = var.project_id + network = google_compute_network.network.name + name = each.key + description = "Terraform-managed." + dest_range = each.value.dest_range + priority = each.value.priority + tags = each.value.tags + next_hop_vpn_tunnel = each.value.next_hop +} diff --git a/modules/net-vpc/outputs.tf b/modules/net-vpc/outputs.tf new file mode 100644 index 0000000000..64649135e8 --- /dev/null +++ b/modules/net-vpc/outputs.tf @@ -0,0 +1,80 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "network" { + description = "Network resource." + value = google_compute_network.network +} + +output "name" { + description = "The name of the VPC being created." + value = google_compute_network.network.name +} + +output "self_link" { + description = "The URI of the VPC being created." + value = google_compute_network.network.self_link +} + +output "project_id" { + description = "Shared VPC host project id." + value = ( + var.shared_vpc_host + ? google_compute_shared_vpc_host_project.shared_vpc_host[*].project + : null + ) + depends_on = [ + google_compute_shared_vpc_host_project.shared_vpc_host, + google_compute_shared_vpc_service_project.service_projects + ] +} + +# TODO(ludoo): use input names as keys +output "subnets" { + description = "Subnet resources." + value = { for k, v in google_compute_subnetwork.subnetwork : k => v } +} + +output "subnet_ips" { + description = "Map of subnet address ranges keyed by name." + value = { for k, v in google_compute_subnetwork.subnetwork : k => v.ip_cidr_range } +} + +output "subnet_self_links" { + description = "Map of subnet self links keyed by name." + value = { for k, v in google_compute_subnetwork.subnetwork : k => v.self_link } +} + +output "subnet_regions" { + description = "Map of subnet regions keyed by name." + value = { for k, v in google_compute_subnetwork.subnetwork : k => v.region } +} + +output "subnet_secondary_ranges" { + description = "Map of subnet secondary ranges keyed by name." + value = { + for k, v in google_compute_subnetwork.subnetwork : + k => { + for range in v.secondary_ip_range : + range.range_name => range.ip_cidr_range + } + } +} + +output "bindings" { + description = "Subnet IAM bindings." + value = { for k, v in google_compute_subnetwork_iam_binding.binding : k => v } +} diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf new file mode 100644 index 0000000000..1f45132118 --- /dev/null +++ b/modules/net-vpc/variables.tf @@ -0,0 +1,137 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "auto_create_subnetworks" { + description = "Set to true to create an auto mode subnet, defaults to custom mode." + type = bool + default = false +} + +variable "description" { + description = "An optional description of this resource (triggers recreation on change)." + type = string + default = "Terraform-managed." +} + +variable "iam_roles" { + description = "List of IAM roles keyed by subnet." + type = map(list(string)) + default = null +} + +variable "iam_members" { + description = "List of IAM members keyed by subnet and role." + type = map(map(list(string))) + default = null +} + +variable "log_configs" { + description = "Map of per-subnet optional configurations for flow logs when enabled." + type = map(map(string)) + default = null +} + +variable "log_config_defaults" { + description = "Default configuration for flow logs when enabled." + type = object({ + aggregation_interval = string + flow_sampling = number + metadata = string + }) + default = { + aggregation_interval = "INTERVAL_5_SEC" + flow_sampling = 0.5 + metadata = "INCLUDE_ALL_METADATA" + } +} + +variable "name" { + description = "The name of the network being created" + type = string +} + +variable "peering_config" { + description = "VPC peering configuration." + type = object({ + peer_vpc_self_link = string + export_routes = bool + import_routes = bool + }) + default = null +} + +variable "project_id" { + description = "The ID of the project where this VPC will be created" + type = string +} + +variable "routes" { + description = "Network routes, keyed by name." + type = map(object({ + dest_range = string + priority = number + tags = list(string) + next_hop_type = string # gateway, instance, ip, vpn_tunnel, ilb + next_hop = string + })) + default = null +} + +variable "routing_mode" { + description = "The network routing mode (default 'GLOBAL')" + type = string + default = "GLOBAL" +} + +variable "shared_vpc_host" { + description = "Enable shared VPC for this project." + type = bool + default = false +} + +variable "shared_vpc_service_projects" { + description = "Shared VPC service projects to register with this host" + type = list(string) + default = [] +} + +variable "subnets" { + description = "The list of subnets being created" + type = map(object({ + ip_cidr_range = string + region = string + secondary_ip_range = map(string) + })) + default = null +} + +variable "subnet_descriptions" { + description = "Optional map of subnet descriptions, keyed by subnet name." + type = map(string) + default = {} +} + +variable "subnet_flow_logs" { + description = "Optional map of boolean to control flow logs (default is disabled), keyed by subnet name." + type = map(bool) + default = {} +} + +variable "subnet_private_access" { + description = "Optional map of boolean to control private Google access (default is enabled), keyed by subnet name." + type = map(bool) + default = {} +} diff --git a/modules/net-vpc/versions.tf b/modules/net-vpc/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpc/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpn-dynamic/README.md b/modules/net-vpn-dynamic/README.md new file mode 100644 index 0000000000..218af8b140 --- /dev/null +++ b/modules/net-vpn-dynamic/README.md @@ -0,0 +1,72 @@ +# Cloud VPN Dynamic Module + +## Example + +This example shows how to configure a single VPN tunnel using a couple of extra features + +- custom advertisement on the tunnel's BGP session; if custom advertisement is not needed, simply set the `bgp_peer_options` attribute to `null` +- internally generated shared secret, which can be fetched from the module's `random_secret` output for reuse; a predefined secret can be used instead by assigning it to the `shared_secret` attribute + +```hcl +module "vpn-dynamic" { + source = "./modules/net-vpn-dynamic" + project_id = "my-project" + region = "europe-west1" + network = "my-vpc" + name = "gateway-1" + tunnels = { + remote-1 = { + bgp_peer = { + address = "169.254.139.134" + asn = 64513 + } + bgp_session_range = "169.254.139.133/30" + ike_version = 2 + peer_ip = var.remote_vpn_gateway.address + shared_secret = null + bgp_peer_options = { + advertise_groups = ["ALL_SUBNETS"] + advertise_ip_ranges = { + "192.168.0.0/24" = "Advertised range description" + } + advertise_mode = "CUSTOM" + route_priority = 1000 + } + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| *gateway_address* | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | | +| *gateway_address_create* | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | +| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | +| *router_advertise_config* | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({...}) | | null | +| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | +| *router_create* | Create router. | bool | | true | +| *router_name* | Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router. | string | | | +| *tunnels* | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({...})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| address | VPN gateway address. | | +| gateway | VPN gateway resource. | | +| name | VPN gateway name. | | +| random_secret | Generated secret. | ✓ | +| router | Router resource (only if auto-created). | | +| router_name | Router name. | | +| self_link | VPN gateway self link. | | +| tunnel_names | VPN tunnel names. | | +| tunnel_self_links | VPN tunnel self links. | | +| tunnels | VPN tunnel resources. | | + diff --git a/modules/net-vpn-dynamic/main.tf b/modules/net-vpn-dynamic/main.tf new file mode 100644 index 0000000000..368b3abe03 --- /dev/null +++ b/modules/net-vpn-dynamic/main.tf @@ -0,0 +1,182 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + gateway_address = ( + var.gateway_address_create + ? google_compute_address.gateway[0].address + : var.gateway_address + ) + router = ( + var.router_create + ? google_compute_router.router[0].name + : var.router_name + ) + secret = random_id.secret.b64_url +} + +resource "google_compute_address" "gateway" { + count = var.gateway_address_create ? 1 : 0 + name = "vpn-${var.name}" + project = var.project_id + region = var.region +} + +resource "google_compute_forwarding_rule" "esp" { + name = "vpn-${var.name}-esp" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "ESP" +} + +resource "google_compute_forwarding_rule" "udp-500" { + name = "vpn-${var.name}-udp-500" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "UDP" + port_range = "500" +} + +resource "google_compute_forwarding_rule" "udp-4500" { + name = "vpn-${var.name}-udp-4500" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "UDP" + port_range = "4500" +} + +resource "google_compute_router" "router" { + count = var.router_create ? 1 : 0 + name = var.router_name == "" ? "vpn-${var.name}" : var.router_name + project = var.project_id + region = var.region + network = var.network + bgp { + advertise_mode = ( + var.router_advertise_config == null + ? null + : var.router_advertise_config.mode + ) + advertised_groups = ( + var.router_advertise_config == null ? null : ( + var.router_advertise_config.mode != "CUSTOM" + ? null + : var.router_advertise_config.groups + ) + ) + dynamic advertised_ip_ranges { + for_each = ( + var.router_advertise_config == null ? {} : ( + var.router_advertise_config.mode != "CUSTOM" + ? null + : var.router_advertise_config.ip_ranges + ) + ) + iterator = range + content { + range = range.key + description = range.value + } + } + asn = var.router_asn + } +} + +resource "google_compute_router_peer" "bgp_peer" { + for_each = var.tunnels + region = var.region + project = var.project_id + name = "${var.name}-${each.key}" + router = local.router + peer_ip_address = each.value.bgp_peer.address + peer_asn = each.value.bgp_peer.asn + advertised_route_priority = ( + each.value.bgp_peer_options == null ? var.route_priority : ( + each.value.bgp_peer_options.route_priority == null + ? var.route_priority + : each.value.bgp_peer_options.route_priority + ) + ) + advertise_mode = ( + each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.advertise_mode + ) + advertised_groups = ( + each.value.bgp_peer_options == null ? null : ( + each.value.bgp_peer_options.advertise_mode != "CUSTOM" + ? null + : each.value.bgp_peer_options.advertise_groups + ) + ) + dynamic advertised_ip_ranges { + for_each = ( + each.value.bgp_peer_options == null ? {} : ( + each.value.bgp_peer_options.advertise_mode != "CUSTOM" + ? {} + : each.value.bgp_peer_options.advertise_ip_ranges + ) + ) + iterator = range + content { + range = range.key + description = range.value + } + } + interface = google_compute_router_interface.router_interface[each.key].name +} + +resource "google_compute_router_interface" "router_interface" { + for_each = var.tunnels + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + router = local.router + ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range + vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name +} + +resource "google_compute_vpn_gateway" "gateway" { + name = var.name + project = var.project_id + region = var.region + network = var.network +} + +resource "google_compute_vpn_tunnel" "tunnels" { + for_each = var.tunnels + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + router = local.router + peer_ip = each.value.peer_ip + ike_version = each.value.ike_version + shared_secret = ( + each.value.shared_secret == "" || each.value.shared_secret == null + ? local.secret + : each.value.shared_secret + ) + target_vpn_gateway = google_compute_vpn_gateway.gateway.self_link + depends_on = [google_compute_forwarding_rule.esp] +} + +resource "random_id" "secret" { + byte_length = 8 +} diff --git a/modules/net-vpn-dynamic/outputs.tf b/modules/net-vpn-dynamic/outputs.tf new file mode 100644 index 0000000000..78e435f3f6 --- /dev/null +++ b/modules/net-vpn-dynamic/outputs.tf @@ -0,0 +1,75 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "address" { + description = "VPN gateway address." + value = local.gateway_address +} + +output "gateway" { + description = "VPN gateway resource." + value = google_compute_vpn_gateway.gateway +} + +output "name" { + description = "VPN gateway name." + value = google_compute_vpn_gateway.gateway.name +} + +output "router" { + description = "Router resource (only if auto-created)." + value = var.router_create ? google_compute_router.router[0] : null +} + +output "router_name" { + description = "Router name." + value = local.router +} + +output "self_link" { + description = "VPN gateway self link." + value = google_compute_vpn_gateway.gateway.self_link +} + +output "tunnels" { + description = "VPN tunnel resources." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name] + } +} + +output "tunnel_names" { + description = "VPN tunnel names." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].name + } +} + +output "tunnel_self_links" { + description = "VPN tunnel self links." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].self_link + } +} + +output "random_secret" { + description = "Generated secret." + sensitive = true + value = local.secret +} diff --git a/modules/net-vpn-dynamic/variables.tf b/modules/net-vpn-dynamic/variables.tf new file mode 100644 index 0000000000..eea77c00bf --- /dev/null +++ b/modules/net-vpn-dynamic/variables.tf @@ -0,0 +1,104 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "gateway_address_create" { + description = "Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable." + type = bool + default = true +} + +variable "gateway_address" { + description = "Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false." + type = string + default = "" +} + +variable "name" { + description = "VPN gateway name, and prefix used for dependent resources." + type = string +} + +variable "network" { + description = "VPC used for the gateway and routes." + type = string +} + +variable "project_id" { + description = "Project where resources will be created." + type = string +} + +variable "region" { + description = "Region used for resources." + type = string +} + +variable "route_priority" { + description = "Route priority, defaults to 1000." + type = number + default = 1000 +} + +variable "router_advertise_config" { + description = "Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions." + type = object({ + groups = list(string) + ip_ranges = map(string) + mode = string + }) + default = null +} + +variable "router_asn" { + description = "Router ASN used for auto-created router." + type = number + default = 64514 +} + +variable "router_create" { + description = "Create router." + type = bool + default = true +} + +variable "router_name" { + description = "Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router." + type = string + default = "" +} + +variable "tunnels" { + description = "VPN tunnel configurations, bgp_peer_options is usually null." + type = map(object({ + bgp_peer = object({ + address = string + asn = number + }) + bgp_peer_options = object({ + advertise_groups = list(string) + advertise_ip_ranges = map(string) + advertise_mode = string + route_priority = number + }) + # each BGP session on the same Cloud Router must use a unique /30 CIDR + # from the 169.254.0.0/16 block. + bgp_session_range = string + ike_version = number + peer_ip = string + shared_secret = string + })) + default = {} +} diff --git a/modules/net-vpn-dynamic/versions.tf b/modules/net-vpn-dynamic/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpn-dynamic/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpn-ha/README.md b/modules/net-vpn-ha/README.md new file mode 100644 index 0000000000..d5fffa6350 --- /dev/null +++ b/modules/net-vpn-ha/README.md @@ -0,0 +1,159 @@ +# Cloud VPN HA Module +This module makes it easy to deploy either GCP-to-GCP or GCP-to-On-prem [Cloud HA VPN](https://cloud.google.com/vpn/docs/concepts/overview#ha-vpn). + +## Examples + +### GCP to GCP +```hcl +module "vpn_ha-1" { + source = "../modules/net-vpn-ha" + project_id = "" + region = "europe-west4" + network = "https://www.googleapis.com/compute/v1/projects//global/networks/network-1" + name = "net1-to-net-2" + peer_gcp_gateway = module.vpn_ha-2.self_link + router_asn = 64514 + tunnels = { + remote-0 = { + bgp_peer = { + address = "169.254.1.1" + asn = 64513 + } + bgp_peer_options = null + bgp_session_range = "169.254.1.2/30" + ike_version = 2 + vpn_gateway_interface = 0 + peer_external_gateway_interface = null + shared_secret = "" + } + remote-1 = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_peer_options = null + bgp_session_range = "169.254.2.2/30" + ike_version = 2 + vpn_gateway_interface = 1 + peer_external_gateway_interface = null + shared_secret = "" + } + } +} + +module "vpn_ha-2" { + source = "../modules/net-vpn-ha" + project_id = "" + region = "europe-west4" + network = "https://www.googleapis.com/compute/v1/projects//global/networks/local-network" + name = "net2-to-net1" + router_asn = 64513 + peer_gcp_gateway = module.vpn_ha-1.self_link + tunnels = { + remote-0 = { + bgp_peer = { + address = "169.254.1.2" + asn = 64514 + } + bgp_peer_options = null + bgp_session_range = "169.254.1.1/30" + ike_version = 2 + vpn_gateway_interface = 0 + peer_external_gateway_interface = null + shared_secret = module.vpn_ha-1.random_secret + } + remote-1 = { + bgp_peer = { + address = "169.254.2.2" + asn = 64514 + } + bgp_peer_options = null + bgp_session_range = "169.254.2.1/30" + ike_version = 2 + vpn_gateway_interface = 1 + peer_external_gateway_interface = null + shared_secret = module.vpn_ha-1.random_secret + } + } +} +``` +### GCP to on-prem + +``` +module "vpn_ha" { + source = "../modules/net-vpn-ha" + project_id = "" + region = "europe-west4" + network = "https://www.googleapis.com/compute/v1/projects//global/networks/my-network" + name = "mynet-to-onprem" + peer_external_gateway = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = [{ + id = 0 + ip_address = "8.8.8.8" # on-prem router ip address + + }] + } + router_asn = 64514 + tunnels = { + remote-0 = { + bgp_peer = { + address = "169.254.1.1" + asn = 64513 + } + bgp_peer_options = null + bgp_session_range = "169.254.1.2/30" + ike_version = 2 + vpn_gateway_interface = 0 + peer_external_gateway_interface = 0 + shared_secret = "mySecret" + } + remote-1 = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_peer_options = null + bgp_session_range = "169.254.2.2/30" + ike_version = 2 + vpn_gateway_interface = 1 + peer_external_gateway_interface = 0 + shared_secret = "mySecret" + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| *peer_external_gateway* | Configuration of an external VPN gateway to which this VPN is connected. | object({...}) | | null | +| *peer_gcp_gateway* | Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected. | string | | null | +| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | +| *router_advertise_config* | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | object({...}) | | null | +| *router_asn* | Router ASN used for auto-created router. | number | | 64514 | +| *router_create* | Create router. | bool | | true | +| *router_name* | Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router. | string | | | +| *tunnels* | VPN tunnel configurations, bgp_peer_options is usually null. | map(object({...})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| external_gateway | External VPN gateway resource. | | +| gateway | HA VPN gateway resource. | | +| name | VPN gateway name. | | +| random_secret | Generated secret. | ✓ | +| router | Router resource (only if auto-created). | | +| router_name | Router name. | | +| self_link | HA VPN gateway self link. | | +| tunnel_names | VPN tunnel names. | | +| tunnel_self_links | VPN tunnel self links. | | +| tunnels | VPN tunnel resources. | | + \ No newline at end of file diff --git a/modules/net-vpn-ha/main.tf b/modules/net-vpn-ha/main.tf new file mode 100644 index 0000000000..49055b1b5f --- /dev/null +++ b/modules/net-vpn-ha/main.tf @@ -0,0 +1,169 @@ + +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + peer_external_gateway = ( + var.peer_external_gateway != null + ? google_compute_external_vpn_gateway.external_gateway[0].self_link + : null + + ) + router = ( + var.router_create + ? google_compute_router.router[0].name + : var.router_name + ) + secret = random_id.secret.b64_url +} + +resource "google_compute_ha_vpn_gateway" "ha_gateway" { + provider = google-beta + name = var.name + project = var.project_id + region = var.region + network = var.network +} + +resource "google_compute_external_vpn_gateway" "external_gateway" { + provider = google-beta + count = var.peer_external_gateway != null ? 1 : 0 + name = "external-${var.name}" + redundancy_type = var.peer_external_gateway.redundancy_type + description = "Terraform managed external VPN gateway" + dynamic "interface" { + for_each = var.peer_external_gateway.interfaces + content { + id = interface.value.id + ip_address = interface.value.ip_address + } + } +} + +resource "google_compute_router" "router" { + provider = google-beta + count = var.router_create ? 1 : 0 + name = var.router_name == "" ? "vpn-${var.name}" : var.router_name + project = var.project_id + region = var.region + network = var.network + bgp { + advertise_mode = ( + var.router_advertise_config == null + ? null + : var.router_advertise_config.mode + ) + advertised_groups = ( + var.router_advertise_config == null ? null : ( + var.router_advertise_config.mode != "CUSTOM" + ? null + : var.router_advertise_config.groups + ) + ) + dynamic advertised_ip_ranges { + for_each = ( + var.router_advertise_config == null ? {} : ( + var.router_advertise_config.mode != "CUSTOM" + ? null + : var.router_advertise_config.ip_ranges + ) + ) + iterator = range + content { + range = range.key + description = range.value + } + } + asn = var.router_asn + } +} + +resource "google_compute_router_peer" "bgp_peer" { + for_each = var.tunnels + region = var.region + project = var.project_id + name = "${var.name}-${each.key}" + router = local.router + peer_ip_address = each.value.bgp_peer.address + peer_asn = each.value.bgp_peer.asn + advertised_route_priority = ( + each.value.bgp_peer_options == null ? var.route_priority : ( + each.value.bgp_peer_options.route_priority == null + ? var.route_priority + : each.value.bgp_peer_options.route_priority + ) + ) + advertise_mode = ( + each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.advertise_mode + ) + advertised_groups = ( + each.value.bgp_peer_options == null ? null : ( + each.value.bgp_peer_options.advertise_mode != "CUSTOM" + ? null + : each.value.bgp_peer_options.advertise_groups + ) + ) + dynamic advertised_ip_ranges { + for_each = ( + each.value.bgp_peer_options == null ? {} : ( + each.value.bgp_peer_options.advertise_mode != "CUSTOM" + ? {} + : each.value.bgp_peer_options.advertise_ip_ranges + ) + ) + iterator = range + content { + range = range.key + description = range.value + } + } + interface = google_compute_router_interface.router_interface[each.key].name +} + +resource "google_compute_router_interface" "router_interface" { + provider = google-beta + for_each = var.tunnels + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + router = local.router + ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range + vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name +} + +resource "google_compute_vpn_tunnel" "tunnels" { + provider = google-beta + for_each = var.tunnels + project = var.project_id + region = var.region + name = "${var.name}-${each.key}" + router = local.router + peer_external_gateway = local.peer_external_gateway + peer_external_gateway_interface = each.value.peer_external_gateway_interface + peer_gcp_gateway = var.peer_gcp_gateway + vpn_gateway_interface = each.value.vpn_gateway_interface + ike_version = each.value.ike_version + shared_secret = ( + each.value.shared_secret == "" || each.value.shared_secret == null + ? local.secret + : each.value.shared_secret + ) + vpn_gateway = google_compute_ha_vpn_gateway.ha_gateway.self_link +} + +resource "random_id" "secret" { + byte_length = 8 +} diff --git a/modules/net-vpn-ha/outputs.tf b/modules/net-vpn-ha/outputs.tf new file mode 100644 index 0000000000..2a5388900e --- /dev/null +++ b/modules/net-vpn-ha/outputs.tf @@ -0,0 +1,80 @@ + +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "gateway" { + description = "HA VPN gateway resource." + value = google_compute_ha_vpn_gateway.ha_gateway +} + +output "external_gateway" { + description = "External VPN gateway resource." + value = ( + var.peer_external_gateway != null + ? google_compute_external_vpn_gateway.external_gateway[0] + : null + ) +} + +output "name" { + description = "VPN gateway name." + value = google_compute_ha_vpn_gateway.ha_gateway.name +} + +output "router" { + description = "Router resource (only if auto-created)." + value = var.router_name == "" ? google_compute_router.router[0] : null +} + +output "router_name" { + description = "Router name." + value = local.router +} + +output "self_link" { + description = "HA VPN gateway self link." + value = google_compute_ha_vpn_gateway.ha_gateway.self_link +} + +output "tunnels" { + description = "VPN tunnel resources." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name] + } +} + +output "tunnel_names" { + description = "VPN tunnel names." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].name + } +} + +output "tunnel_self_links" { + description = "VPN tunnel self links." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].self_link + } +} + +output "random_secret" { + description = "Generated secret." + sensitive = true + value = local.secret +} diff --git a/modules/net-vpn-ha/variables.tf b/modules/net-vpn-ha/variables.tf new file mode 100644 index 0000000000..55f4ec8999 --- /dev/null +++ b/modules/net-vpn-ha/variables.tf @@ -0,0 +1,111 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "name" { + description = "VPN gateway name, and prefix used for dependent resources." + type = string +} + +variable "network" { + description = "VPC used for the gateway and routes." + type = string +} + +variable "peer_external_gateway" { + description = "Configuration of an external VPN gateway to which this VPN is connected." + type = object({ + redundancy_type = string + interfaces = list(object({ + id = number + ip_address = string + })) + }) + default = null +} + +variable "peer_gcp_gateway" { + description = "Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected." + type = string + default = null +} + +variable "project_id" { + description = "Project where resources will be created." + type = string +} + +variable "region" { + description = "Region used for resources." + type = string +} + +variable "route_priority" { + description = "Route priority, defaults to 1000." + type = number + default = 1000 +} + +variable "router_advertise_config" { + description = "Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions." + type = object({ + groups = list(string) + ip_ranges = map(string) + mode = string + }) + default = null +} + +variable "router_asn" { + description = "Router ASN used for auto-created router." + type = number + default = 64514 +} + +variable "router_create" { + description = "Create router." + type = bool + default = true +} + +variable "router_name" { + description = "Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router." + type = string + default = "" +} + +variable "tunnels" { + description = "VPN tunnel configurations, bgp_peer_options is usually null." + type = map(object({ + bgp_peer = object({ + address = string + asn = number + }) + bgp_peer_options = object({ + advertise_groups = list(string) + advertise_ip_ranges = map(string) + advertise_mode = string + route_priority = number + }) + # each BGP session on the same Cloud Router must use a unique /30 CIDR + # from the 169.254.0.0/16 block. + bgp_session_range = string + ike_version = number + vpn_gateway_interface = number + peer_external_gateway_interface = number + shared_secret = string + })) + default = {} +} diff --git a/modules/net-vpn-ha/versions.tf b/modules/net-vpn-ha/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpn-ha/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/net-vpn-static/README.md b/modules/net-vpn-static/README.md new file mode 100644 index 0000000000..5961e88506 --- /dev/null +++ b/modules/net-vpn-static/README.md @@ -0,0 +1,52 @@ +# Cloud VPN Route-based Module + +## Example + +```hcl +module "vpn" { + source = "./modules/net-vpn-static" + project_id = var.project_id + region = var.region + network = var.network + name = "remote" + # gateway_address = var.gateway_address + remote_ranges = [var.remote_ranges] + tunnels = { + remote-0 = { + ike_version = 2 + peer_ip = var.remote_vpn_gateway_address + shared_secret = "" + traffic_selectors = { local = ["0.0.0.0/0"], remote = null } + } + } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | VPN gateway name, and prefix used for dependent resources. | string | ✓ | | +| network | VPC used for the gateway and routes. | string | ✓ | | +| project_id | Project where resources will be created. | string | ✓ | | +| region | Region used for resources. | string | ✓ | | +| *gateway_address* | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | string | | | +| *gateway_address_create* | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | bool | | true | +| *remote_ranges* | Remote IP CIDR ranges. | list(string) | | [] | +| *route_priority* | Route priority, defaults to 1000. | number | | 1000 | +| *tunnels* | VPN tunnel configurations. | map(object({...})) | | {} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| address | VPN gateway address. | | +| gateway | VPN gateway resource. | | +| name | VPN gateway name. | | +| random_secret | Generated secret. | ✓ | +| self_link | VPN gateway self link. | | +| tunnel_names | VPN tunnel names. | | +| tunnel_self_links | VPN tunnel self links. | | +| tunnels | VPN tunnel resources. | | + diff --git a/modules/net-vpn-static/main.tf b/modules/net-vpn-static/main.tf new file mode 100644 index 0000000000..559d8d9592 --- /dev/null +++ b/modules/net-vpn-static/main.tf @@ -0,0 +1,101 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 { + gateway_address = ( + var.gateway_address_create + ? google_compute_address.gateway[0].address + : var.gateway_address + ) + route_pairs = { + for pair in setproduct(keys(var.tunnels), var.remote_ranges) : + "${pair[0]}-${join("-", regexall("[0-9]+", pair[1]))}" => { + tunnel = pair[0], range = pair[1] + } + } + secret = random_id.secret.b64_url +} + +resource "google_compute_address" "gateway" { + count = var.gateway_address_create ? 1 : 0 + name = "vpn-${var.name}" + project = var.project_id + region = var.region +} + +resource "google_compute_forwarding_rule" "esp" { + name = "vpn-${var.name}-esp" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "ESP" +} + +resource "google_compute_forwarding_rule" "udp-500" { + name = "vpn-${var.name}-udp-500" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "UDP" + port_range = "500" +} + +resource "google_compute_forwarding_rule" "udp-4500" { + name = "vpn-${var.name}-udp-4500" + project = var.project_id + region = var.region + target = google_compute_vpn_gateway.gateway.self_link + ip_address = local.gateway_address + ip_protocol = "UDP" + port_range = "4500" +} + +resource "google_compute_route" "route" { + for_each = local.route_pairs + name = "vpn-${each.key}" + project = var.project_id + network = var.network + dest_range = each.value.range + priority = var.route_priority + next_hop_vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.value.tunnel].self_link +} + +resource "google_compute_vpn_gateway" "gateway" { + name = var.name + project = var.project_id + region = var.region + network = var.network +} + +resource "google_compute_vpn_tunnel" "tunnels" { + for_each = var.tunnels + name = "${var.name}-${each.key}" + project = var.project_id + region = var.region + peer_ip = each.value.peer_ip + local_traffic_selector = each.value.traffic_selectors.local + remote_traffic_selector = each.value.traffic_selectors.remote + ike_version = each.value.ike_version + shared_secret = each.value.shared_secret == "" ? local.secret : each.value.shared_secret + target_vpn_gateway = google_compute_vpn_gateway.gateway.self_link + depends_on = [google_compute_forwarding_rule.esp] +} + +resource "random_id" "secret" { + byte_length = 8 +} diff --git a/modules/net-vpn-static/outputs.tf b/modules/net-vpn-static/outputs.tf new file mode 100644 index 0000000000..09ecc5ec0b --- /dev/null +++ b/modules/net-vpn-static/outputs.tf @@ -0,0 +1,65 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "address" { + description = "VPN gateway address." + value = local.gateway_address +} + +output "gateway" { + description = "VPN gateway resource." + value = google_compute_vpn_gateway.gateway +} + +output "name" { + description = "VPN gateway name." + value = google_compute_vpn_gateway.gateway.name +} + +output "self_link" { + description = "VPN gateway self link." + value = google_compute_vpn_gateway.gateway.self_link +} + +output "tunnels" { + description = "VPN tunnel resources." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name] + } +} + +output "tunnel_names" { + description = "VPN tunnel names." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].name + } +} + +output "tunnel_self_links" { + description = "VPN tunnel self links." + value = { + for name in keys(var.tunnels) : + name => google_compute_vpn_tunnel.tunnels[name].self_link + } +} + +output "random_secret" { + description = "Generated secret." + sensitive = true + value = local.secret +} diff --git a/modules/net-vpn-static/variables.tf b/modules/net-vpn-static/variables.tf new file mode 100644 index 0000000000..f3cebacc58 --- /dev/null +++ b/modules/net-vpn-static/variables.tf @@ -0,0 +1,73 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "gateway_address_create" { + description = "Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable." + type = bool + default = true +} + +variable "gateway_address" { + description = "Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false." + type = string + default = "" +} + +variable "name" { + description = "VPN gateway name, and prefix used for dependent resources." + type = string +} + +variable "network" { + description = "VPC used for the gateway and routes." + type = string +} + +variable "project_id" { + description = "Project where resources will be created." + type = string +} + +variable "region" { + description = "Region used for resources." + type = string +} + +variable "remote_ranges" { + description = "Remote IP CIDR ranges." + type = list(string) + default = [] +} + +variable "route_priority" { + description = "Route priority, defaults to 1000." + type = number + default = 1000 +} + +variable "tunnels" { + description = "VPN tunnel configurations." + type = map(object({ + ike_version = number + peer_ip = string + shared_secret = string + traffic_selectors = object({ + local = list(string) + remote = list(string) + }) + })) + default = {} +} diff --git a/modules/net-vpn-static/versions.tf b/modules/net-vpn-static/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/net-vpn-static/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/on-prem-in-a-box/README.md b/modules/on-prem-in-a-box/README.md new file mode 100644 index 0000000000..52098bb1cf --- /dev/null +++ b/modules/on-prem-in-a-box/README.md @@ -0,0 +1,131 @@ +# On-prem-in-a-box Module + +This module allows emulating an on-premise enviroment in a single GCE VM, by connecting a Docker Network to a VPC via a static or dynamic (BGP) VPN connection implemented with Strongswan. It provides a good playground for testing private access and hybrid DNS connectivity between on-premise and Google Cloud. + +To see this module in action, please refer to the folowing end-to-end network examples: +- [hub-and-spoke-peerings](../../infrastructure/hub-and-spoke-peerings/) + +## TODO + +- [ ] describe how to check and troubleshoot the onprem VPN and services +- [ ] add support for service account, scopes and network tags +- [ ] allow passing in arbitrary CoreDNS configurations instead of tweaking a default one via variables + +## Examples + +### Static VPN Gateway +```hcl +module "cloud-vpn" { + source = "modules/net-vpn-static/" + project_id = "" + region = "europe-west4" + network = "vpn-network" + name = "cloud-net-to-on-prem" + remote_ranges = ["192.168.192.0/24"] + tunnels = { + remote-0 = { + ike_version = 2 + peer_ip = module.on-prem.external_address + shared_secret = "" + traffic_selectors = { local = ["0.0.0.0/0"], remote = null } + } + } +} + +module "on-prem" { + source = "modules/on-prem-in-a-box/" + + name = "onprem-instance" + project_id = "" + zone = "europe-west4-b" + network = + subnet_self_link = "https://www.googleapis.com/compute/v1/projects//regions/europe-west4/subnetworks/" + vpn_gateway_type = "static" + peer_ip = module.cloud-vpn.address + local_ip_cidr_range = "192.168.192.0/24" + shared_secret = module.cloud-vpn.random_secret + remote_ip_cidr_ranges = "172.16.0.0/24,172.16.1.0/24,172.16.2.0/24" +} +``` + +### Dynamic VPN Gateway +```hcl +module "cloud-vpn" { + source = "modules/net-vpn-dynamic/" + project_id = "" + region = "europe-west4" + network = "vpn-network" + name = "cloud-net-to-on-prem" + router_asn = 65001 + tunnels = { + remote-1 = { + bgp_peer = { + address = "169.254.0.2" + asn = 65002 + } + bgp_session_range = "169.254.0.1/30" + ike_version = 2 + peer_ip = module.on-prem.external_address + shared_secret = null + bgp_peer_options = { + advertise_groups = ["ALL_SUBNETS"] + advertise_ip_ranges = { + } + advertise_mode = "DEFAULT" + route_priority = 1000 + } + } + } +} + +module "on-prem" { + source = "modules/on-prem-in-a-box/" + + name = "onprem-instance" + project_id = "" + zone = "europe-west4-b" + network = "" + subnet_self_link = "https://www.googleapis.com/compute/v1/projects//regions/europe-west4/subnetworks/" + vpn_gateway_type = "dynamic" + peer_ip = module.cloud-vpn.address + local_ip_cidr_range = "192.168.192.0/24" + shared_secret = module.cloud-vpn.random_secret + peer_bgp_session_range = "169.254.0.1/30" + local_bgp_session_range = "169.254.0.2/30" + peer_bgp_asn = 65001 + local_bgp_asn = 65002 +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| network | VPC network name. | string | ✓ | | +| project_id | Project id. | string | ✓ | | +| subnet_self_link | VPC subnet self link. | string | ✓ | | +| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | object({...}) | ✓ | | +| zone | Compute zone. | string | ✓ | | +| *coredns_config* | CoreDNS configuration, set to null to use default. | string | | null | +| *dns_domain* | DNS domain used for on-prem host records. | string | | onprem.example.com | +| *local_ip_cidr_range* | IP CIDR range used for the Docker onprem network. | string | | 192.168.192.0/24 | +| *machine_type* | Machine type. | string | | g1-small | +| *name* | On-prem-in-a-box compute instance name. | string | | onprem | +| *network_tags* | Network tags. | list(string) | | ["ssh"] | +| *service_account* | Service account customization. | object({...}) | | ... | +| *vpn_dynamic_config* | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | object({...}) | | ... | +| *vpn_static_ranges* | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| dns_ip_address | None | | +| external_address | None | | +| instance_name | None | | +| internal_address | None | | +| toolbox_ip_address | None | | +| vpn_ip_address | None | | +| web_ip_address | None | | + diff --git a/modules/on-prem-in-a-box/assets/Corefile b/modules/on-prem-in-a-box/assets/Corefile new file mode 100644 index 0000000000..91672a257e --- /dev/null +++ b/modules/on-prem-in-a-box/assets/Corefile @@ -0,0 +1,11 @@ +${dns_domain} { + root /etc/coredns + hosts onprem.hosts + log + errors +} +. { + forward . 8.8.8.8 + log + errors +} diff --git a/modules/on-prem-in-a-box/assets/dynamic-vpn-gw-cloud-init.yaml b/modules/on-prem-in-a-box/assets/dynamic-vpn-gw-cloud-init.yaml new file mode 100644 index 0000000000..05fda88f9d --- /dev/null +++ b/modules/on-prem-in-a-box/assets/dynamic-vpn-gw-cloud-init.yaml @@ -0,0 +1,319 @@ +#cloud-config + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package_update: true +package_upgrade: true +package_reboot_if_required: true + +packages: + - apt-transport-https + - ca-certificates + - curl + - gnupg-agent + - software-properties-common + +write_files: + +# Docker daemon configuration +- path: /etc/docker/daemon.json + owner: root:root + permissions: '0644' + content: | + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m" + } + } + +# Docker compose systemd unit for onprem +- path: /etc/systemd/system/docker-onprem.service + permissions: 0644 + owner: root + content: | + [Install] + WantedBy=multi-user.target + [Unit] + Description=Start Docker Compose onprem infrastructure + After=network-online.target docker.socket + Wants=network-online.target docker.socket + [Service] + ExecStart=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose up" + ExecStop=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose down" + +# Docker compose configuration file for onprem +- path: /var/lib/docker-compose/onprem/docker-compose.yaml + permissions: 0644 + owner: root + content: | + version: "3" + services: + vpn: + image: gcr.io/pso-cft-fabric/strongswan:latest + networks: + onprem: + ipv4_address: ${vpn_ip_address} + ports: + - "500:500/udp" + - "4500:4500/udp" + - "179:179/tcp" + privileged: true + cap_add: + - NET_ADMIN + volumes: + - "/lib/modules:/lib/modules:ro" + - "/etc/localtime:/etc/localtime:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro" + - "/var/lib/docker-compose/onprem/ipsec/vti.conf:/etc/strongswan.d/vti.conf:ro" + environment: + - LAN_NETWORKS=${local_ip_cidr_range} + bird: + image: pierky/bird + network_mode: service:vpn + cap_add: + - NET_ADMIN + - NET_BROADCAST + - NET_RAW + privileged: true + volumes: + - "/var/lib/docker-compose/onprem/bird/bird.conf:/etc/bird/bird.conf:ro" + dns: + image: coredns/coredns + command: "-conf /etc/coredns/Corefile" + depends_on: + - "vpn" + - "bird" + networks: + onprem: + ipv4_address: ${dns_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro" + routing_sidecar_dns: + image: alpine + network_mode: service:dns + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + web: + image: nginx:stable-alpine + depends_on: + - "vpn" + - "bird" + - "dns" + dns: + - ${dns_ip_address} + networks: + onprem: + ipv4_address: ${web_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro" + routing_sidecar_web: + image: alpine + network_mode: service:web + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + toolbox: + image: gcr.io/pso-cft-fabric/toolbox:latest + networks: + onprem: + ipv4_address: ${toolbox_ip_address} + depends_on: + - "vpn" + - "dns" + - "web" + dns: + - ${dns_ip_address} + routing_sidecar_toolbox: + image: alpine + network_mode: service:toolbox + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + networks: + onprem: + ipam: + driver: default + config: + - subnet: ${local_ip_cidr_range} + +# IPSEC tunnel secret +- path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets + owner: root:root + permissions: '0600' + content: | + : PSK "${shared_secret}" + +# IPSEC tunnel configuration +- path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf + owner: root:root + permissions: '0644' + content: | + conn %default + ikelifetime=600m + keylife=180m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + mobike=no + ike=aes256gcm16-sha512-modp2048 + esp=aes256gcm16-sha512-modp8192 + authby=psk + + conn gcp + leftupdown="/var/lib/strongswan/ipsec-vti.sh 0 ${peer_bgp_address}/30 ${local_bgp_address}/30" + left=%any + leftid=%any + leftsubnet=0.0.0.0/0 + leftauth=psk + right=${peer_ip_wildcard} + rightid=${peer_ip} + rightsubnet=0.0.0.0/0 + rightauth=psk + type=tunnel + auto=start + dpdaction=restart + closeaction=restart + mark=%unique + +# Charon configuration +- path: /var/lib/docker-compose/onprem/ipsec/vti.conf + owner: root:root + permissions: '0644' + content: | + charon { + install_routes = no + } + +# Bird bgp routing configuration +- path: /var/lib/docker-compose/onprem/bird/bird.conf + owner: root:root + permissions: '0644' + content: | + router id ${local_bgp_address}; + + # Watch interface up/down events + protocol device { + scan time 10; + } + + # Sync routes to kernel + protocol kernel { + learn; + merge paths on; # For ECMP + export filter { + krt_prefsrc = ${vpn_ip_address}; # Internal IP Address of the strongSwan VM. + accept; # Sync all routes to kernel + }; + import all; # Required due to /32 on GCE VMs for the static route below + } + + # Configure a static route to make sure route exists + protocol static { + # Network connected to eth0 + route ${local_ip_cidr_range} recursive ${local_gw_ip}; + # Private google access + route 199.36.153.4/30 via ${peer_bgp_address}; + # Cloud DNS forwarding zone + route 35.199.192.0/19 via ${peer_bgp_address}; + } + # Prefix lists for routing security + # Allow any possible GCP Subnet + define GCP_VPC_A_PREFIXES = [ 10.0.0.0/8{9,29}, 172.16.0.0/12{12,29}, 192.168.0.0/16{16,29} ]; + define LOCAL_PREFIXES = [ ${local_ip_cidr_range} ]; + + # Filter received prefixes + filter gcp_vpc_a_in + { + if (net ~ GCP_VPC_A_PREFIXES) then accept; + else reject; + } + + # Filter advertised prefixes + filter gcp_vpc_a_out + { + if (net ~ LOCAL_PREFIXES) then accept; + else reject; + } + + template bgp gcp_vpc_a { + keepalive time 20; + hold time 60; + graceful restart aware; # Cloud Router uses GR during maintenance + + import filter gcp_vpc_a_in; + import limit 10 action warn; # restart | block | disable + + export filter gcp_vpc_a_out; + export limit 10 action warn; # restart | block | disable + } + + protocol bgp gcp_vpc_a_tun1 from gcp_vpc_a + { + local ${local_bgp_address} as ${local_bgp_asn}; + neighbor ${peer_bgp_address} as ${peer_bgp_asn}; + } + +# CoreDNS configuration +- path: /var/lib/docker-compose/onprem/coredns/Corefile + owner: root:root + permissions: '0644' + content: | + ${coredns_config} + +# CoreDNS onprem hosts file +- path: /var/lib/docker-compose/onprem/coredns/onprem.hosts + owner: root:root + permissions: '0644' + content: | + ${vpn_ip_address} gw.${dns_domain} + ${dns_ip_address} ns.${dns_domain} + ${web_ip_address} www.${dns_domain} + ${toolbox_ip_address} toolbox.${dns_domain} + +# Minimal nginx index page +- path: /var/lib/docker-compose/onprem/nginx/index.html + owner: root:root + permissions: '0644' + content: | + + + + +

On Prem in a Box

+

${instance_name}

+ + + +runcmd: +- [systemctl, daemon-reload] +- [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -' ] +- [ sh, -c, 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ] +- [ sh, -c, 'apt update' ] +- [ sh, -c, 'apt install -y docker-ce docker-ce-cli containerd.io' ] +- [ sh, -c, 'curl -L https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep "tag_name" | cut -d \" -f4)/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose' ] +- [ sh, -c, 'chmod 755 /usr/local/bin/docker-compose' ] +- [systemctl, enable, docker.service] +- [systemctl, start, docker.service] +- [systemctl, enable, docker-onprem.service] +- [systemctl, start, docker-onprem.service] diff --git a/modules/on-prem-in-a-box/assets/static-vpn-gw-cloud-init.yaml b/modules/on-prem-in-a-box/assets/static-vpn-gw-cloud-init.yaml new file mode 100644 index 0000000000..67f7531b93 --- /dev/null +++ b/modules/on-prem-in-a-box/assets/static-vpn-gw-cloud-init.yaml @@ -0,0 +1,225 @@ +#cloud-config + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package_update: true +package_upgrade: true +package_reboot_if_required: true + +packages: + - apt-transport-https + - ca-certificates + - curl + - gnupg-agent + - software-properties-common + +write_files: + +# Docker daemon configuration +- path: /etc/docker/daemon.json + owner: root:root + permissions: '0644' + content: | + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m" + } + } + +# Docker compose systemd unit for onprem +- path: /etc/systemd/system/docker-onprem.service + permissions: 0644 + owner: root + content: | + [Install] + WantedBy=multi-user.target + [Unit] + Description=Start Docker Compose onprem infrastructure + After=network-online.target docker.socket + Wants=network-online.target docker.socket + [Service] + ExecStart=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose up" + ExecStop=/bin/sh -c "cd /var/lib/docker-compose/onprem && /usr/local/bin/docker-compose down" + +# Docker compose configuration file for onprem +- path: /var/lib/docker-compose/onprem/docker-compose.yaml + permissions: 0644 + owner: root + content: | + version: "3" + services: + vpn: + image: gcr.io/pso-cft-fabric/strongswan:latest + networks: + onprem: + ipv4_address: ${vpn_ip_address} + ports: + - "500:500/udp" + - "4500:4500/udp" + privileged: true + cap_add: + - NET_ADMIN + volumes: + - "/lib/modules:/lib/modules:ro" + - "/etc/localtime:/etc/localtime:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.conf:/etc/ipsec.conf:ro" + - "/var/lib/docker-compose/onprem/ipsec/ipsec.secrets:/etc/ipsec.secrets:ro" + environment: + - LAN_NETWORKS=${local_ip_cidr_range} + dns: + image: coredns/coredns + command: "-conf /etc/coredns/Corefile" + depends_on: + - "vpn" + networks: + onprem: + ipv4_address: ${dns_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/coredns:/etc/coredns:ro" + routing_sidecar_dns: + image: alpine + network_mode: service:dns + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + web: + image: nginx:stable-alpine + depends_on: + - "vpn" + - "dns" + dns: + - ${dns_ip_address} + networks: + onprem: + ipv4_address: ${web_ip_address} + volumes: + - "/var/lib/docker-compose/onprem/nginx:/usr/share/nginx/html:ro" + routing_sidecar_web: + image: alpine + network_mode: service:web + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + toolbox: + image: gcr.io/pso-cft-fabric/toolbox:latest + networks: + onprem: + ipv4_address: ${toolbox_ip_address} + depends_on: + - "vpn" + - "dns" + - "web" + dns: + - ${dns_ip_address} + routing_sidecar_toolbox: + image: alpine + network_mode: service:toolbox + command: | + /bin/sh -c "\ + ip route del default &&\ + ip route add default via ${vpn_ip_address}" + privileged: true + networks: + onprem: + ipam: + driver: default + config: + - subnet: ${local_ip_cidr_range} + +# IPSEC tunnel secret +- path: /var/lib/docker-compose/onprem/ipsec/ipsec.secrets + owner: root:root + permissions: '0600' + content: | + : PSK "${shared_secret}" + +# IPSEC tunnel configuration +- path: /var/lib/docker-compose/onprem/ipsec/ipsec.conf + owner: root:root + permissions: '0644' + content: | + conn %default + ikelifetime=600m + keylife=180m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + mobike=no + ike=aes256gcm16-sha512-modp2048 + esp=aes256gcm16-sha512-modp8192 + authby=psk + + conn gcp + left=%any + leftid=%any + leftsubnet=${local_ip_cidr_range} + leftauth=psk + right=${peer_ip_wildcard} + rightid=${peer_ip} + rightsubnet=199.36.153.4/30,35.199.192.0/19,${remote_ip_cidr_ranges} + rightauth=psk + type=tunnel + auto=start + dpdaction=restart + closeaction=restart + +# CoreDNS configuration +- path: /var/lib/docker-compose/onprem/coredns/Corefile + owner: root:root + permissions: '0644' + content: | + ${coredns_config} + +# CoreDNS onprem hosts file +- path: /var/lib/docker-compose/onprem/coredns/onprem.hosts + owner: root:root + permissions: '0644' + content: | + ${vpn_ip_address} gw.${dns_domain} + ${dns_ip_address} ns.${dns_domain} + ${web_ip_address} www.${dns_domain} + ${toolbox_ip_address} toolbox.${dns_domain} + +# Minimal nginx index page +- path: /var/lib/docker-compose/onprem/nginx/index.html + owner: root:root + permissions: '0644' + content: | + + + + +

On Prem in a Box

+

${instance_name}

+ + + +runcmd: +- [systemctl, daemon-reload] +- [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -' ] +- [ sh, -c, 'add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ] +- [ sh, -c, 'apt update' ] +- [ sh, -c, 'apt install -y docker-ce docker-ce-cli containerd.io' ] +- [ sh, -c, 'curl -L https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep "tag_name" | cut -d \" -f4)/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose' ] +- [ sh, -c, 'chmod 755 /usr/local/bin/docker-compose' ] +- [systemctl, enable, docker.service] +- [systemctl, start, docker.service] +- [systemctl, enable, docker-onprem.service] +- [systemctl, start, docker-onprem.service] diff --git a/modules/on-prem-in-a-box/main.tf b/modules/on-prem-in-a-box/main.tf new file mode 100644 index 0000000000..1ddfc34dfe --- /dev/null +++ b/modules/on-prem-in-a-box/main.tf @@ -0,0 +1,130 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + corefile = ( + var.coredns_config == null || var.coredns_config == "" + ? data.template_file.corefile.rendered + : var.coredns_config + ) +} + +resource "google_compute_address" "static" { + project = var.project_id + name = var.name + region = substr(var.zone, 0, length(var.zone) - 2) + address_type = "EXTERNAL" +} + +resource "google_compute_instance" "on_prem_in_a_box" { + project = var.project_id + name = var.name + machine_type = var.machine_type + zone = var.zone + tags = concat(var.network_tags, ["onprem"]) + + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-1804-lts" + } + } + + network_interface { + subnetwork = var.subnet_self_link + access_config { + nat_ip = google_compute_address.static.address + } + } + + metadata = { + user-data = data.template_file.vpn-gw.rendered + } + + service_account { + email = var.service_account.email + scopes = var.service_account.scopes + } + +} + +data "template_file" "corefile" { + template = file("${path.module}/assets/Corefile") + vars = { + dns_domain = var.dns_domain + } +} + +data "template_file" "vpn-gw" { + template = file(format( + "%s/assets/%s-vpn-gw-cloud-init.yaml", path.module, var.vpn_config.type + )) + + vars = { + coredns_config = indent(4, local.corefile) + dns_domain = var.dns_domain + instance_name = var.name + local_ip_cidr_range = var.local_ip_cidr_range + local_gw_ip = cidrhost(var.local_ip_cidr_range, 1) + vpn_ip_address = cidrhost(var.local_ip_cidr_range, 2) + dns_ip_address = cidrhost(var.local_ip_cidr_range, 3) + web_ip_address = cidrhost(var.local_ip_cidr_range, 4) + toolbox_ip_address = cidrhost(var.local_ip_cidr_range, 5) + # vpn config + peer_ip = var.vpn_config.peer_ip + peer_ip_wildcard = "%${var.vpn_config.peer_ip}" + shared_secret = var.vpn_config.shared_secret + # vpn dynamic config + local_bgp_asn = var.vpn_dynamic_config.local_bgp_asn + local_bgp_address = var.vpn_dynamic_config.local_bgp_address + peer_bgp_asn = var.vpn_dynamic_config.peer_bgp_asn + peer_bgp_address = var.vpn_dynamic_config.peer_bgp_address + # vpn static ranges + vpn_static_ranges = join(",", var.vpn_static_ranges) + } +} + +# TODO: use a narrower firewall rule and tie it to the service account + +resource "google_compute_firewall" "allow-vpn" { + name = "onprem-in-a-box-allow-vpn" + description = "Allow VPN traffic to the onprem instance" + network = var.network + project = var.project_id + source_ranges = [format("%s/32", var.vpn_config.peer_ip)] + target_tags = ["onprem"] + allow { + protocol = "tcp" + } + allow { + protocol = "udp" + } + allow { + protocol = "icmp" + } +} + +resource "google_compute_firewall" "allow-iap" { + name = "onprem-in-a-box-allow-iap" + description = "Allow SSH traffic to the onprem instance from IAP" + network = var.network + project = var.project_id + source_ranges = ["35.235.240.0/20"] + target_tags = ["onprem"] + allow { + protocol = "tcp" + ports = ["22"] + } +} diff --git a/modules/on-prem-in-a-box/outputs.tf b/modules/on-prem-in-a-box/outputs.tf new file mode 100644 index 0000000000..4404de163d --- /dev/null +++ b/modules/on-prem-in-a-box/outputs.tf @@ -0,0 +1,43 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "external_address" { + value = google_compute_instance.on_prem_in_a_box.network_interface.0.access_config.0.nat_ip +} + +output "internal_address" { + value = google_compute_instance.on_prem_in_a_box.network_interface.0.network_ip +} + +output "instance_name" { + value = google_compute_instance.on_prem_in_a_box.name +} + +output "vpn_ip_address" { + value = cidrhost(var.local_ip_cidr_range, 2) +} + +output "dns_ip_address" { + value = cidrhost(var.local_ip_cidr_range, 3) +} + +output "web_ip_address" { + value = cidrhost(var.local_ip_cidr_range, 4) +} + +output "toolbox_ip_address" { + value = cidrhost(var.local_ip_cidr_range, 5) +} diff --git a/modules/on-prem-in-a-box/variables.tf b/modules/on-prem-in-a-box/variables.tf new file mode 100644 index 0000000000..b636f2445c --- /dev/null +++ b/modules/on-prem-in-a-box/variables.tf @@ -0,0 +1,118 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "coredns_config" { + description = "CoreDNS configuration, set to null to use default." + type = string + default = null +} + +variable "dns_domain" { + description = "DNS domain used for on-prem host records." + type = string + default = "onprem.example.com" +} + +variable "local_ip_cidr_range" { + description = "IP CIDR range used for the Docker onprem network." + type = string + default = "192.168.192.0/24" +} + +variable "machine_type" { + description = "Machine type." + type = string + default = "g1-small" +} + +variable "name" { + description = "On-prem-in-a-box compute instance name." + type = string + default = "onprem" +} + +variable "network" { + description = "VPC network name." + type = string +} + +variable "network_tags" { + description = "Network tags." + type = list(string) + default = ["ssh"] +} + +variable "project_id" { + description = "Project id." + type = string +} + +variable "service_account" { + description = "Service account customization." + type = object({ + email = string + scopes = list(string) + }) + default = { + email = null + scopes = [ + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring.write" + ] + } +} + +variable "subnet_self_link" { + description = "VPC subnet self link." + type = string +} + +variable "vpn_config" { + description = "VPN configuration, type must be one of 'dynamic' or 'static'." + type = object({ + peer_ip = string + shared_secret = string + type = string + }) +} + +variable "vpn_dynamic_config" { + description = "BGP configuration for dynamic VPN, ignored if VPN type is 'static'." + type = object({ + local_bgp_asn = number + local_bgp_address = string + peer_bgp_asn = number + peer_bgp_address = string + }) + default = { + local_bgp_asn = 65002 + local_bgp_address = "169.254.0.2" + peer_bgp_asn = 65001 + peer_bgp_address = "169.254.0.1" + } +} + +variable "vpn_static_ranges" { + description = "Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'." + type = list(string) + default = [] +} + +variable "zone" { + description = "Compute zone." + type = string +} diff --git a/modules/project/README.md b/modules/project/README.md new file mode 100644 index 0000000000..2d79bd6888 --- /dev/null +++ b/modules/project/README.md @@ -0,0 +1,59 @@ +# Project Module + +## Example + +```hcl +module "project" { + source = "./modules/project" + parent = var.folder.id + billing_account = var.billing_account_id + prefix = "foo" + name = "project-example" + oslogin = true + oslogin_admins = var.admins + services = concat(var.project_services, [ + "cloudkms.googleapis.com", "accesscontextmanager.googleapis.com" + ]) + iam_roles = ["roles/container.hostServiceAgentUser"] + iam_members = { "roles/container.hostServiceAgentUser" = [ + "serviceAccount:${var.gke_service_account}" + ] } +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| name | Project name and id suffix. | string | ✓ | | +| parent | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | +| *auto_create_network* | Whether to create the default network for the project | bool | | false | +| *billing_account* | Billing account id. | string | | | +| *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| *iam_additive_members* | Map of member lists used to set non authoritative bindings, keyed by role. | map(list(string)) | | {} | +| *iam_additive_roles* | List of roles used to set non authoritative bindings. | list(string) | | [] | +| *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | +| *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | [] | +| *labels* | Resource labels. | map(string) | | {} | +| *lien_reason* | If non-empty, creates a project lien with this description. | string | | | +| *oslogin* | Enable OS Login. | bool | | false | +| *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | +| *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | +| *prefix* | Prefix used to generate project id and name. | string | | null | +| *services* | Service APIs to enable. | list(string) | | [] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| cloudsvc_service_account | Cloud services service account (depends on services). | | +| custom_roles | Ids of the created custom roles. | | +| gce_service_account | Default GCE service account (depends on services). | | +| gcr_service_account | Default GCR service account (depends on services). | | +| gke_service_account | Default GKE service account (depends on services). | | +| iam_project_id | Project id (depends on services and IAM bindings). | | +| name | Name (depends on services). | | +| number | Project number (depends on services). | | +| project_id | Project id (depends on services). | | + diff --git a/modules/project/main.tf b/modules/project/main.tf new file mode 100644 index 0000000000..e6d8bab679 --- /dev/null +++ b/modules/project/main.tf @@ -0,0 +1,126 @@ +/** + * Copyright 2018 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 { + cloudsvc_service_account = "${google_project.project.number}@cloudservices.gserviceaccount.com" + gce_service_account = "${google_project.project.number}-compute@developer.gserviceaccount.com" + gcr_service_account = "service-${google_project.project.number}@containerregistry.iam.gserviceaccount.com" + gke_service_account = "service-${google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + iam_additive_pairs = flatten([ + for role in var.iam_additive_roles : [ + for member in lookup(var.iam_additive_members, role, []) : + { role = role, member = member } + ] + ]) + iam_additive = { + for pair in local.iam_additive_pairs : + "${pair.role}-${pair.member}" => pair + } + parent_type = split("/", var.parent)[0] + parent_id = split("/", var.parent)[1] + prefix = var.prefix == null ? "" : "${var.prefix}-" +} + +resource "google_project" "project" { + org_id = local.parent_type == "organizations" ? local.parent_id : null + folder_id = local.parent_type == "folders" ? local.parent_id : null + project_id = "${local.prefix}${var.name}" + name = "${local.prefix}${var.name}" + billing_account = var.billing_account + auto_create_network = var.auto_create_network + labels = var.labels +} + +resource "google_project_iam_custom_role" "roles" { + for_each = var.custom_roles + project = google_project.project.project_id + role_id = each.key + title = "Custom role ${each.key}" + description = "Terraform-managed" + permissions = each.value +} + +resource "google_compute_project_metadata_item" "oslogin_meta" { + count = var.oslogin ? 1 : 0 + project = google_project.project.project_id + key = "enable-oslogin" + value = "TRUE" + # depend on services or it will fail on destroy + depends_on = [google_project_service.project_services] +} + +resource "google_resource_manager_lien" "lien" { + count = var.lien_reason != "" ? 1 : 0 + parent = "projects/${google_project.project.number}" + restrictions = ["resourcemanager.projects.delete"] + origin = "created-by-terraform" + reason = var.lien_reason +} + +resource "google_project_service" "project_services" { + for_each = toset(var.services) + project = google_project.project.project_id + service = each.value + disable_on_destroy = true + disable_dependent_services = true +} + +# IAM notes: +# - external users need to have accepted the invitation email to join +# - oslogin roles also require role to list instances +# - additive (non-authoritative) roles might fail due to dynamic values + +resource "google_project_iam_binding" "authoritative" { + for_each = toset(var.iam_roles) + project = google_project.project.project_id + role = each.value + members = lookup(var.iam_members, each.value, []) +} + +resource "google_project_iam_member" "additive" { + for_each = length(var.iam_additive_roles) > 0 ? local.iam_additive : {} + project = google_project.project.project_id + role = each.value.role + member = each.value.member +} + +resource "google_project_iam_member" "oslogin_iam_serviceaccountuser" { + for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([]) + project = google_project.project.project_id + role = "roles/iam.serviceAccountUser" + member = each.value +} + +resource "google_project_iam_member" "oslogin_compute_viewer" { + for_each = var.oslogin ? toset(distinct(concat(var.oslogin_admins, var.oslogin_users))) : toset([]) + project = google_project.project.project_id + role = "roles/compute.viewer" + member = each.value +} + +resource "google_project_iam_member" "oslogin_admins" { + for_each = var.oslogin ? toset(var.oslogin_admins) : toset([]) + project = google_project.project.project_id + role = "roles/compute.osAdminLogin" + member = each.value +} + +resource "google_project_iam_member" "oslogin_users" { + for_each = var.oslogin ? toset(var.oslogin_users) : toset([]) + project = google_project.project.project_id + role = "roles/compute.osLogin" + member = each.value +} diff --git a/modules/project/outputs.tf b/modules/project/outputs.tf new file mode 100644 index 0000000000..7cb7021748 --- /dev/null +++ b/modules/project/outputs.tf @@ -0,0 +1,74 @@ +/** + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "project_id" { + description = "Project id (depends on services)." + value = google_project.project.project_id + depends_on = [ + google_project_service.project_services + ] +} + +output "iam_project_id" { + description = "Project id (depends on services and IAM bindings)." + value = google_project.project.project_id + depends_on = [ + google_project_service.project_services, + google_project_iam_binding.authoritative, + google_project_iam_member.non_authoritative + ] +} + +output "name" { + description = "Name (depends on services)." + value = google_project.project.name + depends_on = [google_project_service.project_services] +} + +output "number" { + description = "Project number (depends on services)." + value = google_project.project.number + depends_on = [google_project_service.project_services] +} + +output "cloudsvc_service_account" { + description = "Cloud services service account (depends on services)." + value = "${local.cloudsvc_service_account}" + depends_on = [google_project_service.project_services] +} + +output "gce_service_account" { + description = "Default GCE service account (depends on services)." + value = local.gce_service_account + depends_on = [google_project_service.project_services] +} + +output "gcr_service_account" { + description = "Default GCR service account (depends on services)." + value = local.gcr_service_account + depends_on = [google_project_service.project_services] +} + +output "gke_service_account" { + description = "Default GKE service account (depends on services)." + value = local.gke_service_account + depends_on = [google_project_service.project_services] +} + +output "custom_roles" { + description = "Ids of the created custom roles." + value = [for role in google_project_iam_custom_role.roles : role.role_id] +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf new file mode 100644 index 0000000000..b9ecc8645a --- /dev/null +++ b/modules/project/variables.tf @@ -0,0 +1,109 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "auto_create_network" { + description = "Whether to create the default network for the project" + type = bool + default = false +} + +variable "billing_account" { + description = "Billing account id." + type = string + default = "" +} + +variable "custom_roles" { + description = "Map of role name => list of permissions to create in this project." + type = map(list(string)) + default = {} +} + +variable "iam_members" { + description = "Map of member lists used to set authoritative bindings, keyed by role." + type = map(list(string)) + default = {} +} + +variable "iam_roles" { + description = "List of roles used to set authoritative bindings." + type = list(string) + default = [] +} + +variable "iam_additive_members" { + description = "Map of member lists used to set non authoritative bindings, keyed by role." + type = map(list(string)) + default = {} +} + +variable "iam_additive_roles" { + description = "List of roles used to set non authoritative bindings." + type = list(string) + default = [] +} + +variable "labels" { + description = "Resource labels." + type = map(string) + default = {} +} + +variable "lien_reason" { + description = "If non-empty, creates a project lien with this description." + type = string + default = "" +} + +variable "name" { + description = "Project name and id suffix." + type = string +} + +variable "oslogin" { + description = "Enable OS Login." + type = bool + default = false +} + +variable "oslogin_admins" { + description = "List of IAM-style identities that will be granted roles necessary for OS Login administrators." + type = list(string) + default = [] +} + +variable "oslogin_users" { + description = "List of IAM-style identities that will be granted roles necessary for OS Login users." + type = list(string) + default = [] +} + +variable "parent" { + description = "The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id." + type = string +} + +variable "prefix" { + description = "Prefix used to generate project id and name." + type = string + default = null +} + +variable "services" { + description = "Service APIs to enable." + type = list(string) + default = [] +} diff --git a/modules/project/versions.tf b/modules/project/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/project/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/tests/__init__.py b/tests/__init__.py index 35dff2e86e..6913f02e36 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright 2019 Google LLC -# +# Copyright 2020 Google LLC +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - diff --git a/tests/conftest.py b/tests/conftest.py index 3dea14be84..b42458c6d7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,10 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -12,25 +12,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -"Shared fixtures." +"Shared fixtures" import os - import pytest import tftest -_BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASEDIR = os.path.dirname(os.path.dirname(__file__)) @pytest.fixture(scope='session') -def plan(): +def plan_runner(): + "Returns a function to run Terraform plan on a fixture." - def run_plan(testdir): - tfdir = testdir.replace('_', '-') - tf = tftest.TerraformTest(tfdir, _BASEDIR, + def run_plan(fixture_path, is_module=True, **tf_vars): + "Runs Terraform plan and returns parsed output" + tf = tftest.TerraformTest(fixture_path, BASEDIR, os.environ.get('TERRAFORM', 'terraform')) - tf.setup(extra_files=['tests/{}/terraform.tfvars'.format(testdir)]) - return tf.plan(output=True) + tf.setup() + plan = tf.plan(output=True, tf_vars=tf_vars) + root_module = plan.planned_values['root_module']['child_modules'][0] + if is_module: + return (plan, root_module['resources']) + modules = dict((mod['address'], mod['resources']) + for mod in root_module['child_modules']) + return (plan, modules) return run_plan + + +@pytest.fixture(scope='session') +def apply_runner(): + "Returns a function to run Terraform apply on a fixture." + + def run_apply(fixture_path, **tf_vars): + "Runs Terraform apply and returns parsed output" + tf = tftest.TerraformTest(fixture_path, BASEDIR, + os.environ.get('TERRAFORM', 'terraform')) + tf.setup() + apply = tf.apply(tf_vars=tf_vars) + output = tf.output(json_format=True) + return (apply, output) + + return run_apply diff --git a/tests/foundations/__init__.py b/tests/foundations/__init__.py index 35dff2e86e..6913f02e36 100644 --- a/tests/foundations/__init__.py +++ b/tests/foundations/__init__.py @@ -1,14 +1,13 @@ -# Copyright 2019 Google LLC -# +# Copyright 2020 Google LLC +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - diff --git a/tests/foundations/business_units/terraform.tfvars b/tests/foundations/business_units/terraform.tfvars deleted file mode 100644 index 199cdc94d9..0000000000 --- a/tests/foundations/business_units/terraform.tfvars +++ /dev/null @@ -1,9 +0,0 @@ -billing_account_id = "012345-ABCDEF-012345" -business_unit_1_name = "infra" -business_unit_2_name = "analytics" -business_unit_3_name = "data" -environments = ["dev", "test"] -generate_service_account_keys = true -organization_id = "012345678919" -prefix = "fabric-org-env-3" -root_node = "folders/0123456789" diff --git a/tests/foundations/business_units/test_folders.py b/tests/foundations/business_units/test_folders.py deleted file mode 100644 index 2064922b3c..0000000000 --- a/tests/foundations/business_units/test_folders.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test shared and business-units folders" - - -import pytest - - -def test_shared_folder(plan): - "Shared folder resource attributes must match variables." - root_node = plan.variables['root_node'] - resource = plan.modules['module.shared-folder'].resources['google_folder.folders[0]'] - assert resource['values']['parent'] == root_node - assert resource['values']['display_name'] == 'shared' - - -def test_business_unit_folders(plan): - "Business Unit folder resource attributes must match variables." - address_tpl = ( - 'module.business-unit-%s-folders.module.business-unit-folder' - '.google_folder.folders[0]' - ) - count = range(1, 4) - business_unit_names = [ - plan.variables['business_unit_%s_name' % i] for i in count] - root_node = plan.variables['root_node'] - for address in [address_tpl % i for i in count]: - resource = plan.resource_changes[address] - assert resource['change']['after']['parent'] == root_node - assert resource['change']['after']['display_name'] in business_unit_names diff --git a/tests/foundations/business_units/test_outputs.py b/tests/foundations/business_units/test_outputs.py deleted file mode 100644 index c485b2e8c7..0000000000 --- a/tests/foundations/business_units/test_outputs.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test root module outputs." - - -def test_project_ids(plan): - "Project ids should use prefix and match expected values." - prefix = plan.variables['prefix'] - assert plan.outputs['audit_logs_project'] == '%s-audit' % prefix - assert plan.outputs['shared_resources_project'] == '%s-shared' % prefix - assert plan.outputs['terraform_project'] == '%s-terraform' % prefix - - -def test_bucket_names(plan): - "GCS bucket names should use prefix and location and match expected values." - location = plan.variables['gcs_location'].lower() - prefix = plan.variables['prefix'] - bootstrap_bucket = plan.outputs['bootstrap_tf_gcs_bucket'] - assert bootstrap_bucket.startswith(prefix) - assert bootstrap_bucket.endswith('tf-bootstrap') - assert '-%s-' % location in bootstrap_bucket - - -def test_environment_buckets(plan): - "One GCS bucket should be created for each environment." - buckets = plan.outputs['environment_tf_gcs_buckets'] - for environment in plan.variables['environments']: - assert environment in buckets - assert buckets[environment].endswith(environment) - - -def test_bq_dataset(plan): - "Bigquery dataset name should be based on root node type and id." - node_type, node_id = plan.variables['root_node'].split('/') - assert plan.outputs['audit_logs_bq_dataset'] == 'logs_audit_%s_%s' % ( - node_type[:-1], node_id) diff --git a/tests/foundations/business_units/test_projects.py b/tests/foundations/business_units/test_projects.py deleted file mode 100644 index 23194afa55..0000000000 --- a/tests/foundations/business_units/test_projects.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test project creation in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def project_modules(plan): - names = ['module.project-%s' % - name for name in ('audit', 'shared-resources', 'tf')] - return dict((name, plan.modules[name]) for name in names) - - -def test_project_resource(plan, project_modules): - "Project resource attributes must match variables." - names = ('shared', 'terraform', 'audit') - prefix = plan.variables['prefix'] - billing_account = plan.variables['billing_account_id'] - project_names = ['%s-%s' % (prefix, name) for name in names] - for mod in project_modules.values(): - resource = mod.resources['google_project.project'] - assert resource['values']['billing_account'] == billing_account - assert resource['values']['name'] in project_names - - -def test_project_services(plan, project_modules): - "Project service resource must enable APIs specified in the variable." - num_services = len(plan.variables['project_services']) - for mod in project_modules.values(): - project_services = [r for r in mod.resources if r.startswith( - 'google_project_service.project_services')] - assert len(project_services) >= num_services diff --git a/tests/foundations/business_units/test_service_accounts.py b/tests/foundations/business_units/test_service_accounts.py deleted file mode 100644 index 65640f1a20..0000000000 --- a/tests/foundations/business_units/test_service_accounts.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test service account creation in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def mod(plan): - return plan.modules['module.service-accounts-tf-environments'] - - -def test_accounts(plan, mod): - "One service account per environment should be created." - environments = plan.variables['environments'] - prefix = plan.variables['prefix'] - resources = [ - v for k, v in mod.resources.items() if 'google_service_account.' in k] - assert len(resources) == len(environments) - assert sorted([res['values']['account_id'] for res in resources]) == sorted([ - '%s-%s' % (prefix, env) for env in environments]) diff --git a/tests/foundations/environments/__init__.py b/tests/foundations/environments/__init__.py index 35dff2e86e..6913f02e36 100644 --- a/tests/foundations/environments/__init__.py +++ b/tests/foundations/environments/__init__.py @@ -1,14 +1,13 @@ -# Copyright 2019 Google LLC -# +# Copyright 2020 Google LLC +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - diff --git a/tests/foundations/environments/conftest.py b/tests/foundations/environments/conftest.py deleted file mode 100644 index cb648e15af..0000000000 --- a/tests/foundations/environments/conftest.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Plan fixture." - -import os - -import pytest - - -_TFDIR = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[-3:-1]) - - -@pytest.fixture(scope='package') -def plan(plan): - return plan(_TFDIR) diff --git a/tests/foundations/environments/fixture/main.tf b/tests/foundations/environments/fixture/main.tf new file mode 100644 index 0000000000..ba5eef11d1 --- /dev/null +++ b/tests/foundations/environments/fixture/main.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../foundations/environments" + billing_account_id = var.billing_account_id + environments = var.environments + iam_assets_editors = var.iam_assets_editors + iam_assets_owners = var.iam_assets_owners + iam_audit_viewers = var.iam_audit_viewers + iam_sharedsvc_owners = var.iam_sharedsvc_owners + iam_terraform_owners = var.iam_terraform_owners + iam_xpn_config = var.iam_xpn_config + organization_id = var.organization_id + prefix = var.prefix + root_node = var.root_node +} diff --git a/tests/foundations/environments/fixture/variables.tf b/tests/foundations/environments/fixture/variables.tf new file mode 100644 index 0000000000..1516e4cd36 --- /dev/null +++ b/tests/foundations/environments/fixture/variables.tf @@ -0,0 +1,76 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +variable "billing_account_id" { + type = string + default = "1234-5678-9012" +} + +variable "environments" { + type = list(string) + default = ["test", "prod"] +} + +variable "iam_assets_editors" { + type = list(string) + default = ["user:assets-ed-1@example.org", "user:assets-ed-2@example.org"] +} + +variable "iam_assets_owners" { + type = list(string) + default = ["user:assets-own-1@example.org", "user:assets-own-2@example.org"] +} + +variable "iam_audit_viewers" { + type = list(string) + default = ["user:audit-1@example.org", "user:audit2@example.org"] +} + +variable "iam_sharedsvc_owners" { + type = list(string) + default = ["user:shared-1@example.org", "user:shared-2@example.org"] +} + +variable "iam_terraform_owners" { + type = list(string) + default = ["user:tf-1@example.org", "user:tf-2@example.org"] +} + +variable "iam_xpn_config" { + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "organization_id" { + type = string + default = "" +} + +variable "prefix" { + description = "Prefix used for resources that need unique names." + type = string + default = "test" +} + +variable "root_node" { + description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." + type = string + default = "folders/1234567890" +} diff --git a/tests/foundations/environments/terraform.tfvars b/tests/foundations/environments/terraform.tfvars deleted file mode 100644 index acc2a16bf6..0000000000 --- a/tests/foundations/environments/terraform.tfvars +++ /dev/null @@ -1,7 +0,0 @@ -billing_account_id = "012345-ABCDEF-012345" -environments = ["dev", "test"] -generate_service_account_keys = true -grant_xpn_roles = true -organization_id = "012345678919" -prefix = "fabric-org-env-3" -root_node = "folders/0123456789" diff --git a/tests/foundations/environments/test_outputs.py b/tests/foundations/environments/test_outputs.py deleted file mode 100644 index 64f3ca3911..0000000000 --- a/tests/foundations/environments/test_outputs.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test root module outputs." - - -def test_project_ids(plan): - "Project ids should use prefix and match expected values." - prefix = plan.variables['prefix'] - assert plan.outputs['audit_logs_project'] == prefix + '-audit' - assert plan.outputs['shared_resources_project'] == prefix + '-shared' - assert plan.outputs['terraform_project'] == prefix + '-terraform' - - -def test_bucket_names(plan): - "GCS bucket names should use prefix and location and match expected values." - location = plan.variables['gcs_location'].lower() - prefix = plan.variables['prefix'] - bootstrap_bucket = plan.outputs['bootstrap_tf_gcs_bucket'] - assert bootstrap_bucket.startswith(prefix) - assert bootstrap_bucket.endswith('tf-bootstrap') - assert '-%s-' % location in bootstrap_bucket - - -def test_environment_buckets(plan): - "One GCS bucket should be created for each environment." - buckets = plan.outputs['environment_tf_gcs_buckets'] - for environment in plan.variables['environments']: - assert environment in buckets - assert buckets[environment].endswith(environment) - - -def test_bq_dataset(plan): - "Bigquery dataset should be named after the first environment." - assert plan.outputs['audit_logs_bq_dataset'].endswith( - plan.variables['environments'][0]) diff --git a/tests/foundations/environments/test_plan.py b/tests/foundations/environments/test_plan.py new file mode 100644 index 0000000000..96fd124e5c --- /dev/null +++ b/tests/foundations/environments/test_plan.py @@ -0,0 +1,53 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_folder_roles(plan_runner): + "Test folder roles." + _, modules = plan_runner(FIXTURES_DIR, is_module=False) + resources = modules['module.test.module.environment-folders'] + folders = [r for r in resources if r['type'] == 'google_folder'] + assert len(folders) == 2 + assert set(r['values']['display_name'] + for r in folders) == set(['prod', 'test']) + bindings = [r['index'].split('-') + for r in resources if r['type'] == 'google_folder_iam_binding'] + assert len(bindings) == 10 + assert set(b[0] for b in bindings) == set(['prod', 'test']) + assert len(set(b[1] for b in bindings)) == 5 + + +def test_org_roles(plan_runner): + "Test folder roles." + vars = { + 'organization_id': 'organizations/123', + 'iam_xpn_config': '{grant = true, target_org = true}' + } + _, modules = plan_runner(FIXTURES_DIR, is_module=False, **vars) + resources = modules['module.test.module.environment-folders'] + folder_bindings = [r['index'].split('-') + for r in resources if r['type'] == 'google_folder_iam_binding'] + assert len(folder_bindings) == 8 + resources = modules['module.test.module.tf-service-accounts'] + org_bindings = [r['index'].split('-') + for r in resources if r['type'] == 'google_organization_iam_member'] + assert len(org_bindings) == 4 + assert set(b[0] for b in org_bindings) == set(['prod', 'test']) diff --git a/tests/foundations/environments/test_projects.py b/tests/foundations/environments/test_projects.py deleted file mode 100644 index 26d30639aa..0000000000 --- a/tests/foundations/environments/test_projects.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test project creation in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def project_modules(plan): - names = ['module.project-%s' % - name for name in ('audit', 'shared-resources', 'tf')] - return dict((name, plan.modules[name]) for name in names) - - -def test_project_resource(plan, project_modules): - "Project resource attributes must match variables." - root_node = plan.variables['root_node'].split('/')[1] - billing_account = plan.variables['billing_account_id'] - for name, mod in project_modules.items(): - resource = mod.resources['google_project.project'] - assert resource['values']['folder_id'] == root_node - assert resource['values']['billing_account'] == billing_account - - -def test_project_services(plan, project_modules): - "Project service resource must enable APIs specified in the variable." - num_services = len(plan.variables['project_services']) - for mod in project_modules.values(): - project_services = [r for r in mod.resources if r.startswith( - 'google_project_service.project_services')] - assert len(project_services) >= num_services diff --git a/tests/foundations/environments/test_service_accounts.py b/tests/foundations/environments/test_service_accounts.py deleted file mode 100644 index 65640f1a20..0000000000 --- a/tests/foundations/environments/test_service_accounts.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test service account creation in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def mod(plan): - return plan.modules['module.service-accounts-tf-environments'] - - -def test_accounts(plan, mod): - "One service account per environment should be created." - environments = plan.variables['environments'] - prefix = plan.variables['prefix'] - resources = [ - v for k, v in mod.resources.items() if 'google_service_account.' in k] - assert len(resources) == len(environments) - assert sorted([res['values']['account_id'] for res in resources]) == sorted([ - '%s-%s' % (prefix, env) for env in environments]) diff --git a/tests/infrastructure/hub_and_spoke_vpns/conftest.py b/tests/infrastructure/hub_and_spoke_vpns/conftest.py deleted file mode 100644 index cb648e15af..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/conftest.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Plan fixture." - -import os - -import pytest - - -_TFDIR = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[-3:-1]) - - -@pytest.fixture(scope='package') -def plan(plan): - return plan(_TFDIR) diff --git a/tests/infrastructure/hub_and_spoke_vpns/terraform.tfvars b/tests/infrastructure/hub_and_spoke_vpns/terraform.tfvars deleted file mode 100644 index cfe767c3d3..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/terraform.tfvars +++ /dev/null @@ -1,3 +0,0 @@ -hub_project_id = "automation-examples" -spoke_1_project_id = "automation-examples" -spoke_2_project_id = "automation-examples" diff --git a/tests/infrastructure/hub_and_spoke_vpns/test_cloud_routers.py b/tests/infrastructure/hub_and_spoke_vpns/test_cloud_routers.py deleted file mode 100644 index 8a3dd77a9c..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/test_cloud_routers.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test cloud routers resources creation in root module." - - -def test_hub_custom_routers(plan): - "Hub to spoke routers should match input variables." - for i in (1, 2): - router = plan.resources['google_compute_router.hub-to-spoke-%s-custom[0]' % i] - bgp = router['values']['bgp'][0] - assert bgp['advertise_mode'] == 'CUSTOM' - assert bgp['advertised_groups'] == ['ALL_SUBNETS'] - assert bgp['asn'] == plan.variables['hub_bgp_asn'] - subnet_ranges = [s['subnet_ip'] - for s in plan.variables['spoke_%s_subnets' % (3 - i)]] - assert [r['range'] for r in bgp['advertised_ip_ranges']] == subnet_ranges - - -def test_spoke_routers(plan): - "Spoke routers should match input variables." - for i in (1, 2): - router = plan.resources['google_compute_router.spoke-%s' % i] - bgp = router['values']['bgp'][0] - assert bgp['advertise_mode'] == 'DEFAULT' - assert bgp['advertised_groups'] == None - assert bgp['asn'] == plan.variables['spoke_%s_bgp_asn' % i] diff --git a/tests/infrastructure/hub_and_spoke_vpns/test_firewall.py b/tests/infrastructure/hub_and_spoke_vpns/test_firewall.py deleted file mode 100644 index cbba86f9b9..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/test_firewall.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test firewall resources creation in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def firewall_modules(plan): - return [v for k, v in plan.modules.items() if k.startswith('module.firewall-')] - - -def test_firewall_rules(plan, firewall_modules): - "Test that the hub and spoke VPCs have allow-admin firewall rules" - source_ranges = [] - for k in plan.variables: - if not k.endswith('_subnets'): - continue - source_ranges += [s['subnet_ip'] for s in plan.variables[k]] - for mod in firewall_modules: - allow_admins_resource = mod.resources['google_compute_firewall.allow-admins[0]'] - allow_ssh = mod.resources['google_compute_firewall.allow-tag-ssh[0]'] - assert allow_admins_resource['values']['source_ranges'] == source_ranges - assert allow_ssh['values']['source_ranges'] == ['0.0.0.0/0'] diff --git a/tests/infrastructure/hub_and_spoke_vpns/test_outputs.py b/tests/infrastructure/hub_and_spoke_vpns/test_outputs.py deleted file mode 100644 index 819c0d3610..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/test_outputs.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test root module outputs." - - -def test_hub_outputs(plan): - "Hub VPC ranges and regions should match input variables." - output = plan.outputs['hub'] - for subnet in plan.variables['hub_subnets']: - name = subnet['subnet_name'] - assert output['subnets_ips'][name] == subnet['subnet_ip'] - assert output['subnets_regions'][name] == subnet['subnet_region'] - - -def test_spokes_outputs(plan): - "Spokes VPC ranges and regions should match input variables." - for i in (1, 2): - output = plan.outputs['spoke-%s' % i] - for subnet in plan.variables['spoke_%s_subnets' % i]: - name = subnet['subnet_name'] - assert output['subnets_ips'][name] == subnet['subnet_ip'] - assert output['subnets_regions'][name] == subnet['subnet_region'] diff --git a/tests/infrastructure/hub_and_spoke_vpns/test_vpns.py b/tests/infrastructure/hub_and_spoke_vpns/test_vpns.py deleted file mode 100644 index 5cd0697b5f..0000000000 --- a/tests/infrastructure/hub_and_spoke_vpns/test_vpns.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test VPN BGP ASNs in root module." - - -import pytest - - -def test_spokes_peer_asn(plan): - "Test that the spoke-to-hub VPNs mach input variables" - mods = [v for k, v in plan.modules.items() if k.startswith('module.vpn-spoke')] - for mod in mods: - bgp_peer = mod.resources['google_compute_router_peer.bgp_peer[0]'] - assert bgp_peer['values']['peer_asn'] == plan.variables['hub_bgp_asn'] - - -def test_hub_peer_asns(plan): - "Test that the hub-to-spoke VPNs mach input variables" - mods = [v for k, v in plan.modules.items() if k.startswith('module.vpn-hub')] - for mod in mods: - bgp_peer = mod.resources['google_compute_router_peer.bgp_peer[0]'] - asn_varname = 'spoke_%s_bgp_asn' % mod['address'][-1] - assert bgp_peer['values']['peer_asn'] == plan.variables[asn_varname] diff --git a/tests/infrastructure/shared_vpc/terraform.tfvars b/tests/infrastructure/shared_vpc/terraform.tfvars deleted file mode 100644 index 4514993ff3..0000000000 --- a/tests/infrastructure/shared_vpc/terraform.tfvars +++ /dev/null @@ -1,4 +0,0 @@ -root_node = "folders/1234567890" -prefix = "fabric-svpc" -billing_account_id = "012345-012345-012345" -owners_gce = ["user:user@example.com"] diff --git a/tests/infrastructure/shared_vpc/test_outputs.py b/tests/infrastructure/shared_vpc/test_outputs.py deleted file mode 100644 index c8ca16ede4..0000000000 --- a/tests/infrastructure/shared_vpc/test_outputs.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test root module outputs." - - -def test_vpc_ranges(plan): - "VPC ranges should match input variables." - ranges = plan.outputs['vpc_subnets'] - for subnet in plan.variables['subnets']: - assert ranges[subnet['subnet_name']] == subnet['subnet_ip'] - - -def test_project_ids(plan): - "Project ids should use prefix and match expected values." - prefix = plan.variables['prefix'] - assert plan.outputs['host_project_id'] == prefix + '-vpc-host' - assert plan.outputs['service_project_ids']['gce'] == prefix + '-gce' diff --git a/tests/infrastructure/shared_vpc/test_svpc_resources.py b/tests/infrastructure/shared_vpc/test_svpc_resources.py deleted file mode 100644 index 1e7816069c..0000000000 --- a/tests/infrastructure/shared_vpc/test_svpc_resources.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Test shared vpc resources in root module." - - -import pytest - - -@pytest.fixture(scope='module') -def mod(plan): - return plan.modules['module.net-svpc-access'] - - -def test_host_vpc(plan): - "Test that the vpc project is set as shared vpc host." - mod = plan.modules['module.net-vpc-host'] - resources = [v['values'] for v in mod.resources.values() if v['type'] == - 'google_compute_shared_vpc_host_project'] - assert resources[0]['project'] == plan.outputs['host_project_id'] - - -def test_service_projects(plan, mod): - "Test that service projects are registered with the shared vpc." - resources = [v['values'] for v in mod.resources.values() if v['type'] == - 'google_compute_shared_vpc_service_project'] - assert len(resources) == 2 - assert set([r['host_project'] for r in resources]) == set( - [plan.outputs['host_project_id']]) - assert sorted([r['service_project'] for r in resources]) == sorted( - plan.outputs['service_project_ids'].values()) - - -def test_subnet_users(plan, mod): - "Test that the network user role is assigned on subnets." - resources = [v['values'] for v in mod.resources.values() if v['type'] == - 'google_compute_subnetwork_iam_binding'] - assert len(resources) == 2 - assert set([r['project'] for r in resources]) == set( - [plan.outputs['host_project_id']]) - assert sorted([r['subnetwork'] for r in resources]) == ['gce', 'gke'] - - -def test_service_agent(plan, mod): - "Test that the service agent role is assigned for gke only." - resources = [v['values'] for v in mod.resources.values() if v['type'] == - 'google_project_iam_binding'] - assert resources[0] == { - 'project': plan.outputs['host_project_id'], - 'role': 'roles/container.hostServiceAgentUser' - } diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py new file mode 100644 index 0000000000..3b84e5d22f --- /dev/null +++ b/tests/modules/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + diff --git a/tests/infrastructure/shared_vpc/__init__.py b/tests/modules/compute_vm/__init__.py similarity index 86% rename from tests/infrastructure/shared_vpc/__init__.py rename to tests/modules/compute_vm/__init__.py index 086a24e64e..6913f02e36 100644 --- a/tests/infrastructure/shared_vpc/__init__.py +++ b/tests/modules/compute_vm/__init__.py @@ -1,10 +1,10 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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, diff --git a/tests/modules/compute_vm/fixture/main.tf b/tests/modules/compute_vm/fixture/main.tf new file mode 100644 index 0000000000..4f4b3b36d2 --- /dev/null +++ b/tests/modules/compute_vm/fixture/main.tf @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/compute-vm" + project_id = "my-project" + region = "europe-west1" + zone = "europe-west1-b" + name = "test" + network_interfaces = [{ + network = "https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default", + subnetwork = "https://www.googleapis.com/compute/v1/projects/my-project/regions/europe-west1/subnetworks/default-default", + nat = false, + addresses = null + }] + service_account_create = var.service_account_create + instance_count = var.instance_count + use_instance_template = var.use_instance_template + group = var.group + group_manager = var.group_manager +} diff --git a/tests/modules/compute_vm/fixture/variables.tf b/tests/modules/compute_vm/fixture/variables.tf new file mode 100644 index 0000000000..7b2b9aad23 --- /dev/null +++ b/tests/modules/compute_vm/fixture/variables.tf @@ -0,0 +1,40 @@ +/** + * Copyright 2020 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 "group" { + type = any + default = null +} + +variable "group_manager" { + type = any + default = null +} + +variable "instance_count" { + type = number + default = 1 +} + +variable "use_instance_template" { + type = bool + default = false +} + +variable "service_account_create" { + type = bool + default = false +} diff --git a/tests/modules/compute_vm/test_plan.py b/tests/modules/compute_vm/test_plan.py new file mode 100644 index 0000000000..009309984c --- /dev/null +++ b/tests/modules/compute_vm/test_plan.py @@ -0,0 +1,46 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_single_instance(plan_runner): + plan, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 1 + assert resources[0]['type'] == 'google_compute_instance' + + +def test_multiple_instances(plan_runner): + plan, resources = plan_runner(FIXTURES_DIR, instance_count=2) + assert len(resources) == 2 + assert set(r['type'] for r in resources) == set(['google_compute_instance']) + + +def test_service_account(plan_runner): + plan, resources = plan_runner(FIXTURES_DIR, instance_count=2, + service_account_create='true') + assert len(resources) == 3 + assert 'google_service_account' in [r['type'] for r in resources] + + +def test_template(plan_runner): + plan, resources = plan_runner(FIXTURES_DIR, use_instance_template='true') + assert len(resources) == 1 + assert resources[0]['type'] == 'google_compute_instance_template' + assert resources[0]['values']['name_prefix'] == 'test-' diff --git a/tests/modules/compute_vm/test_plan_group.py b/tests/modules/compute_vm/test_plan_group.py new file mode 100644 index 0000000000..de198d731c --- /dev/null +++ b/tests/modules/compute_vm/test_plan_group.py @@ -0,0 +1,44 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_unmanaged(plan_runner): + plan, resources = plan_runner(FIXTURES_DIR, instance_count=2, + group='{named_ports={}}') + assert len(resources) == 3 + assert set(r['type'] for r in resources) == set([ + 'google_compute_instance_group', 'google_compute_instance' + ]) + + +def test_managed(plan_runner): + plan, resources = plan_runner( + FIXTURES_DIR, use_instance_template='true', group_manager=( + '{ ' + 'auto_healing_policies=null, named_ports={}, options=null, ' + 'regional=false, target_size=1, update_policy=null, versions=null' + ' }' + ) + ) + assert len(resources) == 2 + assert set(r['type'] for r in resources) == set([ + 'google_compute_instance_group_manager', 'google_compute_instance_template' + ]) diff --git a/tests/infrastructure/hub_and_spoke_vpns/__init__.py b/tests/modules/cos_container_coredns/__init__.py similarity index 86% rename from tests/infrastructure/hub_and_spoke_vpns/__init__.py rename to tests/modules/cos_container_coredns/__init__.py index 086a24e64e..6913f02e36 100644 --- a/tests/infrastructure/hub_and_spoke_vpns/__init__.py +++ b/tests/modules/cos_container_coredns/__init__.py @@ -1,10 +1,10 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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, diff --git a/tests/modules/cos_container_coredns/fixture/main.tf b/tests/modules/cos_container_coredns/fixture/main.tf new file mode 100644 index 0000000000..82f14f8b8d --- /dev/null +++ b/tests/modules/cos_container_coredns/fixture/main.tf @@ -0,0 +1,24 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/cos-container/coredns" + cloud_config = var.cloud_config + config_variables = var.config_variables + coredns_config = var.coredns_config + file_defaults = var.file_defaults + files = var.files +} diff --git a/tests/modules/cos_container_coredns/fixture/outputs.tf b/tests/modules/cos_container_coredns/fixture/outputs.tf new file mode 100644 index 0000000000..3354b14a15 --- /dev/null +++ b/tests/modules/cos_container_coredns/fixture/outputs.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cloud_config" { + value = module.test.cloud_config +} diff --git a/tests/modules/cos_container_coredns/fixture/variables.tf b/tests/modules/cos_container_coredns/fixture/variables.tf new file mode 100644 index 0000000000..d291be3aa7 --- /dev/null +++ b/tests/modules/cos_container_coredns/fixture/variables.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "cloud_config" { + type = string + default = null +} + +variable "config_variables" { + type = map(any) + default = {} +} + +variable "coredns_config" { + type = string + default = null +} + +variable "file_defaults" { + type = object({ + owner = string + permissions = string + }) + default = { + owner = "root" + permissions = "0644" + } +} + +variable "files" { + type = map(object({ + content = string + owner = string + permissions = string + })) + default = {} +} diff --git a/tests/modules/cos_container_coredns/test_apply.py b/tests/modules/cos_container_coredns/test_apply.py new file mode 100644 index 0000000000..205c8aaf74 --- /dev/null +++ b/tests/modules/cos_container_coredns/test_apply.py @@ -0,0 +1,34 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest +import re +import yaml + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_defaults(apply_runner): + "Test defalt configuration." + _, output = apply_runner(FIXTURES_DIR) + cloud_config = output['cloud_config'] + yaml.safe_load(cloud_config) + assert cloud_config.startswith('#cloud-config') + assert re.findall(r'(?m)^\s+\-\s*path:\s*(\S+)', cloud_config) == [ + '/var/lib/docker/daemon.json', '/etc/systemd/resolved.conf', + '/etc/coredns/Corefile', '/etc/systemd/system/coredns.service' + ] diff --git a/tests/foundations/business_units/__init__.py b/tests/modules/cos_container_mysql/__init__.py similarity index 85% rename from tests/foundations/business_units/__init__.py rename to tests/modules/cos_container_mysql/__init__.py index 47be2ee286..6913f02e36 100644 --- a/tests/foundations/business_units/__init__.py +++ b/tests/modules/cos_container_mysql/__init__.py @@ -1,10 +1,10 @@ -# Copyright 2019 Google LLC -# +# Copyright 2020 Google LLC +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# 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, diff --git a/tests/modules/cos_container_mysql/fixture/main.tf b/tests/modules/cos_container_mysql/fixture/main.tf new file mode 100644 index 0000000000..044314f257 --- /dev/null +++ b/tests/modules/cos_container_mysql/fixture/main.tf @@ -0,0 +1,26 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/cos-container/mysql" + cloud_config = var.cloud_config + config_variables = var.config_variables + image = var.image + kms_config = var.kms_config + mysql_config = var.mysql_config + mysql_data_disk = var.mysql_data_disk + mysql_password = var.mysql_password +} diff --git a/tests/modules/cos_container_mysql/fixture/outputs.tf b/tests/modules/cos_container_mysql/fixture/outputs.tf new file mode 100644 index 0000000000..3354b14a15 --- /dev/null +++ b/tests/modules/cos_container_mysql/fixture/outputs.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cloud_config" { + value = module.test.cloud_config +} diff --git a/tests/modules/cos_container_mysql/fixture/variables.tf b/tests/modules/cos_container_mysql/fixture/variables.tf new file mode 100644 index 0000000000..c2c81da028 --- /dev/null +++ b/tests/modules/cos_container_mysql/fixture/variables.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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 "cloud_config" { + type = string + default = null +} + +variable "config_variables" { + type = map(any) + default = {} +} + +variable "image" { + type = string + default = "mysql:5.7" +} + +variable "kms_config" { + type = object({ + project_id = string + keyring = string + location = string + key = string + }) + default = null +} + +variable "mysql_config" { + type = string + default = null +} + +variable "mysql_data_disk" { + type = string + default = null +} + +variable "mysql_password" { + type = string +} diff --git a/tests/modules/cos_container_mysql/test_apply.py b/tests/modules/cos_container_mysql/test_apply.py new file mode 100644 index 0000000000..3672a5bb58 --- /dev/null +++ b/tests/modules/cos_container_mysql/test_apply.py @@ -0,0 +1,55 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest +import re +import yaml + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_defaults(apply_runner): + "Test defalt configuration." + _, output = apply_runner(FIXTURES_DIR, mysql_password='foo') + cloud_config = output['cloud_config'] + yaml.safe_load(cloud_config) + assert cloud_config.startswith('#cloud-config') + assert re.findall(r'(?m)^\s+\-\s*path:\s*(\S+)', cloud_config) == [ + '/var/lib/docker/daemon.json', + '/run/mysql/secrets/mysql-passwd.txt', + '/etc/systemd/system/mysql.service' + ] + assert 'gcloud' not in cloud_config + + +def test_kms(apply_runner): + "Test KMS configuration." + kms_config = ( + '{project_id="my-project", keyring="my-keyring", location="eu", key="foo"}' + ) + _, output = apply_runner( + FIXTURES_DIR, mysql_password='foo', kms_config=kms_config) + cloud_config = output['cloud_config'] + yaml.safe_load(cloud_config) + assert cloud_config.startswith('#cloud-config') + assert re.findall(r'(?m)^\s+\-\s*path:\s*(\S+)', cloud_config) == [ + '/var/lib/docker/daemon.json', + '/run/mysql/secrets/mysql-passwd-cipher.txt', + '/run/mysql/passwd.sh', + '/etc/systemd/system/mysql.service' + ] + assert 'gcloud' in cloud_config diff --git a/tests/modules/dns/__init__.py b/tests/modules/dns/__init__.py new file mode 100644 index 0000000000..6913f02e36 --- /dev/null +++ b/tests/modules/dns/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/dns/fixture/main.tf b/tests/modules/dns/fixture/main.tf new file mode 100644 index 0000000000..8f6bd0a773 --- /dev/null +++ b/tests/modules/dns/fixture/main.tf @@ -0,0 +1,29 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/dns" + project_id = "my-project" + name = "test" + domain = "test.example." + client_networks = [ + "https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default" + ] + type = var.type + forwarders = var.forwarders + peer_network = var.peer_network + recordsets = var.recordsets +} diff --git a/tests/modules/dns/fixture/variables.tf b/tests/modules/dns/fixture/variables.tf new file mode 100644 index 0000000000..a00eaeae15 --- /dev/null +++ b/tests/modules/dns/fixture/variables.tf @@ -0,0 +1,43 @@ +/** + * Copyright 2020 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 "forwarders" { + type = list(string) + default = null +} + +variable "peer_network" { + type = string + default = null +} + +variable "recordsets" { + type = list(object({ + name = string + type = string + ttl = number + records = list(string) + })) + default = [ + { name = "localhost", type = "A", ttl = 300, records = ["127.0.0.1"] }, + { name = "local-host", type = "A", ttl = 300, records = ["127.0.0.2"] } + ] +} + +variable "type" { + type = string + default = "private" +} diff --git a/tests/modules/dns/test_plan.py b/tests/modules/dns/test_plan.py new file mode 100644 index 0000000000..135adc95fe --- /dev/null +++ b/tests/modules/dns/test_plan.py @@ -0,0 +1,80 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_private(plan_runner): + "Test private zone with two recordsets." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 3 + assert set(r['type'] for r in resources) == set([ + 'google_dns_record_set', 'google_dns_managed_zone' + ]) + for r in resources: + if r['type'] != 'google_dns_managed_zone': + continue + assert r['values']['visibility'] == 'private' + assert len(r['values']['private_visibility_config']) == 1 + + +def test_forwarding_recordsets_null_forwarders(plan_runner): + "Test forwarding zone with wrong set of attributes does not break." + _, resources = plan_runner(FIXTURES_DIR, type='forwarding') + assert len(resources) == 1 + resource = resources[0] + assert resource['type'] == 'google_dns_managed_zone' + assert resource['values']['forwarding_config'] == [] + + +def test_forwarding(plan_runner): + "Test forwarding zone with single forwarder." + _, resources = plan_runner( + FIXTURES_DIR, type='forwarding', recordsets='null', + forwarders='["dummy-vpc-self-link"]') + assert len(resources) == 1 + resource = resources[0] + assert resource['type'] == 'google_dns_managed_zone' + assert resource['values']['forwarding_config'] == [{'target_name_servers': [ + {'forwarding_path': '', 'ipv4_address': 'dummy-vpc-self-link'}]}] + + +def test_peering(plan_runner): + "Test peering zone." + _, resources = plan_runner(FIXTURES_DIR, type='peering', + recordsets='null', peer_network='dummy-vpc-self-link') + assert len(resources) == 1 + resource = resources[0] + assert resource['type'] == 'google_dns_managed_zone' + assert resource['values']['peering_config'] == [ + {'target_network': [{'network_url': 'dummy-vpc-self-link'}]}] + + +def test_public(plan_runner): + "Test public zone with two recordsets." + _, resources = plan_runner(FIXTURES_DIR, type='public') + assert len(resources) == 3 + assert set(r['type'] for r in resources) == set([ + 'google_dns_record_set', 'google_dns_managed_zone' + ]) + for r in resources: + if r['type'] != 'google_dns_managed_zone': + continue + assert r['values']['visibility'] == 'public' + assert r['values']['private_visibility_config'] == [] diff --git a/tests/modules/folders/__init__.py b/tests/modules/folders/__init__.py new file mode 100644 index 0000000000..6913f02e36 --- /dev/null +++ b/tests/modules/folders/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/folders/fixture/main.tf b/tests/modules/folders/fixture/main.tf new file mode 100644 index 0000000000..4848ec5aa4 --- /dev/null +++ b/tests/modules/folders/fixture/main.tf @@ -0,0 +1,23 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/folders" + parent = "organizations/12345678" + names = ["folder-a", "folder-b"] + iam_members = var.iam_members + iam_roles = var.iam_roles +} diff --git a/tests/modules/folders/fixture/variables.tf b/tests/modules/folders/fixture/variables.tf new file mode 100644 index 0000000000..02fb11081c --- /dev/null +++ b/tests/modules/folders/fixture/variables.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2020 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 "iam_members" { + type = map(map(list(string))) + default = null +} + +variable "iam_roles" { + type = map(list(string)) + default = null +} diff --git a/tests/modules/folders/test_plan.py b/tests/modules/folders/test_plan.py new file mode 100644 index 0000000000..fcb8fa64d7 --- /dev/null +++ b/tests/modules/folders/test_plan.py @@ -0,0 +1,49 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_folder(plan_runner): + "Test folder resources." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 2 + assert set(r['type'] for r in resources) == set(['google_folder']) + assert set(r['values']['display_name'] for r in resources) == set([ + 'folder-a', 'folder-b' + ]) + assert set(r['values']['parent'] for r in resources) == set([ + 'organizations/12345678' + ]) + + +def test_iam_roles_only(plan_runner): + "Test folder resources with only iam roles passed." + _, resources = plan_runner( + FIXTURES_DIR, iam_roles='{folder-a = [ "roles/owner"]}') + assert len(resources) == 3 + + +def test_iam(plan_runner): + "Test folder resources with iam roles and members." + iam_roles = '{folder-a = ["roles/owner"], folder-b = ["roles/viewer"]}' + iam_members = '{folder-a = { "roles/owner" = ["user:a@b.com"] }}' + _, resources = plan_runner( + FIXTURES_DIR, iam_roles=iam_roles, iam_members=iam_members) + assert len(resources) == 4 diff --git a/tests/modules/gcs/__init__.py b/tests/modules/gcs/__init__.py new file mode 100644 index 0000000000..6913f02e36 --- /dev/null +++ b/tests/modules/gcs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/gcs/fixture/main.tf b/tests/modules/gcs/fixture/main.tf new file mode 100644 index 0000000000..c0f4b4cbff --- /dev/null +++ b/tests/modules/gcs/fixture/main.tf @@ -0,0 +1,28 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/gcs" + project_id = "my-project" + names = ["bucket-a", "bucket-b"] + prefix = var.prefix + iam_members = var.iam_members + iam_roles = var.iam_roles + labels = var.labels + bucket_policy_only = var.bucket_policy_only + force_destroy = var.force_destroy + versioning = var.versioning +} diff --git a/tests/modules/gcs/fixture/variables.tf b/tests/modules/gcs/fixture/variables.tf new file mode 100644 index 0000000000..7caec8c7e8 --- /dev/null +++ b/tests/modules/gcs/fixture/variables.tf @@ -0,0 +1,55 @@ +/** + * Copyright 2020 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 "bucket_policy_only" { + type = map(bool) + default = { bucket-a = false } +} + +variable "force_destroy" { + type = map(bool) + default = { bucket-a = true } +} + +variable "iam_members" { + type = map(map(list(string))) + default = null +} + +variable "iam_roles" { + type = map(list(string)) + default = null +} + +variable "labels" { + type = map(string) + default = { environment = "test" } +} + +variable "prefix" { + type = string + default = "" +} + +variable "storage_class" { + type = string + default = "MULTI_REGIONAL" +} + +variable "versioning" { + type = map(bool) + default = { bucket-a = true } +} diff --git a/tests/modules/gcs/test_plan.py b/tests/modules/gcs/test_plan.py new file mode 100644 index 0000000000..c2a5934390 --- /dev/null +++ b/tests/modules/gcs/test_plan.py @@ -0,0 +1,81 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_buckets(plan_runner): + "Test bucket resources." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 2 + assert set(r['type'] for r in resources) == set(['google_storage_bucket']) + assert set(r['values']['name'] for r in resources) == set([ + 'bucket-a', 'bucket-b' + ]) + assert set(r['values']['project'] for r in resources) == set([ + 'my-project' + ]) + + +def test_prefix(plan_runner): + "Test bucket name when prefix is set." + _, resources = plan_runner(FIXTURES_DIR, prefix='foo') + assert set(r['values']['name'] for r in resources) == set([ + 'foo-eu-bucket-a', 'foo-eu-bucket-b' + ]) + + +def test_map_values(plan_runner): + "Test that map values set the correct attributes on buckets." + _, resources = plan_runner(FIXTURES_DIR) + bpo = dict((r['values']['name'], r['values']['bucket_policy_only']) + for r in resources) + assert bpo == {'bucket-a': False, 'bucket-b': True} + force_destroy = dict((r['values']['name'], r['values']['force_destroy']) + for r in resources) + assert force_destroy == {'bucket-a': True, 'bucket-b': False} + versioning = dict((r['values']['name'], r['values']['versioning']) + for r in resources) + assert versioning == { + 'bucket-a': [{'enabled': True}], 'bucket-b': [{'enabled': False}] + } + for r in resources: + assert r['values']['labels'] == { + 'environment': 'test', 'location': 'eu', + 'storage_class': 'multi_regional', 'name': r['values']['name'] + } + + +def test_iam_roles_only(plan_runner): + "Test bucket resources with only iam roles passed." + _, resources = plan_runner( + FIXTURES_DIR, iam_roles='{bucket-a = [ "roles/storage.admin"]}') + assert len(resources) == 3 + + +def test_iam(plan_runner): + "Test bucket resources with iam roles and members." + iam_roles = ( + '{bucket-a = ["roles/storage.admin"], ' + 'bucket-b = ["roles/storage.objectAdmin"]}' + ) + iam_members = '{folder-a = { "roles/storage.admin" = ["user:a@b.com"] }}' + _, resources = plan_runner( + FIXTURES_DIR, iam_roles=iam_roles, iam_members=iam_members) + assert len(resources) == 4 diff --git a/tests/modules/net-vpc/__init__.py b/tests/modules/net-vpc/__init__.py new file mode 100644 index 0000000000..6913f02e36 --- /dev/null +++ b/tests/modules/net-vpc/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/net-vpc/fixture/main.tf b/tests/modules/net-vpc/fixture/main.tf new file mode 100644 index 0000000000..a9d92d4778 --- /dev/null +++ b/tests/modules/net-vpc/fixture/main.tf @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/net-vpc" + project_id = var.project_id + name = var.name + iam_members = var.iam_members + iam_roles = var.iam_roles + log_configs = var.log_configs + log_config_defaults = var.log_config_defaults + peering_config = var.peering_config + routes = var.routes + shared_vpc_host = var.shared_vpc_host + shared_vpc_service_projects = var.shared_vpc_service_projects + subnets = var.subnets + subnet_descriptions = var.subnet_descriptions + subnet_flow_logs = var.subnet_flow_logs + subnet_private_access = var.subnet_private_access + auto_create_subnetworks = var.auto_create_subnetworks +} diff --git a/tests/modules/net-vpc/fixture/variables.tf b/tests/modules/net-vpc/fixture/variables.tf new file mode 100644 index 0000000000..56f190a207 --- /dev/null +++ b/tests/modules/net-vpc/fixture/variables.tf @@ -0,0 +1,124 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + type = string + default = "my-project" +} + +variable "name" { + type = string + default = "my-vpc" +} + +variable "auto_create_subnetworks" { + type = bool + default = false +} + +variable "iam_roles" { + type = map(list(string)) + default = null +} + +variable "iam_members" { + type = map(map(list(string))) + default = null +} + +variable "log_configs" { + type = map(map(string)) + default = null +} + +variable "log_config_defaults" { + type = object({ + aggregation_interval = string + flow_sampling = number + metadata = string + }) + default = { + aggregation_interval = "INTERVAL_5_SEC" + flow_sampling = 0.5 + metadata = "INCLUDE_ALL_METADATA" + } +} + +variable "peering_config" { + type = object({ + peer_vpc_self_link = string + export_routes = bool + import_routes = bool + }) + default = null +} + +variable "routes" { + type = map(object({ + dest_range = string + priority = number + tags = list(string) + next_hop_type = string # gateway, instance, ip, vpn_tunnel, ilb + next_hop = string + })) + default = null +} + +variable "routing_mode" { + description = "The network routing mode (default 'GLOBAL')" + type = string + default = "GLOBAL" +} + +variable "shared_vpc_host" { + description = "Enable shared VPC for this project." + type = bool + default = false +} + +variable "shared_vpc_service_projects" { + description = "Shared VPC service projects to register with this host" + type = list(string) + default = [] +} + +variable "subnets" { + description = "The list of subnets being created" + type = map(object({ + ip_cidr_range = string + region = string + secondary_ip_range = map(string) + })) + default = null +} + +variable "subnet_descriptions" { + description = "Optional map of subnet descriptions, keyed by subnet name." + type = map(string) + default = {} +} + +variable "subnet_flow_logs" { + description = "Optional map of boolean to control flow logs (default is disabled), keyed by subnet name." + type = map(bool) + default = {} +} + +variable "subnet_private_access" { + description = "Optional map of boolean to control private Google access (default is enabled), keyed by subnet name." + type = map(bool) + default = {} +} diff --git a/tests/modules/net-vpc/test_plan.py b/tests/modules/net-vpc/test_plan.py new file mode 100644 index 0000000000..fc2852caa7 --- /dev/null +++ b/tests/modules/net-vpc/test_plan.py @@ -0,0 +1,90 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') +_VAR_PEER_VPC_CONFIG = ( + '{' + 'peer_vpc_self_link="projects/my-project/global/networks/my-peer-vpc", ' + 'export_routes=true, import_routes=null' + '}' +) +_VAR_ROUTES_TEMPLATE = ( + '{' + ' next-hop-test = {' + ' dest_range="192.168.128.0/24", priority=1000, tags=null, ' + ' next_hop_type="%s", next_hop="%s"},' + ' gateway-test = {' + ' dest_range="0.0.0.0/0", priority=100, tags=["tag-a"], ' + ' next_hop_type="gateway", ' + ' next_hop="global/gateways/default-internet-gateway"}' + '}' +) +_VAR_ROUTES_NEXT_HOPS = { + 'gateway': 'global/gateways/default-internet-gateway', + 'instance': 'zones/europe-west1-b/test', + 'ip': '192.168.0.128', + 'ilb': 'regions/europe-west1/forwardingRules/test', + 'vpn_tunnel': 'regions/europe-west1/vpnTunnels/foo' +} + + +def test_vpc_simple(plan_runner): + "Test vpc with no extra options." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 1 + assert [r['type'] for r in resources] == ['google_compute_network'] + assert [r['values']['name'] for r in resources] == ['my-vpc'] + assert [r['values']['project'] for r in resources] == ['my-project'] + + +def test_vpc_shared(plan_runner): + "Test shared vpc variables." + _, resources = plan_runner(FIXTURES_DIR, shared_vpc_host='true', + shared_vpc_service_projects='["tf-a", "tf-b"]') + assert len(resources) == 4 + assert set(r['type'] for r in resources) == set([ + 'google_compute_network', 'google_compute_shared_vpc_host_project', + 'google_compute_shared_vpc_service_project' + ]) + + +def test_vpc_peering(plan_runner): + "Test vpc peering variables." + _, resources = plan_runner(FIXTURES_DIR, peering_config=_VAR_PEER_VPC_CONFIG) + assert len(resources) == 3 + assert set(r['type'] for r in resources) == set([ + 'google_compute_network', 'google_compute_network_peering' + ]) + peerings = [r['values'] + for r in resources if r['type'] == 'google_compute_network_peering'] + assert [p['name'] for p in peerings] == [ + 'my-vpc-my-peer-vpc', 'my-peer-vpc-my-vpc'] + assert [p['export_custom_routes'] for p in peerings] == [True, False] + assert [p['import_custom_routes'] for p in peerings] == [False, True] + + +def test_vpc_routes(plan_runner): + "Test vpc routes." + for next_hop_type, next_hop in _VAR_ROUTES_NEXT_HOPS.items(): + _var_routes = _VAR_ROUTES_TEMPLATE % (next_hop_type, next_hop) + _, resources = plan_runner(FIXTURES_DIR, routes=_var_routes) + assert len(resources) == 3 + resource = [r for r in resources if r['values'] + ['name'] == 'next-hop-test'][0] + assert resource['values']['next_hop_%s' % next_hop_type] diff --git a/tests/modules/net-vpc/test_plan_subnets.py b/tests/modules/net-vpc/test_plan_subnets.py new file mode 100644 index 0000000000..f08f425203 --- /dev/null +++ b/tests/modules/net-vpc/test_plan_subnets.py @@ -0,0 +1,79 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') +_VAR_SUBNETS = ( + '{ ' + 'a={region = "europe-west1", ip_cidr_range = "10.0.0.0/24",' + ' secondary_ip_range=null},' + 'b={region = "europe-west1", ip_cidr_range = "10.0.1.0/24",' + ' secondary_ip_range=null},' + 'c={region = "europe-west1", ip_cidr_range = "10.0.2.0/24",' + ' secondary_ip_range={a="192.168.0.0/24", b="192.168.1.0/24"}},' + '}' +) +_VAR_LOG_CONFIG = '{a = { flow_sampling = 0.1 }}' +_VAR_LOG_CONFIG_DEFAULTS = ( + '{' + 'aggregation_interval = "INTERVAL_10_MIN", ' + 'flow_sampling = 0.5, ' + 'metadata = "INCLUDE_ALL_METADATA"' + '}' +) + + +def test_subnets_simple(plan_runner): + "Test subnets variable." + _, resources = plan_runner(FIXTURES_DIR, subnets=_VAR_SUBNETS) + assert len(resources) == 4 + subnets = [r['values'] + for r in resources if r['type'] == 'google_compute_subnetwork'] + assert set(s['name'] for s in subnets) == set( + ['my-vpc-a', 'my-vpc-b', 'my-vpc-c']) + assert set(len(s['secondary_ip_range']) for s in subnets) == set([0, 0, 2]) + + +def test_subnet_log_configs(plan_runner): + "Test subnets flow logs configuration and defaults." + _, resources = plan_runner(FIXTURES_DIR, subnets=_VAR_SUBNETS, + log_configs=_VAR_LOG_CONFIG, + log_config_defaults=_VAR_LOG_CONFIG_DEFAULTS, + subnet_flow_logs='{a=true, b=true}') + assert len(resources) == 4 + flow_logs = {} + for r in resources: + if r['type'] != 'google_compute_subnetwork': + continue + flow_logs[r['values']['name']] = r['values']['log_config'] + assert flow_logs == { + # enable, override one default option + 'my-vpc-a': [{ + 'aggregation_interval': 'INTERVAL_10_MIN', + 'flow_sampling': 0.1, + 'metadata': 'INCLUDE_ALL_METADATA' + }], + # enable, use defaults + 'my-vpc-b': [{ + 'aggregation_interval': 'INTERVAL_10_MIN', + 'flow_sampling': 0.5, + 'metadata': 'INCLUDE_ALL_METADATA' + }], + # don't enable + 'my-vpc-c': [] + } diff --git a/tests/modules/project/__init__.py b/tests/modules/project/__init__.py new file mode 100644 index 0000000000..6913f02e36 --- /dev/null +++ b/tests/modules/project/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf new file mode 100644 index 0000000000..91a838f637 --- /dev/null +++ b/tests/modules/project/fixture/main.tf @@ -0,0 +1,35 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "test" { + source = "../../../../modules/project" + name = "my-project" + billing_account = "12345-12345-12345" + auto_create_network = var.auto_create_network + custom_roles = var.custom_roles + iam_members = var.iam_members + iam_roles = var.iam_roles + iam_additive_members = var.iam_additive_members + iam_additive_roles = var.iam_additive_roles + labels = var.labels + lien_reason = var.lien_reason + oslogin = var.oslogin + oslogin_admins = var.oslogin_admins + oslogin_users = var.oslogin_users + parent = var.parent + prefix = var.prefix + services = var.services +} diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf new file mode 100644 index 0000000000..bcf7da90cd --- /dev/null +++ b/tests/modules/project/fixture/variables.tf @@ -0,0 +1,85 @@ +/** + * Copyright 2020 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 "auto_create_network" { + type = bool + default = false +} + +variable "custom_roles" { + type = map(list(string)) + default = {} +} + +variable "iam_members" { + type = map(list(string)) + default = {} +} + +variable "iam_roles" { + type = list(string) + default = [] +} + +variable "iam_additive_members" { + type = map(list(string)) + default = {} +} + +variable "iam_additive_roles" { + type = list(string) + default = [] +} + +variable "labels" { + type = map(string) + default = {} +} + +variable "lien_reason" { + type = string + default = "" +} + +variable "oslogin" { + type = bool + default = false +} + +variable "oslogin_admins" { + type = list(string) + default = [] +} + +variable "oslogin_users" { + type = list(string) + default = [] +} + +variable "parent" { + type = string + default = "folders/12345678" +} + +variable "prefix" { + type = string + default = null +} + +variable "services" { + type = list(string) + default = [] +} diff --git a/tests/modules/project/test_plan.py b/tests/modules/project/test_plan.py new file mode 100644 index 0000000000..4c5aba825d --- /dev/null +++ b/tests/modules/project/test_plan.py @@ -0,0 +1,42 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_prefix(plan_runner): + "Test project id prefix." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 1 + assert resources[0]['values']['name'] == 'my-project' + _, resources = plan_runner(FIXTURES_DIR, prefix='foo') + assert len(resources) == 1 + assert resources[0]['values']['name'] == 'foo-my-project' + + +def test_parent(plan_runner): + "Test project parent." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 1 + assert resources[0]['values']['folder_id'] == '12345678' + assert resources[0]['values'].get('org_id') == None + _, resources = plan_runner(FIXTURES_DIR, parent='organizations/12345678') + assert len(resources) == 1 + assert resources[0]['values']['org_id'] == '12345678' + assert resources[0]['values'].get('folder_id') == None diff --git a/tests/requirements.txt b/tests/requirements.txt index a5b3fbc2d0..f31307ca2c 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ -pytest>=4.3.1 -pytest-tldr>=0.2.1 -tftest>=1.2.0 +pytest>=4.6.0 +PyYAML>=5.3 +tftest>=1.5.0 diff --git a/tools/tfdoc/tfdoc.py b/tools/tfdoc/tfdoc.py index bb6dffb037..1e3fbf2c82 100755 --- a/tools/tfdoc/tfdoc.py +++ b/tools/tfdoc/tfdoc.py @@ -16,6 +16,7 @@ import collections import enum +import glob import os import re import string @@ -241,12 +242,16 @@ def replace_doc(module, doc): def main(module=None, replace=True): "Program entry point." try: - with open(os.path.join(module, 'variables.tf')) as file: - variables = [v for v in parse_items( - file.read(), RE_VARIABLES, VariableToken, Variable, VariableData)] - with open(os.path.join(module, 'outputs.tf')) as file: - outputs = [o for o in parse_items( - file.read(), RE_OUTPUTS, OutputToken, Output, OutputData)] + variables = [] + for path in glob.glob(os.path.join(module, 'variables*tf')): + with open(path) as file: + variables += [v for v in parse_items( + file.read(), RE_VARIABLES, VariableToken, Variable, VariableData)] + outputs = [] + for path in glob.glob(os.path.join(module, 'outputs*tf')): + with open(path) as file: + outputs += [o for o in parse_items( + file.read(), RE_OUTPUTS, OutputToken, Output, OutputData)] except (IOError, OSError) as e: raise SystemExit(e) doc = get_doc(variables, outputs)