diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dc521ef..35047c77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ All notable changes to this project will be documented in this file. ### BLUEPRINTS +- [[#1936](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1936)] Move squid to __need_fixing ([sruffilli](https://github.com/sruffilli)) +- [[#1931](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1931)] Quota monitor blueprint: don't fail quota fetch on deleted project ([ludoo](https://github.com/ludoo)) +- [[#1930](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1930)] Allow granting network user role on host project from project module and factory ([simonebruzzechesse](https://github.com/simonebruzzechesse)) +- [[#1924](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1924)] Update quota monitor blueprint to support project discovery ([maunope](https://github.com/maunope)) +- [[#1912](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1912)] **incompatible change:** Custom role factories for organization and project modules ([ludoo](https://github.com/ludoo)) +- [[#1916](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1916)] Add triggerer configuration for Composer ([wiktorn](https://github.com/wiktorn)) +- [[#1907](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1907)] Add support for subnet-level service network user grants to project module, improve docs ([ludoo](https://github.com/ludoo)) - [[#1871](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1871)] Added workstation-cluster module ([apichick](https://github.com/apichick)) - [[#1886](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1886)] Fixes to F5 blueprint docs ([LucaPrete](https://github.com/LucaPrete)) - [[#1874](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1874)] Added PSC support to CloudSQL Module ([luigi-bitonti](https://github.com/luigi-bitonti)) @@ -15,16 +22,38 @@ All notable changes to this project will be documented in this file. ### DOCUMENTATION +- [[#1936](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1936)] Move squid to __need_fixing ([sruffilli](https://github.com/sruffilli)) - [[#1890](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1890)] Use TFTEST_E2E_ instead of TF_VAR variables ([wiktorn](https://github.com/wiktorn)) - [[#1871](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1871)] Added workstation-cluster module ([apichick](https://github.com/apichick)) - [[#1883](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1883)] F5 deployment blueprint ([LucaPrete](https://github.com/LucaPrete)) ### FAST +- [[#1932](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1932)] Simplify organization tags.tf locals ([juliocc](https://github.com/juliocc)) +- [[#1912](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1912)] **incompatible change:** Custom role factories for organization and project modules ([ludoo](https://github.com/ludoo)) - [[#1900](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1900)] Patch Github actions ci google-github-actions/auth@v0 --> v2 ([ibrahimparvez2](https://github.com/ibrahimparvez2)) ### MODULES +- [[#1936](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1936)] Move squid to __need_fixing ([sruffilli](https://github.com/sruffilli)) +- [[#1935](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1935)] E2E tests fixes ([wiktorn](https://github.com/wiktorn)) +- [[#1933](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1933)] Add project-scoped secure tags ([juliocc](https://github.com/juliocc)) +- [[#1932](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1932)] Simplify organization tags.tf locals ([juliocc](https://github.com/juliocc)) +- [[#1930](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1930)] Allow granting network user role on host project from project module and factory ([simonebruzzechesse](https://github.com/simonebruzzechesse)) +- [[#1928](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1928)] **incompatible change:** Fix health check autocreation and id output in passthrough LB modules ([ludoo](https://github.com/ludoo)) +- [[#1926](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1926)] Add support for policy based routes to net-vpc ([sruffilli](https://github.com/sruffilli)) +- [[#1905](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1905)] gke-cluster-standard : Support upgrade_settings for node auto provisioner ([noony](https://github.com/noony)) +- [[#1923](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1923)] Removed deprecated variable and added labels ([luigi-bitonti](https://github.com/luigi-bitonti)) +- [[#1922](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1922)] can_ip_forward in simple-nva examples ([sruffilli](https://github.com/sruffilli)) +- [[#1921](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1921)] Sync tf version to version used by tests ([wiktorn](https://github.com/wiktorn)) +- [[#1920](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1920)] Bump tf version ([ludoo](https://github.com/ludoo)) +- [[#1918](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1918)] Added missing parameters in kubelet and linux node configuration ([luigi-bitonti](https://github.com/luigi-bitonti)) +- [[#1917](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1917)] Added the possibility to configure maintenance window and deny maintenance period in Cloud SQL module module ([francesco-pavan-huware](https://github.com/francesco-pavan-huware)) +- [[#1912](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1912)] **incompatible change:** Custom role factories for organization and project modules ([ludoo](https://github.com/ludoo)) +- [[#1909](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1909)] net_lb_ext module e2e and example testing changes ([dibaskar-google](https://github.com/dibaskar-google)) +- [[#1908](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1908)] README fixes for #1907 ([wiktorn](https://github.com/wiktorn)) +- [[#1906](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1906)] gke-cluster-standard : Set optional shielded_instance_config block in cluster_autoscaling.auto_provisioning_defaults ([noony](https://github.com/noony)) +- [[#1907](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1907)] Add support for subnet-level service network user grants to project module, improve docs ([ludoo](https://github.com/ludoo)) - [[#1904](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1904)] gke-cluster-standard : Add possibility to enable image streaming feature at cluster level ([noony](https://github.com/noony)) - [[#1903](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1903)] Enable sole tenancy (`node_affinities`) on compute_vm ([LucaPrete](https://github.com/LucaPrete)) - [[#1901](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1901)] Add IPv6 to HA VPN module + test inventories ([LucaPrete](https://github.com/LucaPrete)) @@ -41,6 +70,7 @@ All notable changes to this project will be documented in this file. ### TOOLS +- [[#1932](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1932)] Simplify organization tags.tf locals ([juliocc](https://github.com/juliocc)) - [[#1890](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1890)] Use TFTEST_E2E_ instead of TF_VAR variables ([wiktorn](https://github.com/wiktorn)) ## [28.0.0] - 2023-11-24 diff --git a/blueprints/README.md b/blueprints/README.md index 75e2d21e..b9851bde 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -9,7 +9,7 @@ Currently available blueprints: - **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Minimal Data Platform](./data-solutions/data-platform-minimal), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground), [MLOps with Vertex AI](./data-solutions/vertex-mlops), [Shielded Folder](./data-solutions/shielded-folder), [BigQuery ML and Vertex AI Pipeline](./data-solutions/bq-ml) - **factories** - [The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory) - **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [GKE Autopilot](./gke/autopilot) -- **networking** - [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [HA VPN over Interconnect](./networking/ha-vpn-over-interconnect/), [GLB and multi-regional daisy-chaining through hybrid NEGs](./networking/glb-hybrid-neg-internal), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), On-prem DNS and Google Private Access, [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke) +- **networking** - [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [HA VPN over Interconnect](./networking/ha-vpn-over-interconnect/), [GLB and multi-regional daisy-chaining through hybrid NEGs](./networking/glb-hybrid-neg-internal), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), On-prem DNS and Google Private Access, [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke) - **serverless** - [Cloud Run series](./serverless/cloud-run-explore) - **third party solutions** - [OpenShift on GCP user-provisioned infrastructure](./third-party-solutions/openshift), [Wordpress deployment on Cloud Run](./third-party-solutions/wordpress/cloudrun) diff --git a/blueprints/cloud-operations/quota-monitoring/README.md b/blueprints/cloud-operations/quota-monitoring/README.md index aeaa9b4d..26a702e8 100644 --- a/blueprints/cloud-operations/quota-monitoring/README.md +++ b/blueprints/cloud-operations/quota-monitoring/README.md @@ -38,9 +38,10 @@ The region, location of the bundle used to deploy the function, and scheduling f The `quota_config` variable mirrors the arguments accepted by the Python program, and allows configuring several different aspects of its behaviour: +- `quota_config.discover_root` organization or folder to be used to discover all underlying projects to track quotas for, in `organizations/nnnnn` or `folders/nnnnn` format - `quota_config.exclude` do not generate metrics for quotas matching prefixes listed here - `quota_config.include` only generate metrics for quotas matching prefixes listed here -- `quota_config.projects` projects to track quotas for, defaults to the project where metrics are stored +- `quota_config.projects` projects to track quotas for, defaults to the project where metrics are stored, if projects are automatically discovered, those in this list are appended. - `quota_config.regions` regions to track quotas for, defaults to the `global` region for project-level quotas - `dry_run` do not write actual metrics - `verbose` increase logging verbosity @@ -54,7 +55,6 @@ Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/c - `terraform init` - `terraform apply -var project_id=my-project-id` - ## Variables | name | description | type | required | default | @@ -64,10 +64,9 @@ Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/c | [bundle_path](variables.tf#L33) | Path used to write the intermediate Cloud Function code bundle. | string | | "./bundle.zip" | | [name](variables.tf#L39) | Arbitrary string used to name created resources. | string | | "quota-monitor" | | [project_create_config](variables.tf#L45) | Create project instead of using an existing one. | object({…}) | | null | -| [quota_config](variables.tf#L59) | Cloud function configuration. | object({…}) | | {} | -| [region](variables.tf#L76) | Compute region used in the example. | string | | "europe-west1" | -| [schedule_config](variables.tf#L82) | Schedule timer configuration in crontab format. | string | | "0 * * * *" | - +| [quota_config](variables.tf#L59) | Cloud function configuration. | object({…}) | | {} | +| [region](variables.tf#L85) | Compute region used in the example. | string | | "europe-west1" | +| [schedule_config](variables.tf#L91) | Schedule timer configuration in crontab format. | string | | "0 * * * *" | ## Test @@ -80,5 +79,5 @@ module "test" { billing_account = "12345-ABCDE-12345" } } -# tftest modules=4 resources=14 +# tftest modules=4 resources=19 ``` diff --git a/blueprints/cloud-operations/quota-monitoring/main.tf b/blueprints/cloud-operations/quota-monitoring/main.tf index a49891c0..d5f9a486 100644 --- a/blueprints/cloud-operations/quota-monitoring/main.tf +++ b/blueprints/cloud-operations/quota-monitoring/main.tf @@ -20,6 +20,8 @@ locals { ? [var.project_id] : var.quota_config.projects ) + discovery_root_type = split("/", coalesce(var.quota_config["discovery_root"], "/"))[0] + discovery_root_id = split("/", coalesce(var.quota_config["discovery_root"], "/"))[1] } module "project" { @@ -29,8 +31,11 @@ module "project" { parent = try(var.project_create_config.parent, null) project_create = var.project_create_config != null services = [ - "compute.googleapis.com", - "cloudfunctions.googleapis.com" + "cloudasset.googleapis.com", + "cloudbuild.googleapis.com", + "cloudfunctions.googleapis.com", + "cloudscheduler.googleapis.com", + "compute.googleapis.com" ] } @@ -81,6 +86,55 @@ resource "google_cloud_scheduler_job" "default" { } } +resource "google_organization_iam_member" "org_asset_viewer" { + count = local.discovery_root_type == "organizations" ? 1 : 0 + org_id = local.discovery_root_id + role = "roles/cloudasset.viewer" + member = module.cf.service_account_iam_email +} + + +# role with the least privilege including compute.projects.get permission +resource "google_organization_iam_member" "org_network_viewer" { + count = local.discovery_root_type == "organizations" ? 1 : 0 + org_id = local.discovery_root_id + role = "roles/compute.networkViewer" + member = module.cf.service_account_iam_email +} + +resource "google_organization_iam_member" "org_quota_viewer" { + count = local.discovery_root_type == "organizations" ? 1 : 0 + org_id = local.discovery_root_id + role = "roles/servicemanagement.quotaViewer" + member = module.cf.service_account_iam_email +} + +resource "google_folder_iam_member" "folder_asset_viewer" { + count = local.discovery_root_type == "folders" ? 1 : 0 + folder = local.discovery_root_id + role = "roles/cloudasset.viewer" + member = module.cf.service_account_iam_email +} + +# role with the least privilege including compute.projects.get permission +resource "google_folder_iam_member" "folder_network_viewer" { + count = local.discovery_root_type == "folders" ? 1 : 0 + folder = local.discovery_root_id + role = "roles/compute.networkViewer" + member = module.cf.service_account_iam_email +} + +resource "google_folder_iam_member" "folder_quota_viewer" { + count = local.discovery_root_type == "folders" ? 1 : 0 + folder = local.discovery_root_id + role = "roles/servicemanagement.quotaViewer" + member = module.cf.service_account_iam_email +} + + + + + resource "google_project_iam_member" "metric_writer" { project = module.project.project_id role = "roles/monitoring.metricWriter" diff --git a/blueprints/cloud-operations/quota-monitoring/src/main.py b/blueprints/cloud-operations/quota-monitoring/src/main.py index 5a845364..27ade68a 100755 --- a/blueprints/cloud-operations/quota-monitoring/src/main.py +++ b/blueprints/cloud-operations/quota-monitoring/src/main.py @@ -39,6 +39,9 @@ HTTP_HEADERS = {'content-type': 'application/json; charset=UTF-8'} URL_PROJECT = 'https://compute.googleapis.com/compute/v1/projects/{}' URL_REGION = 'https://compute.googleapis.com/compute/v1/projects/{}/regions/{}' URL_TS = 'https://monitoring.googleapis.com/v3/projects/{}/timeSeries' +URL_DISCOVERY = ('https://cloudasset.googleapis.com/v1/{}/assets?' + 'assetTypes=cloudresourcemanager.googleapis.com%2FProject&' + 'contentType=RESOURCE&pageSize=100&pageToken={}') _Quota = collections.namedtuple('_Quota', 'project region tstamp metric limit usage') @@ -48,6 +51,10 @@ HTTPRequest = collections.namedtuple( }]) +class NotFound(Exception): + pass + + class Quota(_Quota): 'Compute quota.' @@ -80,8 +87,8 @@ class Quota(_Quota): else: d['valueType'] = 'INT64' d['points'][0]['value'] = {'int64Value': value} - # remove this label if cardinality gets too high - d['metric']['labels']['quota'] = f'{self.usage}/{self.limit}' + # re-enable the following line if cardinality is not a problem + # d['metric']['labels']['quota'] = f'{self.usage}/{self.limit}' return d @property @@ -92,7 +99,7 @@ class Quota(_Quota): ratio = 0 yield self._api_format('ratio', ratio) yield self._api_format('usage', self.usage) - # yield self._api_format('limit', self.limit) + yield self._api_format('limit', self.limit) def batched(iterable, n): @@ -112,6 +119,23 @@ def configure_logging(verbose=True): warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) +def discover_projects(discovery_root): + 'Discovers projects under a folder or organization.' + if discovery_root.partition('/')[0] not in ('folders', 'organizations'): + raise SystemExit(f'Invalid discovery root {discovery_root}.') + next_page_token = '' + while True: + list_assets_results = fetch( + HTTPRequest(URL_DISCOVERY.format(discovery_root, next_page_token))) + if 'assets' in list_assets_results: + for asset in list_assets_results['assets']: + if (asset['resource']['data']['lifecycleState'] == 'ACTIVE'): + yield asset['resource']['data']['projectId'] + next_page_token = list_assets_results.get('nextPageToken') + if not next_page_token: + break + + def fetch(request, delete=False): 'Minimal HTTP client interface for API calls.' logging.debug(f'fetch {"POST" if request.data else "GET"} {request.url}') @@ -132,6 +156,9 @@ def fetch(request, delete=False): except json.JSONDecodeError as e: logging.critical(e) raise SystemExit(f'Error decoding response: {response.content}') + if response.status_code == 404: + raise NotFound( + f'Resource not found. Error: {rdata.get("error")} URL: {request.url}') if response.status_code != 200: logging.critical(rdata) error = rdata.get('error', {}) @@ -155,17 +182,25 @@ def get_quotas(project, region='global'): request = HTTPRequest(URL_PROJECT.format(project)) else: request = HTTPRequest(URL_REGION.format(project, region)) - resp = fetch(request) - ts = datetime.datetime.utcnow() - for quota in resp.get('quotas'): - yield Quota(project, region, ts, **quota) + try: + resp = fetch(request) + except NotFound as e: + logging.warn(e.args[0]) + else: + ts = datetime.datetime.utcnow() + for quota in resp.get('quotas'): + yield Quota(project, region, ts, **quota) @click.command() @click.argument('project-id', required=True) +@click.option( + '--discovery-root', '-dr', required=False, help= + 'Root node used to dynamically fetch projects, in organizations/nnn or folders/nnn format.' +) @click.option( '--project-ids', multiple=True, help= - 'Project ids to monitor (multiple). Defaults to monitoring project if not set.' + 'Project ids to monitor (multiple). Defaults to monitoring project if not set, values are appended to those found under discovery-root' ) @click.option('--regions', multiple=True, help='Regions (multiple). Defaults to "global" if not set.') @@ -175,11 +210,13 @@ def get_quotas(project, region='global'): help='Exclude quotas starting with keyword (multiple).') @click.option('--dry-run', is_flag=True, help='Do not write metrics.') @click.option('--verbose', is_flag=True, help='Verbose output.') -def main_cli(project_id=None, project_ids=None, regions=None, include=None, - exclude=None, dry_run=False, verbose=False): +def main_cli(project_id=None, discovery_root=None, project_ids=None, + regions=None, include=None, exclude=None, dry_run=False, + verbose=False): 'Fetch GCE quotas and writes them as custom metrics to Stackdriver.' try: - _main(project_id, project_ids, regions, include, exclude, dry_run, verbose) + _main(project_id, discovery_root, project_ids, regions, include, exclude, + dry_run, verbose) except RuntimeError as e: logging.exception(f'exception raised: {e.args[0]}') @@ -193,14 +230,18 @@ def main(event, context): raise -def _main(monitoring_project, projects=None, regions=None, include=None, - exclude=None, dry_run=False, verbose=False): +def _main(monitoring_project, discovery_root=None, projects=None, regions=None, + include=None, exclude=None, dry_run=False, verbose=False): """Module entry point used by cli and cloud function wrappers.""" configure_logging(verbose=verbose) - projects = projects or [monitoring_project] + + # default to monitoring scope project if projects parameter is not passed, then merge the list with discovered projects, if any regions = regions or ['global'] include = set(include or []) exclude = set(exclude or []) + projects = projects or [monitoring_project] + if (discovery_root): + projects = set(list(projects) + list(discover_projects(discovery_root))) for k in ('monitoring_project', 'projects', 'regions', 'include', 'exclude'): logging.debug(f'{k} {locals().get(k)}') timeseries = [] diff --git a/blueprints/cloud-operations/quota-monitoring/variables.tf b/blueprints/cloud-operations/quota-monitoring/variables.tf index 21cf7653..737cf0f9 100644 --- a/blueprints/cloud-operations/quota-monitoring/variables.tf +++ b/blueprints/cloud-operations/quota-monitoring/variables.tf @@ -63,14 +63,23 @@ variable "quota_config" { "a2", "c2", "c2d", "committed", "g2", "interconnect", "m1", "m2", "m3", "nvidia", "preemptible" ]) - include = optional(list(string)) - projects = optional(list(string)) - regions = optional(list(string)) - dry_run = optional(bool, false) - verbose = optional(bool, false) + discovery_root = optional(string, "") + dry_run = optional(bool, false) + include = optional(list(string)) + projects = optional(list(string)) + regions = optional(list(string)) + verbose = optional(bool, false) }) nullable = false default = {} + validation { + condition = ( + var.quota_config.discovery_root == "" || + startswith(var.quota_config.discovery_root, "folders/") || + startswith(var.quota_config.discovery_root, "organizations/") + ) + error_message = "non-null discovery root needs to start with folders/ or organizations/" + } } variable "region" { diff --git a/blueprints/factories/project-factory/README.md b/blueprints/factories/project-factory/README.md index 231ca8dc..67081df2 100644 --- a/blueprints/factories/project-factory/README.md +++ b/blueprints/factories/project-factory/README.md @@ -57,7 +57,7 @@ module "project-factory" { # location where the yaml files are read from factory_data_path = "data" } -# tftest modules=7 resources=31 files=prj-app-1,prj-app-2,prj-app-3 inventory=example.yaml +# tftest modules=7 resources=33 files=prj-app-1,prj-app-2,prj-app-3 inventory=example.yaml ``` ```yaml @@ -85,9 +85,15 @@ service_accounts: ```yaml labels: - app: app-2 - team: foo + app: app-2 + team: foo parent: folders/12345678 +org_policies: + "compute.restrictSharedVpcSubnetworks": + rules: + - allow: + values: + - projects/foo-host/regions/europe-west1/subnetworks/prod-default-ew1 service_accounts: app-2-be: {} services: @@ -98,13 +104,17 @@ services: shared_vpc_service_config: host_project: foo-host service_identity_iam: - "roles/compute.networkUser": - - cloudservices - - container-engine "roles/vpcaccess.user": - - cloudrun + - cloudrun "roles/container.hostServiceAgentUser": - - container-engine + - container-engine + service_identity_subnet_iam: + europe-west1/prod-default-ew1: + - cloudservices + - container-engine + network_subnet_users: + europe-west1/prod-default-ew1: + - group:team-1@example.com # tftest-file id=prj-app-2 path=data/prj-app-2.yaml ``` @@ -117,15 +127,16 @@ services: # tftest-file id=prj-app-3 path=data/prj-app-3.yaml ``` + ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factory_data_path](variables.tf#L89) | Path to folder with YAML project description data files. | string | ✓ | | -| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | -| [data_merges](variables.tf#L47) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | -| [data_overrides](variables.tf#L67) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | +| [factory_data_path](variables.tf#L91) | Path to folder with YAML project description data files. | string | ✓ | | +| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | +| [data_merges](variables.tf#L49) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | +| [data_overrides](variables.tf#L69) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | ## Outputs @@ -134,6 +145,7 @@ services: | [projects](outputs.tf#L17) | Project module outputs. | | | [service_accounts](outputs.tf#L22) | Service account emails. | | + ## Tests These tests validate fixes to the project factory. diff --git a/blueprints/factories/project-factory/factory.tf b/blueprints/factories/project-factory/factory.tf index b8da24a4..4028186c 100644 --- a/blueprints/factories/project-factory/factory.tf +++ b/blueprints/factories/project-factory/factory.tf @@ -79,9 +79,11 @@ locals { try(v.shared_vpc_service_config, null) != null ? merge( { + network_users = [] service_identity_iam = {} service_identity_subnet_iam = {} service_iam_grants = [] + network_subnet_users = {} }, v.shared_vpc_service_config ) diff --git a/blueprints/factories/project-factory/variables.tf b/blueprints/factories/project-factory/variables.tf index 18b315d6..d37f9399 100644 --- a/blueprints/factories/project-factory/variables.tf +++ b/blueprints/factories/project-factory/variables.tf @@ -29,9 +29,11 @@ variable "data_defaults" { services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string + network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) + network_subnet_users = optional(map(list(string)), {}) }), { host_project = null }) tag_bindings = optional(map(string), {}) # non-project resources diff --git a/blueprints/networking/README.md b/blueprints/networking/README.md index 40660022..9dca3f97 100644 --- a/blueprints/networking/README.md +++ b/blueprints/networking/README.md @@ -73,14 +73,14 @@ The emulated on-premises environment can be used to test access to different ser
---> - ### Network filtering with Squid This [blueprint](./filtering-proxy/) how to deploy a filtering HTTP proxy to restrict Internet access, in a simplified setup using a VPC with two subnets and a Cloud DNS zone, and an optional MIG for scaling.
+--> + ### Shared VPC with GKE and per-subnet support This [blueprint](./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. diff --git a/blueprints/networking/__need_fixing/README.md b/blueprints/networking/__need_fixing/README.md index 21d3e674..44a0006d 100644 --- a/blueprints/networking/__need_fixing/README.md +++ b/blueprints/networking/__need_fixing/README.md @@ -3,3 +3,4 @@ The blueprints in this folder are either deprecated or need work on them. - nginx reverse proxy cluster needs tests and resolving a cycle +- filtering-proxy needs upstream `cloud-config-container/__need_fixing/squid` to be fixed diff --git a/blueprints/networking/filtering-proxy-psc/README.md b/blueprints/networking/__need_fixing/filtering-proxy-psc/README.md similarity index 97% rename from blueprints/networking/filtering-proxy-psc/README.md rename to blueprints/networking/__need_fixing/filtering-proxy-psc/README.md index dd9f0585..db5149bc 100644 --- a/blueprints/networking/filtering-proxy-psc/README.md +++ b/blueprints/networking/__need_fixing/filtering-proxy-psc/README.md @@ -29,10 +29,9 @@ To simplify the usage of the proxy, a Cloud DNS private zone is created in each ## Test - ```hcl module "test" { - source = "./fabric/blueprints/networking/filtering-proxy-psc" + source = "./fabric/blueprints/networking/__need_fixing/filtering-proxy-psc" prefix = "fabric" project_create = { billing_account = "123456-ABCDEF-123456" diff --git a/blueprints/networking/filtering-proxy-psc/consumer.tf b/blueprints/networking/__need_fixing/filtering-proxy-psc/consumer.tf similarity index 95% rename from blueprints/networking/filtering-proxy-psc/consumer.tf rename to blueprints/networking/__need_fixing/filtering-proxy-psc/consumer.tf index 08f5b413..38849ee0 100644 --- a/blueprints/networking/filtering-proxy-psc/consumer.tf +++ b/blueprints/networking/__need_fixing/filtering-proxy-psc/consumer.tf @@ -19,7 +19,7 @@ ############################################################################### module "vpc-consumer" { - source = "../../../modules/net-vpc" + source = "../../../../modules/net-vpc" project_id = module.project.project_id name = "${var.prefix}-app" subnets = [ @@ -36,7 +36,7 @@ module "vpc-consumer" { ############################################################################### module "test-vm-consumer" { - source = "../../../modules/compute-vm" + source = "../../../../modules/compute-vm" project_id = module.project.project_id zone = "${var.region}-b" name = "${var.prefix}-test-vm" @@ -83,7 +83,7 @@ resource "google_compute_forwarding_rule" "psc_ilb_consumer" { ############################################################################### module "private-dns" { - source = "../../../modules/dns" + source = "../../../../modules/dns" project_id = module.project.project_id name = "${var.prefix}-internal" zone_config = { @@ -99,7 +99,7 @@ module "private-dns" { } module "firewall-consumer" { - source = "../../../modules/net-vpc-firewall" + source = "../../../../modules/net-vpc-firewall" project_id = module.project.project_id network = module.vpc-consumer.name } diff --git a/blueprints/networking/filtering-proxy-psc/main.tf b/blueprints/networking/__need_fixing/filtering-proxy-psc/main.tf similarity index 92% rename from blueprints/networking/filtering-proxy-psc/main.tf rename to blueprints/networking/__need_fixing/filtering-proxy-psc/main.tf index 6908197f..ed35be3a 100644 --- a/blueprints/networking/filtering-proxy-psc/main.tf +++ b/blueprints/networking/__need_fixing/filtering-proxy-psc/main.tf @@ -19,7 +19,7 @@ ############################################################################### module "project" { - source = "../../../modules/project" + source = "../../../../modules/project" project_create = var.project_create != null billing_account = try(var.project_create.billing_account, null) parent = try(var.project_create.parent, null) @@ -33,7 +33,7 @@ module "project" { } module "vpc" { - source = "../../../modules/net-vpc" + source = "../../../../modules/net-vpc" project_id = module.project.project_id name = "${var.prefix}-vpc" subnets = [ @@ -53,7 +53,7 @@ module "vpc" { } module "firewall" { - source = "../../../modules/net-vpc-firewall" + source = "../../../../modules/net-vpc-firewall" project_id = module.project.project_id network = module.vpc.name ingress_rules = { @@ -73,7 +73,7 @@ module "firewall" { } module "nat" { - source = "../../../modules/net-cloudnat" + source = "../../../../modules/net-cloudnat" project_id = module.project.project_id region = var.region name = "default" @@ -118,7 +118,7 @@ resource "google_compute_service_attachment" "service_attachment" { ############################################################################### module "service-account-squid" { - source = "../../../modules/iam-service-account" + source = "../../../../modules/iam-service-account" project_id = module.project.project_id name = "svc-squid" iam_project_roles = { @@ -130,7 +130,7 @@ module "service-account-squid" { } module "cos-squid" { - source = "../../../modules/cloud-config-container/squid" + source = "../../../../modules/cloud-config-container/__need_fixing/squid" allow = var.allowed_domains clients = [var.cidrs.app] squid_config = "${path.module}/squid.conf" @@ -140,7 +140,7 @@ module "cos-squid" { } module "squid-vm" { - source = "../../../modules/compute-vm" + source = "../../../../modules/compute-vm" project_id = module.project.project_id zone = "${var.region}-b" name = "squid-vm" @@ -165,7 +165,7 @@ module "squid-vm" { } module "squid-mig" { - source = "../../../modules/compute-mig" + source = "../../../../modules/compute-mig" project_id = module.project.project_id location = "${var.region}-b" name = "squid-mig" @@ -202,7 +202,7 @@ module "squid-mig" { } module "squid-ilb" { - source = "../../../modules/net-lb-int" + source = "../../../../modules/net-lb-int" project_id = module.project.project_id region = var.region name = "squid-ilb" diff --git a/blueprints/networking/filtering-proxy-psc/squid.conf b/blueprints/networking/__need_fixing/filtering-proxy-psc/squid.conf similarity index 100% rename from blueprints/networking/filtering-proxy-psc/squid.conf rename to blueprints/networking/__need_fixing/filtering-proxy-psc/squid.conf diff --git a/blueprints/networking/filtering-proxy-psc/startup.sh b/blueprints/networking/__need_fixing/filtering-proxy-psc/startup.sh similarity index 100% rename from blueprints/networking/filtering-proxy-psc/startup.sh rename to blueprints/networking/__need_fixing/filtering-proxy-psc/startup.sh diff --git a/blueprints/networking/filtering-proxy-psc/variables.tf b/blueprints/networking/__need_fixing/filtering-proxy-psc/variables.tf similarity index 100% rename from blueprints/networking/filtering-proxy-psc/variables.tf rename to blueprints/networking/__need_fixing/filtering-proxy-psc/variables.tf diff --git a/blueprints/networking/filtering-proxy/README.md b/blueprints/networking/__need_fixing/filtering-proxy/README.md similarity index 95% rename from blueprints/networking/filtering-proxy/README.md rename to blueprints/networking/__need_fixing/filtering-proxy/README.md index 70dcf6df..b2c1d38e 100644 --- a/blueprints/networking/filtering-proxy/README.md +++ b/blueprints/networking/__need_fixing/filtering-proxy/README.md @@ -41,7 +41,7 @@ You can optionally deploy the Squid server as [Managed Instance Group](https://c ```hcl module "test1" { - source = "./fabric/blueprints/networking/filtering-proxy" + source = "./fabric/blueprints/networking/__need_fixing/filtering-proxy" billing_account = "123456-123456-123456" mig = true prefix = "fabric" @@ -52,7 +52,7 @@ module "test1" { ```hcl module "test2" { - source = "./fabric/blueprints/networking/filtering-proxy" + source = "./fabric/blueprints/networking/__need_fixing/filtering-proxy" billing_account = "123456-123456-123456" mig = false prefix = "fabric" diff --git a/blueprints/networking/filtering-proxy/main.tf b/blueprints/networking/__need_fixing/filtering-proxy/main.tf similarity index 90% rename from blueprints/networking/filtering-proxy/main.tf rename to blueprints/networking/__need_fixing/filtering-proxy/main.tf index 107ca1f7..d8036cb2 100644 --- a/blueprints/networking/filtering-proxy/main.tf +++ b/blueprints/networking/__need_fixing/filtering-proxy/main.tf @@ -27,7 +27,7 @@ locals { ############################################################################### module "folder-netops" { - source = "../../../modules/folder" + source = "../../../../modules/folder" parent = var.root_node name = "netops" } @@ -37,7 +37,7 @@ module "folder-netops" { ############################################################################### module "project-host" { - source = "../../../modules/project" + source = "../../../../modules/project" billing_account = var.billing_account name = "host" parent = module.folder-netops.id @@ -53,7 +53,7 @@ module "project-host" { } module "vpc" { - source = "../../../modules/net-vpc" + source = "../../../../modules/net-vpc" project_id = module.project-host.project_id name = "vpc" subnets = [ @@ -71,7 +71,7 @@ module "vpc" { } module "firewall" { - source = "../../../modules/net-vpc-firewall" + source = "../../../../modules/net-vpc-firewall" project_id = module.project-host.project_id network = module.vpc.name ingress_rules = { @@ -91,7 +91,7 @@ module "firewall" { } module "nat" { - source = "../../../modules/net-cloudnat" + source = "../../../../modules/net-cloudnat" project_id = module.project-host.project_id region = var.region name = "default" @@ -114,7 +114,7 @@ module "nat" { } module "private-dns" { - source = "../../../modules/dns" + source = "../../../../modules/dns" project_id = module.project-host.project_id name = "internal" zone_config = { @@ -134,7 +134,7 @@ module "private-dns" { ############################################################################### module "service-account-squid" { - source = "../../../modules/iam-service-account" + source = "../../../../modules/iam-service-account" project_id = module.project-host.project_id name = "svc-squid" iam_project_roles = { @@ -146,13 +146,13 @@ module "service-account-squid" { } module "cos-squid" { - source = "../../../modules/cloud-config-container/squid" + source = "../../../../modules/cloud-config-container/__need_fixing/squid" allow = var.allowed_domains clients = [var.cidrs.apps] } module "squid-vm" { - source = "../../../modules/compute-vm" + source = "../../../../modules/compute-vm" project_id = module.project-host.project_id zone = "${var.region}-b" name = "squid-vm" @@ -177,7 +177,7 @@ module "squid-vm" { module "squid-mig" { count = var.mig ? 1 : 0 - source = "../../../modules/compute-mig" + source = "../../../../modules/compute-mig" project_id = module.project-host.project_id location = "${var.region}-b" name = "squid-mig" @@ -206,7 +206,7 @@ module "squid-mig" { module "squid-ilb" { count = var.mig ? 1 : 0 - source = "../../../modules/net-lb-int" + source = "../../../../modules/net-lb-int" project_id = module.project-host.project_id region = var.region name = "squid-ilb" @@ -236,7 +236,7 @@ module "squid-ilb" { ############################################################################### module "folder-apps" { - source = "../../../modules/folder" + source = "../../../../modules/folder" parent = var.root_node name = "apps" org_policies = { @@ -248,7 +248,7 @@ module "folder-apps" { } module "project-app" { - source = "../../../modules/project" + source = "../../../../modules/project" billing_account = var.billing_account name = "app1" parent = module.folder-apps.id @@ -263,7 +263,7 @@ module "project-app" { } module "test-vm" { - source = "../../../modules/compute-vm" + source = "../../../../modules/compute-vm" project_id = module.project-app.project_id zone = "${var.region}-b" name = "test-vm" diff --git a/blueprints/networking/filtering-proxy/outputs.tf b/blueprints/networking/__need_fixing/filtering-proxy/outputs.tf similarity index 100% rename from blueprints/networking/filtering-proxy/outputs.tf rename to blueprints/networking/__need_fixing/filtering-proxy/outputs.tf diff --git a/blueprints/networking/filtering-proxy/squid.png b/blueprints/networking/__need_fixing/filtering-proxy/squid.png similarity index 100% rename from blueprints/networking/filtering-proxy/squid.png rename to blueprints/networking/__need_fixing/filtering-proxy/squid.png diff --git a/blueprints/networking/filtering-proxy/variables.tf b/blueprints/networking/__need_fixing/filtering-proxy/variables.tf similarity index 100% rename from blueprints/networking/filtering-proxy/variables.tf rename to blueprints/networking/__need_fixing/filtering-proxy/variables.tf diff --git a/default-versions.tf b/default-versions.tf index 4d3bd967..4a283718 100644 --- a/default-versions.tf +++ b/default-versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/fast/stages/0-bootstrap/templates/workflow-github.yaml b/fast/stages/0-bootstrap/templates/workflow-github.yaml index 74a2e1f8..5969f5a9 100644 --- a/fast/stages/0-bootstrap/templates/workflow-github.yaml +++ b/fast/stages/0-bootstrap/templates/workflow-github.yaml @@ -47,6 +47,19 @@ jobs: name: Checkout repository uses: actions/checkout@v3 +# # Print JWT token payload, useful for debugging +# - id: jwt-debug +# name: Print GITHUB_TOKEN payload +# shell: python -u {0} +# run: | +# import base64 +# import json +# +# token = '${{ secrets.GITHUB_TOKEN }}' +# payload_text = token.split('.')[1] +# payload = json.loads(base64.urlsafe_b64decode(payload_text + '=' * (4-len(payload_text) %4))) +# print(json.dumps(payload, indent=2)) + # set up SSH key authentication to the modules repository - id: ssh-config name: Configure SSH authentication diff --git a/fast/stages/1-resman/organization.tf b/fast/stages/1-resman/organization.tf index 11200035..90ded30e 100644 --- a/fast/stages/1-resman/organization.tf +++ b/fast/stages/1-resman/organization.tf @@ -48,21 +48,21 @@ module "organization" { description = "Resource management context." iam = {} values = { - data = null - gke = null - networking = null - sandbox = null - security = null - teams = null - tenant = null + data = {} + gke = {} + networking = {} + sandbox = {} + security = {} + teams = {} + tenant = {} } } (var.tag_names.environment) = { description = "Environment definition." iam = {} values = { - development = null - production = null + development = {} + production = {} } } (var.tag_names.tenant) = { diff --git a/fast/stages/2-networking-a-peering/data/dns-policy-rules.yaml b/fast/stages/2-networking-a-peering/data/dns-policy-rules.yaml index f157cec0..94cfb4f8 100644 --- a/fast/stages/2-networking-a-peering/data/dns-policy-rules.yaml +++ b/fast/stages/2-networking-a-peering/data/dns-policy-rules.yaml @@ -81,6 +81,12 @@ googleapis-restricted: gstatic-all: dns_name: "*.gstatic.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu: + dns_name: "kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu-all: + dns_name: "*.kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } notebooks-all: dns_name: "*.notebooks.cloud.google.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } diff --git a/fast/stages/2-networking-b-vpn/data/dns-policy-rules.yaml b/fast/stages/2-networking-b-vpn/data/dns-policy-rules.yaml index f157cec0..94cfb4f8 100644 --- a/fast/stages/2-networking-b-vpn/data/dns-policy-rules.yaml +++ b/fast/stages/2-networking-b-vpn/data/dns-policy-rules.yaml @@ -81,6 +81,12 @@ googleapis-restricted: gstatic-all: dns_name: "*.gstatic.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu: + dns_name: "kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu-all: + dns_name: "*.kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } notebooks-all: dns_name: "*.notebooks.cloud.google.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } diff --git a/fast/stages/2-networking-c-nva/data/dns-policy-rules.yaml b/fast/stages/2-networking-c-nva/data/dns-policy-rules.yaml index f157cec0..94cfb4f8 100644 --- a/fast/stages/2-networking-c-nva/data/dns-policy-rules.yaml +++ b/fast/stages/2-networking-c-nva/data/dns-policy-rules.yaml @@ -81,6 +81,12 @@ googleapis-restricted: gstatic-all: dns_name: "*.gstatic.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu: + dns_name: "kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu-all: + dns_name: "*.kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } notebooks-all: dns_name: "*.notebooks.cloud.google.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } diff --git a/fast/stages/2-networking-d-separate-envs/data/dns-policy-rules.yaml b/fast/stages/2-networking-d-separate-envs/data/dns-policy-rules.yaml index f157cec0..94cfb4f8 100644 --- a/fast/stages/2-networking-d-separate-envs/data/dns-policy-rules.yaml +++ b/fast/stages/2-networking-d-separate-envs/data/dns-policy-rules.yaml @@ -81,6 +81,12 @@ googleapis-restricted: gstatic-all: dns_name: "*.gstatic.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu: + dns_name: "kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu-all: + dns_name: "*.kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } notebooks-all: dns_name: "*.notebooks.cloud.google.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } diff --git a/fast/stages/2-networking-e-nva-bgp/data/dns-policy-rules.yaml b/fast/stages/2-networking-e-nva-bgp/data/dns-policy-rules.yaml index f157cec0..94cfb4f8 100644 --- a/fast/stages/2-networking-e-nva-bgp/data/dns-policy-rules.yaml +++ b/fast/stages/2-networking-e-nva-bgp/data/dns-policy-rules.yaml @@ -81,6 +81,12 @@ googleapis-restricted: gstatic-all: dns_name: "*.gstatic.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu: + dns_name: "kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } +kernels-gu-all: + dns_name: "*.kernels.googleusercontent.com." + local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } notebooks-all: dns_name: "*.notebooks.cloud.google.com." local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } } diff --git a/modules/__experimental/alloydb-instance/versions.tf b/modules/__experimental/alloydb-instance/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/__experimental/alloydb-instance/versions.tf +++ b/modules/__experimental/alloydb-instance/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/__experimental/net-neg/versions.tf b/modules/__experimental/net-neg/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/__experimental/net-neg/versions.tf +++ b/modules/__experimental/net-neg/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/__experimental/project-iam-magic/versions.tf b/modules/__experimental/project-iam-magic/versions.tf index 2ce95782..4a283718 100644 --- a/modules/__experimental/project-iam-magic/versions.tf +++ b/modules/__experimental/project-iam-magic/versions.tf @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,15 +13,15 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" - version = ">= 4.71.0" # tftest + version = ">= 5.6.0, < 6.0.0" # tftest } google-beta = { source = "hashicorp/google-beta" - version = ">= 4.71.0" # tftest + version = ">= 5.6.0, < 6.0.0" # tftest } } } diff --git a/modules/api-gateway/versions.tf b/modules/api-gateway/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/api-gateway/versions.tf +++ b/modules/api-gateway/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/apigee/versions.tf b/modules/apigee/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/apigee/versions.tf +++ b/modules/apigee/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/artifact-registry/versions.tf b/modules/artifact-registry/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/artifact-registry/versions.tf +++ b/modules/artifact-registry/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/bigquery-dataset/versions.tf b/modules/bigquery-dataset/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/bigquery-dataset/versions.tf +++ b/modules/bigquery-dataset/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/bigtable-instance/README.md b/modules/bigtable-instance/README.md index 703810a0..05dcbdde 100644 --- a/modules/bigtable-instance/README.md +++ b/modules/bigtable-instance/README.md @@ -237,7 +237,7 @@ module "bigtable-instance" { | [deletion_protection](variables.tf#L56) | Whether or not to allow Terraform to destroy the instance. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the instance will fail. | bool | | true | | [display_name](variables.tf#L63) | The human-readable display name of the Bigtable instance. | string | | null | | [iam](variables.tf#L69) | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [instance_type](variables.tf#L75) | (deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'. | string | | null | +| [labels](variables.tf#L75) | Labels to be attached to the instance. | map(string) | | {} | | [tables](variables.tf#L91) | Tables to be created in the BigTable instance. | map(object({…})) | | {} | ## Outputs diff --git a/modules/bigtable-instance/main.tf b/modules/bigtable-instance/main.tf index 3a00eab8..309bbb1a 100644 --- a/modules/bigtable-instance/main.tf +++ b/modules/bigtable-instance/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,12 +36,11 @@ locals { } resource "google_bigtable_instance" "default" { - project = var.project_id - name = var.name - - instance_type = var.instance_type - display_name = var.display_name == null ? var.display_name : var.name + project = var.project_id + name = var.name + display_name = coalesce(var.display_name, var.name) deletion_protection = var.deletion_protection + labels = var.labels dynamic "cluster" { for_each = local.clusters_autoscaling diff --git a/modules/bigtable-instance/outputs.tf b/modules/bigtable-instance/outputs.tf index a2fd2646..5cfabd6a 100644 --- a/modules/bigtable-instance/outputs.tf +++ b/modules/bigtable-instance/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/bigtable-instance/variables.tf b/modules/bigtable-instance/variables.tf index d8567766..132d5c2c 100644 --- a/modules/bigtable-instance/variables.tf +++ b/modules/bigtable-instance/variables.tf @@ -72,10 +72,10 @@ variable "iam" { default = {} } -variable "instance_type" { - description = "(deprecated) The instance type to create. One of 'DEVELOPMENT' or 'PRODUCTION'." - type = string - default = null +variable "labels" { + description = "Labels to be attached to the instance." + type = map(string) + default = {} } variable "name" { diff --git a/modules/bigtable-instance/versions.tf b/modules/bigtable-instance/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/bigtable-instance/versions.tf +++ b/modules/bigtable-instance/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/billing-account/versions.tf b/modules/billing-account/versions.tf index c7a022f0..4a283718 100644 --- a/modules/billing-account/versions.tf +++ b/modules/billing-account/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" @@ -25,3 +25,5 @@ terraform { } } } + + diff --git a/modules/binauthz/versions.tf b/modules/binauthz/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/binauthz/versions.tf +++ b/modules/binauthz/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/README.md b/modules/cloud-config-container/README.md index 2307a76d..d7017dcb 100644 --- a/modules/cloud-config-container/README.md +++ b/modules/cloud-config-container/README.md @@ -14,7 +14,6 @@ These modules are designed for several use cases: - [CoreDNS](./coredns) - [MySQL](./mysql) - [Nginx](./nginx) -- [Squid forward proxy](./squid) - On-prem in Docker (*needs fixing*) ## Using the modules diff --git a/modules/cloud-config-container/__need_fixing/onprem/versions.tf b/modules/cloud-config-container/__need_fixing/onprem/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/__need_fixing/onprem/versions.tf +++ b/modules/cloud-config-container/__need_fixing/onprem/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/squid/README.md b/modules/cloud-config-container/__need_fixing/squid/README.md similarity index 94% rename from modules/cloud-config-container/squid/README.md rename to modules/cloud-config-container/__need_fixing/squid/README.md index 4ee29ed9..dd0ac01b 100644 --- a/modules/cloud-config-container/squid/README.md +++ b/modules/cloud-config-container/__need_fixing/squid/README.md @@ -14,7 +14,7 @@ Logging and monitoring are enabled via the [Google Cloud Logging agent](https:// 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. +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 @@ -24,7 +24,7 @@ This example will create a `cloud-config` that allows any client in the 10.0.0.0 ```hcl module "cos-squid" { - source = "./fabric/modules/cloud-config-container/squid" + source = "./fabric/modules/cloud-config-container/__need_fixing/squid" allow = [".github.com"] clients = ["10.0.0.0/8"] } @@ -43,9 +43,11 @@ module "vm" { google-logging-enabled = true } boot_disk = { - image = "projects/cos-cloud/global/images/family/cos-stable" - type = "pd-ssd" - size = 10 + initialize_params = { + image = "projects/cos-cloud/global/images/family/cos-stable" + type = "pd-ssd" + size = 10 + } } tags = ["http-server", "ssh"] } diff --git a/modules/cloud-config-container/squid/cloud-config.yaml b/modules/cloud-config-container/__need_fixing/squid/cloud-config.yaml similarity index 100% rename from modules/cloud-config-container/squid/cloud-config.yaml rename to modules/cloud-config-container/__need_fixing/squid/cloud-config.yaml diff --git a/modules/cloud-config-container/squid/docker/Dockerfile b/modules/cloud-config-container/__need_fixing/squid/docker/Dockerfile similarity index 100% rename from modules/cloud-config-container/squid/docker/Dockerfile rename to modules/cloud-config-container/__need_fixing/squid/docker/Dockerfile diff --git a/modules/cloud-config-container/squid/docker/cloudbuild.yaml b/modules/cloud-config-container/__need_fixing/squid/docker/cloudbuild.yaml similarity index 100% rename from modules/cloud-config-container/squid/docker/cloudbuild.yaml rename to modules/cloud-config-container/__need_fixing/squid/docker/cloudbuild.yaml diff --git a/modules/cloud-config-container/squid/docker/entrypoint.sh b/modules/cloud-config-container/__need_fixing/squid/docker/entrypoint.sh similarity index 100% rename from modules/cloud-config-container/squid/docker/entrypoint.sh rename to modules/cloud-config-container/__need_fixing/squid/docker/entrypoint.sh diff --git a/modules/cloud-config-container/squid/main.tf b/modules/cloud-config-container/__need_fixing/squid/main.tf similarity index 100% rename from modules/cloud-config-container/squid/main.tf rename to modules/cloud-config-container/__need_fixing/squid/main.tf diff --git a/modules/cloud-config-container/squid/outputs.tf b/modules/cloud-config-container/__need_fixing/squid/outputs.tf similarity index 100% rename from modules/cloud-config-container/squid/outputs.tf rename to modules/cloud-config-container/__need_fixing/squid/outputs.tf diff --git a/modules/cloud-config-container/squid/squid.conf b/modules/cloud-config-container/__need_fixing/squid/squid.conf similarity index 100% rename from modules/cloud-config-container/squid/squid.conf rename to modules/cloud-config-container/__need_fixing/squid/squid.conf diff --git a/modules/cloud-config-container/squid/variables.tf b/modules/cloud-config-container/__need_fixing/squid/variables.tf similarity index 100% rename from modules/cloud-config-container/squid/variables.tf rename to modules/cloud-config-container/__need_fixing/squid/variables.tf diff --git a/modules/cloud-config-container/squid/versions.tf b/modules/cloud-config-container/__need_fixing/squid/versions.tf similarity index 96% rename from modules/cloud-config-container/squid/versions.tf rename to modules/cloud-config-container/__need_fixing/squid/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/squid/versions.tf +++ b/modules/cloud-config-container/__need_fixing/squid/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/coredns/versions.tf b/modules/cloud-config-container/coredns/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/coredns/versions.tf +++ b/modules/cloud-config-container/coredns/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/cos-generic-metadata/versions.tf b/modules/cloud-config-container/cos-generic-metadata/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/cos-generic-metadata/versions.tf +++ b/modules/cloud-config-container/cos-generic-metadata/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/envoy-sni-dyn-fwd-proxy/versions.tf b/modules/cloud-config-container/envoy-sni-dyn-fwd-proxy/versions.tf index 28a6f3a1..4a283718 100644 --- a/modules/cloud-config-container/envoy-sni-dyn-fwd-proxy/versions.tf +++ b/modules/cloud-config-container/envoy-sni-dyn-fwd-proxy/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" @@ -26,3 +26,4 @@ terraform { } } + diff --git a/modules/cloud-config-container/envoy-traffic-director/versions.tf b/modules/cloud-config-container/envoy-traffic-director/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/envoy-traffic-director/versions.tf +++ b/modules/cloud-config-container/envoy-traffic-director/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/mysql/versions.tf b/modules/cloud-config-container/mysql/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/mysql/versions.tf +++ b/modules/cloud-config-container/mysql/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/nginx-tls/versions.tf b/modules/cloud-config-container/nginx-tls/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/nginx-tls/versions.tf +++ b/modules/cloud-config-container/nginx-tls/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/nginx/versions.tf b/modules/cloud-config-container/nginx/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/nginx/versions.tf +++ b/modules/cloud-config-container/nginx/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-config-container/simple-nva/README.md b/modules/cloud-config-container/simple-nva/README.md index 3fb279c8..0f3741fa 100644 --- a/modules/cloud-config-container/simple-nva/README.md +++ b/modules/cloud-config-container/simple-nva/README.md @@ -7,6 +7,7 @@ This NVAs can be used to interconnect up to 8 VPCs. The NVAs run [Container-Optimized OS (COS)](https://cloud.google.com/container-optimized-os/docs). COS is a Linux-based OS designed for running containers. By default, it only allows SSH ingress connections. To see the exact host firewall configuration, run `sudo iptables -L -v`. More info available in the [official](https://cloud.google.com/container-optimized-os/docs/how-to/firewall) documentation. To configure the firewall, you can either + - use the [open_ports](variables.tf#L84) variable - for a thiner grain control, pass a custom bash script at startup with iptables commands @@ -55,6 +56,7 @@ module "vm" { zone = "europe-west8-b" name = "cos-nva" network_interfaces = local.network_interfaces + can_ip_forward = true metadata = { user-data = module.cos-nva.cloud_config google-logging-enabled = true @@ -75,9 +77,9 @@ module "vm" { The sample code brings up [FRRouting](https://frrouting.org/) container. -``` +```conf # tftest-file id=frr_conf path=./frr.conf -# Example frr.conmf file +# Example frr.conf file log syslog informational no ipv6 forwarding @@ -86,7 +88,7 @@ router bgp 65001 line vty ``` -Following code assumes a file in the same folder named frr.conf exists. +Following code assumes a file in the same folder named frr.conf exists. ```hcl locals { @@ -126,6 +128,7 @@ module "vm" { zone = "europe-west8-b" name = "cos-nva" network_interfaces = local.network_interfaces + can_ip_forward = true metadata = { user-data = module.cos-nva.cloud_config google-logging-enabled = true diff --git a/modules/cloud-config-container/simple-nva/versions.tf b/modules/cloud-config-container/simple-nva/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-config-container/simple-nva/versions.tf +++ b/modules/cloud-config-container/simple-nva/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-function-v1/versions.tf b/modules/cloud-function-v1/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-function-v1/versions.tf +++ b/modules/cloud-function-v1/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-function-v2/versions.tf b/modules/cloud-function-v2/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-function-v2/versions.tf +++ b/modules/cloud-function-v2/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-identity-group/versions.tf b/modules/cloud-identity-group/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-identity-group/versions.tf +++ b/modules/cloud-identity-group/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloud-run/versions.tf b/modules/cloud-run/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloud-run/versions.tf +++ b/modules/cloud-run/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/cloudsql-instance/versions.tf b/modules/cloudsql-instance/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/cloudsql-instance/versions.tf +++ b/modules/cloudsql-instance/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/compute-mig/versions.tf b/modules/compute-mig/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/compute-mig/versions.tf +++ b/modules/compute-mig/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/compute-vm/versions.tf b/modules/compute-vm/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/compute-vm/versions.tf +++ b/modules/compute-vm/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/container-registry/versions.tf b/modules/container-registry/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/container-registry/versions.tf +++ b/modules/container-registry/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/data-catalog-policy-tag/versions.tf b/modules/data-catalog-policy-tag/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/data-catalog-policy-tag/versions.tf +++ b/modules/data-catalog-policy-tag/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/datafusion/versions.tf b/modules/datafusion/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/datafusion/versions.tf +++ b/modules/datafusion/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/dataplex-datascan/versions.tf b/modules/dataplex-datascan/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/dataplex-datascan/versions.tf +++ b/modules/dataplex-datascan/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/dataplex/versions.tf b/modules/dataplex/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/dataplex/versions.tf +++ b/modules/dataplex/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/dataproc/versions.tf b/modules/dataproc/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/dataproc/versions.tf +++ b/modules/dataproc/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/dns-response-policy/versions.tf b/modules/dns-response-policy/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/dns-response-policy/versions.tf +++ b/modules/dns-response-policy/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/dns/versions.tf b/modules/dns/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/dns/versions.tf +++ b/modules/dns/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/endpoints/versions.tf b/modules/endpoints/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/endpoints/versions.tf +++ b/modules/endpoints/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/folder/README.md b/modules/folder/README.md index fb455495..094bab58 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -303,10 +303,9 @@ module "org" { tags = { environment = { description = "Environment specification." - iam = null values = { - dev = null - prod = null + dev = {} + prod = {} } } } diff --git a/modules/folder/versions.tf b/modules/folder/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/folder/versions.tf +++ b/modules/folder/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gcs/versions.tf b/modules/gcs/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gcs/versions.tf +++ b/modules/gcs/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gcve-private-cloud/versions.tf b/modules/gcve-private-cloud/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gcve-private-cloud/versions.tf +++ b/modules/gcve-private-cloud/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gke-cluster-autopilot/versions.tf b/modules/gke-cluster-autopilot/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gke-cluster-autopilot/versions.tf +++ b/modules/gke-cluster-autopilot/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gke-cluster-standard/README.md b/modules/gke-cluster-standard/README.md index 75607083..0cd7727f 100644 --- a/modules/gke-cluster-standard/README.md +++ b/modules/gke-cluster-standard/README.md @@ -310,27 +310,27 @@ module "cluster-1" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [location](variables.tf#L179) | Cluster zone or region. | string | ✓ | | -| [name](variables.tf#L290) | Cluster name. | string | ✓ | | -| [project_id](variables.tf#L326) | Cluster project id. | string | ✓ | | -| [vpc_config](variables.tf#L337) | VPC-level configuration. | object({…}) | ✓ | | +| [location](variables.tf#L211) | Cluster zone or region. | string | ✓ | | +| [name](variables.tf#L322) | Cluster name. | string | ✓ | | +| [project_id](variables.tf#L358) | Cluster project id. | string | ✓ | | +| [vpc_config](variables.tf#L369) | VPC-level configuration. | object({…}) | ✓ | | | [backup_configs](variables.tf#L17) | Configuration for Backup for GKE. | object({…}) | | {} | -| [cluster_autoscaling](variables.tf#L38) | Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler. | object({…}) | | null | -| [deletion_protection](variables.tf#L83) | Whether or not to allow Terraform to destroy the cluster. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the cluster will fail. | bool | | true | -| [description](variables.tf#L90) | Cluster description. | string | | null | -| [enable_addons](variables.tf#L96) | Addons enabled in the cluster (true means enabled). | object({…}) | | {…} | -| [enable_features](variables.tf#L120) | Enable cluster-level features. Certain features allow configuration. | object({…}) | | {…} | -| [issue_client_certificate](variables.tf#L167) | Enable issuing client certificate. | bool | | false | -| [labels](variables.tf#L173) | Cluster resource labels. | map(string) | | null | -| [logging_config](variables.tf#L184) | Logging configuration. | object({…}) | | {} | -| [maintenance_config](variables.tf#L205) | Maintenance window configuration. | object({…}) | | {…} | -| [max_pods_per_node](variables.tf#L228) | Maximum number of pods per node in this cluster. | number | | 110 | -| [min_master_version](variables.tf#L234) | Minimum version of the master, defaults to the version of the most recent official release. | string | | null | -| [monitoring_config](variables.tf#L240) | Monitoring configuration. Google Cloud Managed Service for Prometheus is enabled by default. | object({…}) | | {} | -| [node_config](variables.tf#L295) | Node-level configuration. | object({…}) | | {} | -| [node_locations](variables.tf#L305) | Zones in which the cluster's nodes are located. | list(string) | | [] | -| [private_cluster_config](variables.tf#L312) | Private cluster configuration. | object({…}) | | null | -| [release_channel](variables.tf#L331) | Release channel for GKE upgrades. | string | | null | +| [cluster_autoscaling](variables.tf#L38) | Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler. | object({…}) | | null | +| [deletion_protection](variables.tf#L115) | Whether or not to allow Terraform to destroy the cluster. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply that would delete the cluster will fail. | bool | | true | +| [description](variables.tf#L122) | Cluster description. | string | | null | +| [enable_addons](variables.tf#L128) | Addons enabled in the cluster (true means enabled). | object({…}) | | {…} | +| [enable_features](variables.tf#L152) | Enable cluster-level features. Certain features allow configuration. | object({…}) | | {…} | +| [issue_client_certificate](variables.tf#L199) | Enable issuing client certificate. | bool | | false | +| [labels](variables.tf#L205) | Cluster resource labels. | map(string) | | null | +| [logging_config](variables.tf#L216) | Logging configuration. | object({…}) | | {} | +| [maintenance_config](variables.tf#L237) | Maintenance window configuration. | object({…}) | | {…} | +| [max_pods_per_node](variables.tf#L260) | Maximum number of pods per node in this cluster. | number | | 110 | +| [min_master_version](variables.tf#L266) | Minimum version of the master, defaults to the version of the most recent official release. | string | | null | +| [monitoring_config](variables.tf#L272) | Monitoring configuration. Google Cloud Managed Service for Prometheus is enabled by default. | object({…}) | | {} | +| [node_config](variables.tf#L327) | Node-level configuration. | object({…}) | | {} | +| [node_locations](variables.tf#L337) | Zones in which the cluster's nodes are located. | list(string) | | [] | +| [private_cluster_config](variables.tf#L344) | Private cluster configuration. | object({…}) | | null | +| [release_channel](variables.tf#L363) | Release channel for GKE upgrades. | string | | null | ## Outputs diff --git a/modules/gke-cluster-standard/main.tf b/modules/gke-cluster-standard/main.tf index b82a127e..71d5eee2 100644 --- a/modules/gke-cluster-standard/main.tf +++ b/modules/gke-cluster-standard/main.tf @@ -13,6 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +locals { + cas = var.cluster_autoscaling + cas_apd = try(local.cas.auto_provisioning_defaults, null) + cas_apd_us = try(local.cas_apd.upgrade_settings, null) +} + resource "google_container_cluster" "cluster" { provider = google-beta project = var.project_id @@ -40,7 +47,6 @@ resource "google_container_cluster" "cluster" { ? "ADVANCED_DATAPATH" : "DATAPATH_PROVIDER_UNSPECIFIED" ) - # the default node pool is deleted here, use the gke-nodepool module instead. # shielded nodes are controlled by the cluster-level enable_features variable node_config { @@ -55,7 +61,6 @@ resource "google_container_cluster" "cluster" { } } } - # gcfs_config deactivation need the block to be defined so it can't be dynamic node_pool_defaults { node_config_defaults { @@ -64,7 +69,6 @@ resource "google_container_cluster" "cluster" { } } } - addons_config { dns_cache_config { enabled = var.enable_addons.dns_cache @@ -106,81 +110,115 @@ resource "google_container_cluster" "cluster" { enabled = var.backup_configs.enable_backup_agent } } - dynamic "authenticator_groups_config" { for_each = var.enable_features.groups_for_rbac != null ? [""] : [] content { security_group = var.enable_features.groups_for_rbac } } - dynamic "binary_authorization" { for_each = var.enable_features.binary_authorization ? [""] : [] content { evaluation_mode = "PROJECT_SINGLETON_POLICY_ENFORCE" } } - dynamic "cost_management_config" { for_each = var.enable_features.cost_management == true ? [""] : [] content { enabled = true } } - dynamic "cluster_autoscaling" { - for_each = var.cluster_autoscaling == null ? [] : [""] + for_each = local.cas == null ? [] : [""] content { - enabled = true - + enabled = true autoscaling_profile = var.cluster_autoscaling.autoscaling_profile - dynamic "auto_provisioning_defaults" { - for_each = var.cluster_autoscaling.auto_provisioning_defaults != null ? [""] : [] + for_each = local.cas_apd != null ? [""] : [] content { - boot_disk_kms_key = var.cluster_autoscaling.auto_provisioning_defaults.boot_disk_kms_key - disk_size = var.cluster_autoscaling.auto_provisioning_defaults.disk_size - disk_type = var.cluster_autoscaling.auto_provisioning_defaults.disk_type - image_type = var.cluster_autoscaling.auto_provisioning_defaults.image_type - oauth_scopes = var.cluster_autoscaling.auto_provisioning_defaults.oauth_scopes - service_account = var.cluster_autoscaling.auto_provisioning_defaults.service_account + boot_disk_kms_key = local.cas_apd.boot_disk_kms_key + disk_size = local.cas_apd.disk_size + disk_type = local.cas_apd.disk_type + image_type = local.cas_apd.image_type + oauth_scopes = local.cas_apd.oauth_scopes + service_account = local.cas_apd.service_account dynamic "management" { - for_each = var.cluster_autoscaling.auto_provisioning_defaults.management != null ? [""] : [] + for_each = local.cas_apd.management != null ? [""] : [] content { - auto_repair = var.cluster_autoscaling.auto_provisioning_defaults.management.auto_repair - auto_upgrade = var.cluster_autoscaling.auto_provisioning_defaults.management.auto_upgrade + auto_repair = local.cas_apd.management.auto_repair + auto_upgrade = local.cas_apd.management.auto_upgrade } } dynamic "shielded_instance_config" { - for_each = var.cluster_autoscaling.auto_provisioning_defaults.shielded_instance_config != null ? [""] : [] + for_each = local.cas_apd.shielded_instance_config != null ? [""] : [] content { - enable_integrity_monitoring = var.cluster_autoscaling.auto_provisioning_defaults.shielded_instance_config.integrity_monitoring - enable_secure_boot = var.cluster_autoscaling.auto_provisioning_defaults.shielded_instance_config.secure_boot + enable_integrity_monitoring = ( + local.cas_apd.shielded_instance_config.integrity_monitoring + ) + enable_secure_boot = ( + local.cas_apd.shielded_instance_config.secure_boot + ) + } + } + dynamic "upgrade_settings" { + for_each = local.cas_apd_us != null ? [""] : [] + content { + strategy = ( + local.cas_apd_us.blue_green != null ? "BLUE_GREEN" : "SURGE" + ) + max_surge = try(local.cas_apd_us.surge.max, null) + max_unavailable = try(local.cas_apd_us.surge.unavailable, null) + dynamic "blue_green_settings" { + for_each = local.cas_apd_us.blue_green != null ? [""] : [] + content { + node_pool_soak_duration = ( + local.cas_apd_us.blue_green.node_pool_soak_duration + ) + dynamic "standard_rollout_policy" { + for_each = ( + local.cas_apd_us.blue_green.standard_rollout_policy != null + ? [""] + : [] + ) + content { + batch_node_count = ( + local.cas_apd_us.blue_green.standard_rollout_policy.batch_node_count + ) + batch_percentage = ( + local.cas_apd_us.blue_green.standard_rollout_policy.batch_percentage + ) + batch_soak_duration = ( + local.cas_apd_us.blue_green.standard_rollout_policy.batch_soak_duration + ) + } + } + } + } } } } } dynamic "resource_limits" { - for_each = var.cluster_autoscaling.cpu_limits != null ? [""] : [] + for_each = local.cas.cpu_limits != null ? [""] : [] content { resource_type = "cpu" - minimum = var.cluster_autoscaling.cpu_limits.min - maximum = var.cluster_autoscaling.cpu_limits.max + minimum = local.cas.cpu_limits.min + maximum = local.cas.cpu_limits.max } } dynamic "resource_limits" { - for_each = var.cluster_autoscaling.mem_limits != null ? [""] : [] + for_each = local.cas.mem_limits != null ? [""] : [] content { resource_type = "memory" - minimum = var.cluster_autoscaling.mem_limits.min - maximum = var.cluster_autoscaling.mem_limits.max + minimum = local.cas.mem_limits.min + maximum = local.cas.mem_limits.max } } dynamic "resource_limits" { for_each = ( - try(var.cluster_autoscaling.gpu_resources, null) == null + try(local.cas.gpu_resources, null) == null ? [] - : var.cluster_autoscaling.gpu_resources + : local.cas.gpu_resources ) iterator = gpu_resources content { @@ -191,7 +229,6 @@ resource "google_container_cluster" "cluster" { } } } - dynamic "database_encryption" { for_each = var.enable_features.database_encryption != null ? [""] : [] content { @@ -199,7 +236,6 @@ resource "google_container_cluster" "cluster" { key_name = var.enable_features.database_encryption.key_name } } - dynamic "dns_config" { for_each = var.enable_features.dns != null ? [""] : [] content { @@ -208,31 +244,36 @@ resource "google_container_cluster" "cluster" { cluster_dns_domain = var.enable_features.dns.domain } } - dynamic "gateway_api_config" { for_each = var.enable_features.gateway_api ? [""] : [] content { channel = "CHANNEL_STANDARD" } } - dynamic "ip_allocation_policy" { for_each = var.vpc_config.secondary_range_blocks != null ? [""] : [] content { - cluster_ipv4_cidr_block = var.vpc_config.secondary_range_blocks.pods - services_ipv4_cidr_block = var.vpc_config.secondary_range_blocks.services - stack_type = var.vpc_config.stack_type + cluster_ipv4_cidr_block = ( + var.vpc_config.secondary_range_blocks.pods + ) + services_ipv4_cidr_block = ( + var.vpc_config.secondary_range_blocks.services + ) + stack_type = var.vpc_config.stack_type } } dynamic "ip_allocation_policy" { for_each = var.vpc_config.secondary_range_names != null ? [""] : [] content { - cluster_secondary_range_name = var.vpc_config.secondary_range_names.pods - services_secondary_range_name = var.vpc_config.secondary_range_names.services - stack_type = var.vpc_config.stack_type + cluster_secondary_range_name = ( + var.vpc_config.secondary_range_names.pods + ) + services_secondary_range_name = ( + var.vpc_config.secondary_range_names.services + ) + stack_type = var.vpc_config.stack_type } } - # Send GKE cluster logs from chosen sources to Cloud Logging. # System logs must be enabled if any other source is enabled. # This is validated by input variable validation rules. @@ -256,7 +297,6 @@ resource "google_container_cluster" "cluster" { enable_components = [] } } - maintenance_policy { dynamic "daily_maintenance_window" { for_each = ( @@ -294,13 +334,11 @@ resource "google_container_cluster" "cluster" { } } } - master_auth { client_certificate_config { issue_client_certificate = var.issue_client_certificate } } - dynamic "master_authorized_networks_config" { for_each = var.vpc_config.master_authorized_ranges != null ? [""] : [] content { @@ -314,14 +352,12 @@ resource "google_container_cluster" "cluster" { } } } - dynamic "mesh_certificates" { for_each = var.enable_features.mesh_certificates != null ? [""] : [] content { enable_certificates = var.enable_features.mesh_certificates } } - monitoring_config { enable_components = toset(compact([ # System metrics is the minimum requirement if any other metrics are enabled. This is checked by input var validation. @@ -342,7 +378,6 @@ resource "google_container_cluster" "cluster" { enabled = var.monitoring_config.enable_managed_prometheus } } - # Dataplane V2 has built-in network policies dynamic "network_policy" { for_each = ( @@ -355,7 +390,6 @@ resource "google_container_cluster" "cluster" { provider = "CALICO" } } - dynamic "notification_config" { for_each = var.enable_features.upgrade_notifications != null ? [""] : [] content { @@ -369,7 +403,6 @@ resource "google_container_cluster" "cluster" { } } } - dynamic "private_cluster_config" { for_each = ( var.private_cluster_config != null ? [""] : [] @@ -383,21 +416,18 @@ resource "google_container_cluster" "cluster" { } } } - dynamic "pod_security_policy_config" { for_each = var.enable_features.pod_security_policy ? [""] : [] content { enabled = var.enable_features.pod_security_policy } } - dynamic "release_channel" { for_each = var.release_channel != null ? [""] : [] content { channel = var.release_channel } } - dynamic "resource_usage_export_config" { for_each = ( try(var.enable_features.resource_usage_export.dataset, null) != null @@ -416,14 +446,12 @@ resource "google_container_cluster" "cluster" { } } } - dynamic "vertical_pod_autoscaling" { for_each = var.enable_features.vertical_pod_autoscaling ? [""] : [] content { enabled = var.enable_features.vertical_pod_autoscaling } } - dynamic "workload_identity_config" { for_each = var.enable_features.workload_identity ? [""] : [] content { @@ -436,7 +464,11 @@ resource "google_container_cluster" "cluster" { } resource "google_gke_backup_backup_plan" "backup_plan" { - for_each = var.backup_configs.enable_backup_agent ? var.backup_configs.backup_plans : {} + for_each = ( + var.backup_configs.enable_backup_agent + ? var.backup_configs.backup_plans + : {} + ) name = each.key cluster = google_container_cluster.cluster.id location = each.value.region @@ -449,19 +481,20 @@ resource "google_gke_backup_backup_plan" "backup_plan" { backup_schedule { cron_schedule = each.value.schedule } - backup_config { include_volume_data = each.value.include_volume_data include_secrets = each.value.include_secrets - dynamic "encryption_key" { for_each = each.value.encryption_key != null ? [""] : [] content { gcp_kms_encryption_key = each.value.encryption_key } } - - all_namespaces = lookup(each.value, "namespaces", null) != null || lookup(each.value, "applications", null) != null ? null : true + all_namespaces = ( + lookup(each.value, "namespaces", null) != null + || + lookup(each.value, "applications", null) != null ? null : true + ) dynamic "selected_namespaces" { for_each = each.value.namespaces != null ? [""] : [] content { diff --git a/modules/gke-cluster-standard/variables.tf b/modules/gke-cluster-standard/variables.tf index d2e54c12..840dd5ae 100644 --- a/modules/gke-cluster-standard/variables.tf +++ b/modules/gke-cluster-standard/variables.tf @@ -54,6 +54,21 @@ variable "cluster_autoscaling" { integrity_monitoring = optional(bool, true) secure_boot = optional(bool, false) })) + upgrade_settings = optional(object({ + blue_green = optional(object({ + node_pool_soak_duration = optional(string) + standard_rollout_policy = optional(object({ + batch_percentage = optional(number) + batch_node_count = optional(number) + batch_soak_duration = optional(string) + })) + })) + surge = optional(object({ + max = optional(number) + unavailable = optional(number) + })) + })) + # add validation rule to ensure only one is present if upgrade settings is defined })) cpu_limits = optional(object({ min = number @@ -71,13 +86,30 @@ variable "cluster_autoscaling" { }) default = null validation { - condition = (var.cluster_autoscaling == null ? true : contains(["BALANCED", "OPTIMIZE_UTILIZATION"], var.cluster_autoscaling.autoscaling_profile)) + condition = (var.cluster_autoscaling == null ? true : contains( + ["BALANCED", "OPTIMIZE_UTILIZATION"], + var.cluster_autoscaling.autoscaling_profile + )) error_message = "Invalid autoscaling_profile." } validation { - condition = (var.cluster_autoscaling == null ? true : contains(["pd-standard", "pd-ssd", "pd-balanced"], var.cluster_autoscaling.auto_provisioning_defaults.disk_type)) + condition = ( + var.cluster_autoscaling == null ? true : contains( + ["pd-standard", "pd-ssd", "pd-balanced"], + var.cluster_autoscaling.auto_provisioning_defaults.disk_type) + ) error_message = "Invalid disk_type." } + validation { + condition = ( + try(var.cluster_autoscaling.upgrade_settings, null) == null || ( + try(var.cluster_autoscaling.upgrade_settings.blue_green, null) == null ? 0 : 1 + + + try(var.cluster_autoscaling.upgrade_settings.surge, null) == null ? 0 : 1 + ) == 1 + ) + error_message = "Upgrade settings can only use blue/green or surge." + } } variable "deletion_protection" { diff --git a/modules/gke-cluster-standard/versions.tf b/modules/gke-cluster-standard/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gke-cluster-standard/versions.tf +++ b/modules/gke-cluster-standard/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gke-hub/versions.tf b/modules/gke-hub/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gke-hub/versions.tf +++ b/modules/gke-hub/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/gke-nodepool/versions.tf b/modules/gke-nodepool/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/gke-nodepool/versions.tf +++ b/modules/gke-nodepool/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/iam-service-account/versions.tf b/modules/iam-service-account/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/iam-service-account/versions.tf +++ b/modules/iam-service-account/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/kms/versions.tf b/modules/kms/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/kms/versions.tf +++ b/modules/kms/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/logging-bucket/versions.tf b/modules/logging-bucket/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/logging-bucket/versions.tf +++ b/modules/logging-bucket/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/ncc-spoke-ra/versions.tf b/modules/ncc-spoke-ra/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/ncc-spoke-ra/versions.tf +++ b/modules/ncc-spoke-ra/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-address/versions.tf b/modules/net-address/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-address/versions.tf +++ b/modules/net-address/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-cloudnat/versions.tf b/modules/net-cloudnat/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-cloudnat/versions.tf +++ b/modules/net-cloudnat/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-firewall-policy/versions.tf b/modules/net-firewall-policy/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-firewall-policy/versions.tf +++ b/modules/net-firewall-policy/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-ipsec-over-interconnect/versions.tf b/modules/net-ipsec-over-interconnect/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-ipsec-over-interconnect/versions.tf +++ b/modules/net-ipsec-over-interconnect/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-lb-app-ext/versions.tf b/modules/net-lb-app-ext/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-lb-app-ext/versions.tf +++ b/modules/net-lb-app-ext/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-lb-app-int/versions.tf b/modules/net-lb-app-int/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-lb-app-int/versions.tf +++ b/modules/net-lb-app-int/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-lb-ext/README.md b/modules/net-lb-ext/README.md index 4e0d7d8a..674848cc 100644 --- a/modules/net-lb-ext/README.md +++ b/modules/net-lb-ext/README.md @@ -290,7 +290,7 @@ module "nlb" { | [group_self_links](outputs.tf#L53) | Optional unmanaged instance group self links. | | | [groups](outputs.tf#L60) | Optional unmanaged instance group resources. | | | [health_check](outputs.tf#L65) | Auto-created health-check resource. | | -| [health_check_self_id](outputs.tf#L70) | Auto-created health-check self id. | | +| [health_check_id](outputs.tf#L70) | Auto-created health-check id. | | | [health_check_self_link](outputs.tf#L75) | Auto-created health-check self link. | | | [id](outputs.tf#L80) | Fully qualified forwarding rule ids. | | diff --git a/modules/net-lb-ext/health-check.tf b/modules/net-lb-ext/health-check.tf index d41a437d..d5208f08 100644 --- a/modules/net-lb-ext/health-check.tf +++ b/modules/net-lb-ext/health-check.tf @@ -17,7 +17,9 @@ # tfdoc:file:description Health check resource. locals { - hc = var.health_check_config + hc = ( + var.health_check != null ? null : var.health_check_config + ) hc_grpc = try(local.hc.grpc, null) != null hc_http = try(local.hc.http, null) != null hc_http2 = try(local.hc.http2, null) != null diff --git a/modules/net-lb-ext/outputs.tf b/modules/net-lb-ext/outputs.tf index bd3f383a..0868e832 100644 --- a/modules/net-lb-ext/outputs.tf +++ b/modules/net-lb-ext/outputs.tf @@ -67,8 +67,8 @@ output "health_check" { value = try(google_compute_region_health_check.default.0, null) } -output "health_check_self_id" { - description = "Auto-created health-check self id." +output "health_check_id" { + description = "Auto-created health-check id." value = try(google_compute_region_health_check.default.0.id, null) } diff --git a/modules/net-lb-ext/versions.tf b/modules/net-lb-ext/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-lb-ext/versions.tf +++ b/modules/net-lb-ext/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-lb-int/README.md b/modules/net-lb-int/README.md index 39344d5f..e1d9fd56 100644 --- a/modules/net-lb-int/README.md +++ b/modules/net-lb-int/README.md @@ -331,7 +331,7 @@ module "ilb" { | [group_self_links](outputs.tf#L56) | Optional unmanaged instance group self links. | | | [groups](outputs.tf#L63) | Optional unmanaged instance group resources. | | | [health_check](outputs.tf#L68) | Auto-created health-check resource. | | -| [health_check_self_id](outputs.tf#L73) | Auto-created health-check self id. | | +| [health_check_id](outputs.tf#L73) | Auto-created health-check id. | | | [health_check_self_link](outputs.tf#L78) | Auto-created health-check self link. | | | [id](outputs.tf#L83) | Fully qualified forwarding rule ids. | | diff --git a/modules/net-lb-int/health-check.tf b/modules/net-lb-int/health-check.tf index c9525878..4f5af03e 100644 --- a/modules/net-lb-int/health-check.tf +++ b/modules/net-lb-int/health-check.tf @@ -17,7 +17,9 @@ # tfdoc:file:description Health check resource. locals { - hc = var.health_check_config + hc = ( + var.health_check != null ? null : var.health_check_config + ) hc_grpc = try(local.hc.grpc, null) != null hc_http = try(local.hc.http, null) != null hc_http2 = try(local.hc.http2, null) != null diff --git a/modules/net-lb-int/outputs.tf b/modules/net-lb-int/outputs.tf index c4dabbb7..e78476fb 100644 --- a/modules/net-lb-int/outputs.tf +++ b/modules/net-lb-int/outputs.tf @@ -70,8 +70,8 @@ output "health_check" { value = try(google_compute_health_check.default.0, null) } -output "health_check_self_id" { - description = "Auto-created health-check self id." +output "health_check_id" { + description = "Auto-created health-check id." value = try(google_compute_health_check.default.0.id, null) } diff --git a/modules/net-lb-int/versions.tf b/modules/net-lb-int/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-lb-int/versions.tf +++ b/modules/net-lb-int/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-lb-proxy-int/README.md b/modules/net-lb-proxy-int/README.md index 85f9716c..0e036353 100644 --- a/modules/net-lb-proxy-int/README.md +++ b/modules/net-lb-proxy-int/README.md @@ -90,7 +90,7 @@ module "int-tcp-proxy" { subnetwork = var.subnet.self_link } } -# tftest modules=1 resources=4 +# tftest modules=1 resources=3 ``` ### Instance Groups @@ -315,7 +315,7 @@ module "int-tcp-proxy" { | [group_self_links](outputs.tf#L37) | Optional unmanaged instance group self links. | | | [groups](outputs.tf#L44) | Optional unmanaged instance group resources. | | | [health_check](outputs.tf#L49) | Auto-created health-check resource. | | -| [health_check_self_id](outputs.tf#L54) | Auto-created health-check self id. | | +| [health_check_id](outputs.tf#L54) | Auto-created health-check id. | | | [health_check_self_link](outputs.tf#L59) | Auto-created health-check self link. | | | [id](outputs.tf#L64) | Fully qualified forwarding rule id. | | | [neg_ids](outputs.tf#L69) | Autogenerated network endpoint group ids. | | diff --git a/modules/net-lb-proxy-int/health-check.tf b/modules/net-lb-proxy-int/health-check.tf index 8c0ff4f1..5ec01c25 100644 --- a/modules/net-lb-proxy-int/health-check.tf +++ b/modules/net-lb-proxy-int/health-check.tf @@ -17,7 +17,9 @@ # tfdoc:file:description Health check resource. locals { - hc = var.health_check_config + hc = ( + var.health_check != null ? null : var.health_check_config + ) hc_grpc = try(local.hc.grpc, null) != null hc_http = try(local.hc.http, null) != null hc_http2 = try(local.hc.http2, null) != null diff --git a/modules/net-lb-proxy-int/outputs.tf b/modules/net-lb-proxy-int/outputs.tf index 2bd35ee3..092bae7a 100644 --- a/modules/net-lb-proxy-int/outputs.tf +++ b/modules/net-lb-proxy-int/outputs.tf @@ -51,8 +51,8 @@ output "health_check" { value = try(google_compute_region_health_check.default.0, null) } -output "health_check_self_id" { - description = "Auto-created health-check self id." +output "health_check_id" { + description = "Auto-created health-check id." value = try(google_compute_region_health_check.default.0.id, null) } diff --git a/modules/net-lb-proxy-int/versions.tf b/modules/net-lb-proxy-int/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-lb-proxy-int/versions.tf +++ b/modules/net-lb-proxy-int/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-swp/versions.tf b/modules/net-swp/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-swp/versions.tf +++ b/modules/net-swp/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vlan-attachment/versions.tf b/modules/net-vlan-attachment/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vlan-attachment/versions.tf +++ b/modules/net-vlan-attachment/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpc-firewall/versions.tf b/modules/net-vpc-firewall/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpc-firewall/versions.tf +++ b/modules/net-vpc-firewall/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpc-peering/versions.tf b/modules/net-vpc-peering/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpc-peering/versions.tf +++ b/modules/net-vpc-peering/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index a2fdde4b..d5b7b79a 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -17,6 +17,7 @@ This module allows creation and management of VPC networks including subnetworks - [DNS Policies](#dns-policies) - [Subnet Factory](#subnet-factory) - [Custom Routes](#custom-routes) + - [Policy Based Routes](#policy-based-routes) - [Private Google Access routes](#private-google-access-routes) - [Allow Firewall Policy to be evaluated before Firewall Rules](#allow-firewall-policy-to-be-evaluated-before-firewall-rules) - [IPv6](#ipv6) @@ -455,6 +456,40 @@ module "vpc" { # tftest modules=5 resources=15 inventory=routes.yaml ``` +### Policy Based Routes + +Policy based routes can be configured through the `policy_based_routes` variable. + +```hcl +module "vpc" { + source = "./fabric/modules/net-vpc" + project_id = var.project_id + name = "my-vpc" + policy_based_routes = { + skip-pbr-for-nva = { + use_default_routing = true + priority = 100 + target = { + tags = ["nva"] + } + } + send-all-to-nva = { + next_hop_ilb_ip = "10.0.0.253" + priority = 101 + filter = { + src_range = "10.0.0.0/8" + dest_range = "0.0.0.0/0" + } + target = { + interconnect_attachment = "europe-west8" + } + } + } + create_googleapis_routes = null +} +# tftest modules=1 resources=3 inventory=pbr.yaml +``` + ### Private Google Access routes By default the VPC module creates IPv4 routes for the [Private Google Access ranges](https://cloud.google.com/vpc/docs/configure-private-google-access#config-routing). This behavior can be controlled through the `create_googleapis_routes` variable: @@ -541,7 +576,7 @@ module "vpc" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [name](variables.tf#L95) | The name of the network being created. | string | ✓ | | -| [project_id](variables.tf#L111) | The ID of the project where this VPC will be created. | string | ✓ | | +| [project_id](variables.tf#L159) | The ID of the project where this VPC will be created. | string | ✓ | | | [auto_create_subnetworks](variables.tf#L17) | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | | [create_googleapis_routes](variables.tf#L23) | Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null. | object({…}) | | {} | | [delete_default_routes_on_create](variables.tf#L34) | Set to true to delete the default routes at creation time. | bool | | false | @@ -552,15 +587,16 @@ module "vpc" { | [ipv6_config](variables.tf#L79) | Optional IPv6 configuration for this network. | object({…}) | | {} | | [mtu](variables.tf#L89) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | number | | null | | [peering_config](variables.tf#L100) | VPC peering configuration. | object({…}) | | null | -| [psa_config](variables.tf#L116) | The Private Service Access configuration for Service Networking. | object({…}) | | null | -| [routes](variables.tf#L127) | Network routes, keyed by name. | map(object({…})) | | {} | -| [routing_mode](variables.tf#L148) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" | -| [shared_vpc_host](variables.tf#L158) | Enable shared VPC for this project. | bool | | false | -| [shared_vpc_service_projects](variables.tf#L164) | Shared VPC service projects to register with this host. | list(string) | | [] | -| [subnets](variables.tf#L170) | Subnet configuration. | list(object({…})) | | [] | -| [subnets_proxy_only](variables.tf#L217) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…})) | | [] | -| [subnets_psc](variables.tf#L251) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] | -| [vpc_create](variables.tf#L283) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | +| [policy_based_routes](variables.tf#L111) | Policy based routes, keyed by name. | map(object({…})) | | {} | +| [psa_config](variables.tf#L164) | The Private Service Access configuration for Service Networking. | object({…}) | | null | +| [routes](variables.tf#L175) | Network routes, keyed by name. | map(object({…})) | | {} | +| [routing_mode](variables.tf#L196) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" | +| [shared_vpc_host](variables.tf#L206) | Enable shared VPC for this project. | bool | | false | +| [shared_vpc_service_projects](variables.tf#L212) | Shared VPC service projects to register with this host. | list(string) | | [] | +| [subnets](variables.tf#L218) | Subnet configuration. | list(object({…})) | | [] | +| [subnets_proxy_only](variables.tf#L265) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…})) | | [] | +| [subnets_psc](variables.tf#L299) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] | +| [vpc_create](variables.tf#L331) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | ## Outputs diff --git a/modules/net-vpc/routes.tf b/modules/net-vpc/routes.tf index 065ea5fd..be16bb93 100644 --- a/modules/net-vpc/routes.tf +++ b/modules/net-vpc/routes.tf @@ -108,3 +108,32 @@ resource "google_compute_route" "vpn_tunnel" { tags = each.value.tags next_hop_vpn_tunnel = each.value.next_hop } + +resource "google_network_connectivity_policy_based_route" "default" { + for_each = var.policy_based_routes + project = var.project_id + network = local.network.id + name = "${var.name}-${each.key}" + description = each.value.description + priority = each.value.priority + next_hop_other_routes = each.value.use_default_routing ? "DEFAULT_ROUTING" : null + next_hop_ilb_ip = each.value.use_default_routing ? null : each.value.next_hop_ilb_ip + filter { + protocol_version = "IPV4" + ip_protocol = each.value.filter.ip_protocol + dest_range = each.value.filter.dest_range + src_range = each.value.filter.src_range + } + dynamic "virtual_machine" { + for_each = each.value.target.tags != null ? [""] : [] + content { + tags = each.value.target.tags + } + } + dynamic "interconnect_attachment" { + for_each = each.value.target.interconnect_attachment != null ? [""] : [] + content { + region = each.value.target.interconnect_attachment + } + } +} diff --git a/modules/net-vpc/subnets.tf b/modules/net-vpc/subnets.tf index fe5abea9..1604df9b 100644 --- a/modules/net-vpc/subnets.tf +++ b/modules/net-vpc/subnets.tf @@ -182,6 +182,12 @@ resource "google_compute_subnetwork" "proxy_only" { ) purpose = each.value.global ? "GLOBAL_MANAGED_PROXY" : "REGIONAL_MANAGED_PROXY" role = each.value.active ? "ACTIVE" : "BACKUP" + + lifecycle { + # Until https://github.com/hashicorp/terraform-provider-google/issues/16804 is fixed + # ignore permadiff in ipv6_access_type for proxy_only subnets + ignore_changes = [ipv6_access_type] + } } resource "google_compute_subnetwork" "psc" { diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index f463470d..06b95209 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -108,6 +108,54 @@ variable "peering_config" { default = null } +variable "policy_based_routes" { + description = "Policy based routes, keyed by name." + type = map(object({ + description = optional(string, "Terraform-managed.") + labels = optional(map(string)) + priority = optional(number) + next_hop_ilb_ip = optional(string) + use_default_routing = optional(bool, false) + filter = optional(object({ + ip_protocol = optional(string) + dest_range = optional(string) + src_range = optional(string) + }), {}) + target = optional(object({ + interconnect_attachment = optional(string) + tags = optional(list(string)) + }), {}) + })) + default = {} + nullable = false + validation { + condition = alltrue([ + for r in var.policy_based_routes : + contains(["TCP", "UDP", "ALL", null], r.filter.ip_protocol) + if r.filter.ip_protocol != null + ]) + error_message = "Unsupported protocol for route." + } + validation { + condition = alltrue([ + for r in var.policy_based_routes : + ( + (r.use_default_routing == true ? 1 : 0) + + + (r.next_hop_ilb_ip != null ? 1 : 0) + ) == 1 + ]) + error_message = "Either set `use_default_routing = true` or specify an internal passthrough LB IP." + } + validation { + condition = alltrue([ + for r in var.policy_based_routes : + r.target.tags == null || r.target.interconnect_attachment == null + ]) + error_message = "Either use virtual machine tags or a vlan attachment region as a target." + } +} + variable "project_id" { description = "The ID of the project where this VPC will be created." type = string diff --git a/modules/net-vpc/versions.tf b/modules/net-vpc/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpc/versions.tf +++ b/modules/net-vpc/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpn-dynamic/versions.tf b/modules/net-vpn-dynamic/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpn-dynamic/versions.tf +++ b/modules/net-vpn-dynamic/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpn-ha/versions.tf b/modules/net-vpn-ha/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpn-ha/versions.tf +++ b/modules/net-vpn-ha/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/net-vpn-static/versions.tf b/modules/net-vpn-static/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/net-vpn-static/versions.tf +++ b/modules/net-vpn-static/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/organization/README.md b/modules/organization/README.md index 90ab4574..dfbac180 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -453,7 +453,7 @@ module "org" { "roles/resourcemanager.tagAdmin" = ["group:${var.group_email}"] } values = { - dev = null + dev = {} prod = { description = "Environment: production." iam = { @@ -480,6 +480,7 @@ module "org" { | [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | google_org_policy_policy | | [outputs.tf](./outputs.tf) | Module outputs. | | | [tags.tf](./tags.tf) | None | google_tags_tag_binding · google_tags_tag_key · google_tags_tag_key_iam_binding · google_tags_tag_value · google_tags_tag_value_iam_binding | +| [variables-tags.tf](./variables-tags.tf) | None | | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -487,7 +488,7 @@ module "org" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [organization_id](variables.tf#L211) | Organization id in organizations/nnnnnn format. | string | ✓ | | +| [organization_id](variables.tf#L189) | Organization id in organizations/nnnnnn format. | string | ✓ | | | [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | | [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | | [factories_config](variables.tf#L31) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | @@ -499,11 +500,11 @@ module "org" { | [logging_data_access](variables.tf#L95) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | | [logging_exclusions](variables.tf#L110) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string) | | {} | | [logging_sinks](variables.tf#L117) | Logging sinks to create for the organization. | map(object({…})) | | {} | -| [network_tags](variables.tf#L148) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [org_policies](variables.tf#L170) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | -| [org_policy_custom_constraints](variables.tf#L197) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | -| [tag_bindings](variables.tf#L220) | Tag bindings for this organization, in key => tag value id format. | map(string) | | null | -| [tags](variables.tf#L226) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | +| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | +| [org_policies](variables.tf#L148) | Organization policies applied to this organization keyed by policy name. | map(object({…})) | | {} | +| [org_policy_custom_constraints](variables.tf#L175) | Organization policy custom constraints keyed by constraint name. | map(object({…})) | | {} | +| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this organization, in key => tag value id format. | map(string) | | {} | +| [tags](variables-tags.tf#L52) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | ## Outputs diff --git a/modules/organization/tags.tf b/modules/organization/tags.tf index 7fb1c068..d25757c2 100644 --- a/modules/organization/tags.tf +++ b/modules/organization/tags.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,12 @@ locals { _tag_values = flatten([ for tag, attrs in local.tags : [ - for value, value_attrs in coalesce(attrs.values, {}) : { - description = coalesce( - value_attrs == null ? null : value_attrs.description, - "Managed by the Terraform organization module." - ) - key = "${tag}/${value}" - id = try(value_attrs.id, null) - name = value - roles = keys(coalesce( - value_attrs == null ? null : value_attrs.iam, {} - )) + for value, value_attrs in attrs.values : { + description = value_attrs.description, + key = "${tag}/${value}" + id = try(value_attrs.id, null) + name = value + roles = keys(value_attrs.iam) tag = tag tag_id = attrs.id tag_network = try(attrs.network, null) != null @@ -47,7 +42,7 @@ locals { ]) _tags_iam = flatten([ for tag, attrs in local.tags : [ - for role in keys(coalesce(attrs.iam, {})) : { + for role in keys(attrs.iam) : { role = role tag = tag tag_id = attrs.id @@ -129,7 +124,7 @@ resource "google_tags_tag_value_iam_binding" "default" { # bindings resource "google_tags_tag_binding" "binding" { - for_each = coalesce(var.tag_bindings, {}) + for_each = var.tag_bindings parent = "//cloudresourcemanager.googleapis.com/${var.organization_id}" tag_value = each.value } diff --git a/modules/organization/variables-tags.tf b/modules/organization/variables-tags.tf new file mode 100644 index 00000000..4688504d --- /dev/null +++ b/modules/organization/variables-tags.tf @@ -0,0 +1,79 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "network_tags" { + description = "Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level." + type = map(object({ + description = optional(string, "Managed by the Terraform organization module.") + iam = optional(map(list(string)), {}) + id = optional(string) + network = string # project_id/vpc_name + values = optional(map(object({ + description = optional(string, "Managed by the Terraform organization module.") + iam = optional(map(list(string)), {}) + })), {}) + })) + nullable = false + default = {} + validation { + condition = ( + alltrue([ + for k, v in var.network_tags : v != null + ]) && + # all values are non-null + alltrue(flatten([ + for k, v in var.network_tags : [for k2, v2 in v.values : v2 != null] + ])) + ) + error_message = "Use an empty map instead of null as value." + } +} + +variable "tag_bindings" { + description = "Tag bindings for this organization, in key => tag value id format." + type = map(string) + default = {} + nullable = false +} + +variable "tags" { + description = "Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level." + type = map(object({ + description = optional(string, "Managed by the Terraform organization module.") + iam = optional(map(list(string)), {}) + id = optional(string) + values = optional(map(object({ + description = optional(string, "Managed by the Terraform organization module.") + iam = optional(map(list(string)), {}) + id = optional(string) + })), {}) + })) + nullable = false + default = {} + validation { + condition = ( + # all keys are non-null + alltrue([ + for k, v in var.tags : v != null + ]) && + # all values are non-null + alltrue(flatten([ + for k, v in var.tags : [for k2, v2 in v.values : v2 != null] + ])) + ) + error_message = "Use an empty map instead of null as value." + } +} diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index f11900db..61e76d0f 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -145,28 +145,6 @@ variable "logging_sinks" { } } -variable "network_tags" { - description = "Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level." - type = map(object({ - description = optional(string, "Managed by the Terraform organization module.") - iam = optional(map(list(string)), {}) - id = optional(string) - network = string # project_id/vpc_name - values = optional(map(object({ - description = optional(string, "Managed by the Terraform organization module.") - iam = optional(map(list(string)), {}) - })), {}) - })) - nullable = false - default = {} - validation { - condition = alltrue([ - for k, v in var.network_tags : v != null - ]) - error_message = "Use an empty map instead of null as value." - } -} - variable "org_policies" { description = "Organization policies applied to this organization keyed by policy name." type = map(object({ @@ -216,31 +194,3 @@ variable "organization_id" { error_message = "The organization_id must in the form organizations/nnn." } } - -variable "tag_bindings" { - description = "Tag bindings for this organization, in key => tag value id format." - type = map(string) - default = null -} - -variable "tags" { - description = "Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level." - type = map(object({ - description = optional(string, "Managed by the Terraform organization module.") - iam = optional(map(list(string)), {}) - id = optional(string) - values = optional(map(object({ - description = optional(string, "Managed by the Terraform organization module.") - iam = optional(map(list(string)), {}) - id = optional(string) - })), {}) - })) - nullable = false - default = {} - validation { - condition = alltrue([ - for k, v in var.tags : v != null - ]) - error_message = "Use an empty map instead of null as value." - } -} diff --git a/modules/organization/versions.tf b/modules/organization/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/organization/versions.tf +++ b/modules/organization/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/project/README.md b/modules/project/README.md index fb085662..b1a36720 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -18,7 +18,8 @@ This module implements the creation and management of one GCP project including - [Log Sinks](#log-sinks) - [Data Access Logs](#data-access-logs) - [Cloud KMS Encryption Keys](#cloud-kms-encryption-keys) -- [Tags](#tags) +- [Attaching Tags](#attaching-tags) +- [Project-scoped Tags](#project-scoped-tags) - [Outputs](#outputs) - [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it) - [Files](#files) @@ -233,7 +234,7 @@ The module allows managing Shared VPC status for both hosts and service projects Project service association for VPC host projects can be - authoritatively managed in the host project by enabling Shared VPC and specifying the set of service projects, or -- additively managed in service projects by by enabling Shared VPC in the host project and then "attaching" each service project independently +- additively managed in service projects by enabling Shared VPC in the host project and then "attaching" each service project independently IAM bindings in the host project for API service identities can be managed from service projects in two different ways: @@ -317,9 +318,51 @@ module "service-project" { # tftest modules=2 resources=9 inventory=shared-vpc-auto-grants.yaml e2e ``` -In specific cases it might make sense to selectively grant the `compute.networkUser` role for service identities at the subnet level, and while that is best done via org policies it's also supported by this module. +The `compute.networkUser` role for identities other than API services (e.g. users, groups or service accounts) can be managed via the `network_users` attribute, by specifying the list of identities. Avoid using dynamically generated lists, as this attribute is involved in a `for_each` loop and may result in Terraform errors. -In this example, Compute service identity will be granted compute.networkUser in the `gce` subnet defined in `europe-west1` region. +Note that this configuration grants the role at project level which results in the identities being able to configure resources on all the VPCs and subnets belonging to the host project. The most reliable way to restrict which subnets can be used on the newly created project is via the `compute.restrictSharedVpcSubnetworks` organization policy. For more information on the Org Policy configuration check the corresponding [Organization Policy section](#organization-policies). The following example details this configuration. + +```hcl +module "host-project" { + source = "./fabric/modules/project" + billing_account = var.billing_account_id + name = "host" + parent = var.folder_id + prefix = var.prefix + shared_vpc_host_config = { + enabled = true + } +} + +module "service-project" { + source = "./fabric/modules/project" + billing_account = var.billing_account_id + name = "service" + parent = var.folder_id + prefix = var.prefix + org_policies = { + "compute.restrictSharedVpcSubnetworks" = { + rules = [{ + allow = { + values = ["projects/host/regions/europe-west1/subnetworks/prod-default-ew1"] + } + }] + } + } + services = [ + "container.googleapis.com", + ] + shared_vpc_service_config = { + host_project = module.host-project.project_id + network_users = ["group:${var.group_email}"] + # reuse the list of services from the module's outputs + service_iam_grants = module.service-project.services + } +} +# tftest modules=2 resources=11 inventory=shared-vpc-host-project-iam.yaml e2e +``` + +In specific cases it might make sense to selectively grant the `compute.networkUser` role for service identities at the subnet level, and while that is best done via org policies it's also supported by this module. In this example, Compute service identity and `team-1@example.com` Google Group will be granted compute.networkUser in the `gce` subnet defined in `europe-west1` region in the `host` project (not included in the example) via the `service_identity_subnet_iam` and `network_subnet_users` attributes. ```hcl module "host-project" { @@ -347,9 +390,12 @@ module "service-project" { service_identity_subnet_iam = { "europe-west1/gce" = ["compute"] } + network_subnet_users = { + "europe-west1/gce" = ["group:team-1@example.com"] + } } } -# tftest modules=2 resources=6 inventory=shared-vpc-subnet-grants.yaml +# tftest modules=2 resources=7 inventory=shared-vpc-subnet-grants.yaml ``` ## Organization Policies @@ -610,9 +656,9 @@ module "project" { # tftest modules=1 resources=6 e2e ``` -## Tags +## Attaching Tags -Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. +You can attach secure tags to a project with the `tag_bindings` attribute ```hcl module "org" { @@ -621,10 +667,9 @@ module "org" { tags = { environment = { description = "Environment specification." - iam = null values = { - dev = null - prod = null + dev = {} + prod = {} } } } @@ -642,6 +687,41 @@ module "project" { # tftest modules=2 resources=6 ``` +## Project-scoped Tags + +To create project-scoped secure tags, use the `tags` and `network_tags` attributes. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + parent = var.folder_id + tags = { + mytag1 = {} + mytag2 = { + iam = { + "roles/resourcemanager.tagAdmin" = ["user:admin@example.com"] + } + values = { + myvalue1 = {} + myvalue2 = { + iam = { + "roles/resourcemanager.tagUser" = ["user:user@example.com"] + } + } + } + } + } + network_tags = { + my_net_tag = { + network = "${var.project_id}/${var.vpc.name}" + } + } +} +# tftest modules=1 resources=8 +``` + + ## Outputs Most of this module's outputs depend on its resources, to allow Terraform to compute all dependencies required for the project to be correctly configured. This allows you to reference outputs like `project_id` in other modules or resources without having to worry about setting `depends_on` blocks manually. @@ -891,7 +971,8 @@ module "bucket" { | [outputs.tf](./outputs.tf) | Module outputs. | | | [service-accounts.tf](./service-accounts.tf) | Service identities and supporting resources. | google_kms_crypto_key_iam_member · google_project_default_service_accounts · google_project_iam_member · google_project_service_identity | | [shared-vpc.tf](./shared-vpc.tf) | Shared VPC project-level configuration. | google_compute_shared_vpc_host_project · google_compute_shared_vpc_service_project · google_compute_subnetwork_iam_member · google_project_iam_member | -| [tags.tf](./tags.tf) | None | google_tags_tag_binding | +| [tags.tf](./tags.tf) | None | google_tags_tag_binding · google_tags_tag_key · google_tags_tag_key_iam_binding · google_tags_tag_value · google_tags_tag_value_iam_binding | +| [variables-tags.tf](./variables-tags.tf) | None | | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | | [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | google_access_context_manager_service_perimeter_resource | @@ -919,6 +1000,7 @@ module "bucket" { | [logging_exclusions](variables.tf#L151) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | | [logging_sinks](variables.tf#L158) | Logging sinks to create for this project. | map(object({…})) | | {} | | [metric_scopes](variables.tf#L189) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | [org_policies](variables.tf#L201) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | | [parent](variables.tf#L228) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | | [prefix](variables.tf#L238) | Optional prefix used to generate project id and name. | string | | null | @@ -929,9 +1011,10 @@ module "bucket" { | [service_perimeter_standard](variables.tf#L280) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | string | | null | | [services](variables.tf#L286) | Service APIs to enable. | list(string) | | [] | | [shared_vpc_host_config](variables.tf#L292) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L301) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L324) | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | -| [tag_bindings](variables.tf#L330) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | +| [shared_vpc_service_config](variables.tf#L301) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L329) | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | +| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | +| [tags](variables-tags.tf#L51) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | ## Outputs diff --git a/modules/project/shared-vpc.tf b/modules/project/shared-vpc.tf index 91a56a90..925b5aeb 100644 --- a/modules/project/shared-vpc.tf +++ b/modules/project/shared-vpc.tf @@ -68,6 +68,24 @@ locals { for v in local._svpc_service_subnet_iam : "${v.region}:${v.subnet}:${v.service}" => v } + # normalize the network user subnet IAM binding + _svpc_network_user_subnet_iam = ( + local._svpc.network_subnet_users == null || local._svpc.host_project == null + ? [] + : flatten([ + for subnet, members in local._svpc.network_subnet_users : [ + for member in members : { + region = split("/", subnet)[0] + subnet = split("/", subnet)[1] + member = member + } + ] + ]) + ) + svpc_network_user_subnet_iam = { + for v in local._svpc_network_user_subnet_iam : + "${v.region}:${v.subnet}:${v.member}" => v + } } resource "google_compute_shared_vpc_host_project" "shared_vpc_host" { @@ -111,6 +129,14 @@ resource "google_project_iam_member" "shared_vpc_host_robots" { ] } +resource "google_project_iam_member" "shared_vpc_host_iam" { + for_each = toset(var.shared_vpc_service_config.network_users) + project = var.shared_vpc_service_config.host_project + role = "roles/compute.networkUser" + member = each.value + depends_on = [] +} + resource "google_compute_subnetwork_iam_member" "shared_vpc_host_robots" { for_each = local.svpc_service_subnet_iam project = var.shared_vpc_service_config.host_project @@ -131,3 +157,12 @@ resource "google_compute_subnetwork_iam_member" "shared_vpc_host_robots" { data.google_storage_project_service_account.gcs_sa, ] } + +resource "google_compute_subnetwork_iam_member" "shared_vpc_host_subnets_iam" { + for_each = local.svpc_network_user_subnet_iam + project = var.shared_vpc_service_config.host_project + region = each.value.region + subnetwork = each.value.subnet + role = "roles/compute.networkUser" + member = each.value.member +} diff --git a/modules/project/tags.tf b/modules/project/tags.tf index 683143bd..4f2a6664 100644 --- a/modules/project/tags.tf +++ b/modules/project/tags.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,115 @@ * limitations under the License. */ +locals { + _tag_values = flatten([ + for tag, attrs in local.tags : [ + for value, value_attrs in attrs.values : { + description = value_attrs.description, + key = "${tag}/${value}" + id = try(value_attrs.id, null) + name = value + roles = keys(value_attrs.iam) + tag = tag + tag_id = attrs.id + tag_network = try(attrs.network, null) != null + } + ] + ]) + _tag_values_iam = flatten([ + for key, value_attrs in local.tag_values : [ + for role in value_attrs.roles : { + id = value_attrs.id + key = value_attrs.key + name = value_attrs.name + role = role + tag = value_attrs.tag + } + ] + ]) + _tags_iam = flatten([ + for tag, attrs in local.tags : [ + for role in keys(attrs.iam) : { + role = role + tag = tag + tag_id = attrs.id + } + ] + ]) + tag_values = { + for t in local._tag_values : t.key => t + } + tag_values_iam = { + for t in local._tag_values_iam : "${t.key}:${t.role}" => t + } + tags = merge(var.tags, var.network_tags) + tags_iam = { + for t in local._tags_iam : "${t.tag}:${t.role}" => t + } +} + +# keys + +resource "google_tags_tag_key" "default" { + for_each = { for k, v in local.tags : k => v if v.id == null } + parent = "projects/${local.project.project_id}" + purpose = ( + lookup(each.value, "network", null) == null ? null : "GCE_FIREWALL" + ) + purpose_data = ( + lookup(each.value, "network", null) == null ? null : { network = each.value.network } + ) + short_name = each.key + description = each.value.description + # depends_on = [ + # google_organization_iam_binding.authoritative, + # google_organization_iam_binding.bindings, + # google_organization_iam_member.bindings + # ] +} + +resource "google_tags_tag_key_iam_binding" "default" { + for_each = local.tags_iam + tag_key = ( + each.value.tag_id == null + ? google_tags_tag_key.default[each.value.tag].id + : each.value.tag_id + ) + role = each.value.role + members = coalesce( + local.tags[each.value.tag]["iam"][each.value.role], [] + ) +} + +# values + +resource "google_tags_tag_value" "default" { + for_each = { for k, v in local.tag_values : k => v if v.id == null } + parent = ( + each.value.tag_id == null + ? google_tags_tag_key.default[each.value.tag].id + : each.value.tag_id + ) + short_name = each.value.name + description = each.value.description +} + +resource "google_tags_tag_value_iam_binding" "default" { + for_each = local.tag_values_iam + tag_value = ( + each.value.id == null + ? google_tags_tag_value.default[each.value.key].id + : each.value.id + ) + role = each.value.role + members = coalesce( + local.tags[each.value.tag]["values"][each.value.name]["iam"][each.value.role], + [] + ) +} + +# bindings + resource "google_tags_tag_binding" "binding" { for_each = coalesce(var.tag_bindings, {}) parent = "//cloudresourcemanager.googleapis.com/projects/${local.project.number}" diff --git a/modules/project/variables-tags.tf b/modules/project/variables-tags.tf new file mode 100644 index 00000000..8914aae6 --- /dev/null +++ b/modules/project/variables-tags.tf @@ -0,0 +1,78 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "network_tags" { + description = "Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level." + type = map(object({ + description = optional(string, "Managed by the Terraform project module.") + iam = optional(map(list(string)), {}) + id = optional(string) + network = string # project_id/vpc_name + values = optional(map(object({ + description = optional(string, "Managed by the Terraform project module.") + iam = optional(map(list(string)), {}) + })), {}) + })) + nullable = false + default = {} + validation { + condition = ( + alltrue([ + for k, v in var.network_tags : v != null + ]) && + # all values are non-null + alltrue(flatten([ + for k, v in var.network_tags : [for k2, v2 in v.values : v2 != null] + ])) + ) + error_message = "Use an empty map instead of null as value." + } +} + +variable "tag_bindings" { + description = "Tag bindings for this project, in key => tag value id format." + type = map(string) + default = null +} + +variable "tags" { + description = "Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level." + type = map(object({ + description = optional(string, "Managed by the Terraform project module.") + iam = optional(map(list(string)), {}) + id = optional(string) + values = optional(map(object({ + description = optional(string, "Managed by the Terraform project module.") + iam = optional(map(list(string)), {}) + id = optional(string) + })), {}) + })) + nullable = false + default = {} + validation { + condition = ( + # all keys are non-null + alltrue([ + for k, v in var.tags : v != null + ]) && + # all values are non-null + alltrue(flatten([ + for k, v in var.tags : [for k2, v2 in v.values : v2 != null] + ])) + ) + error_message = "Use an empty map instead of null as value." + } +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 7ca60036..e17e44f6 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -303,9 +303,11 @@ variable "shared_vpc_service_config" { # the list of valid service identities is in service-agents.yaml type = object({ host_project = string + network_users = optional(list(string), []) service_identity_iam = optional(map(list(string)), {}) service_identity_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) + network_subnet_users = optional(map(list(string)), {}) }) default = { host_project = null @@ -314,10 +316,13 @@ variable "shared_vpc_service_config" { validation { condition = var.shared_vpc_service_config.host_project != null || ( var.shared_vpc_service_config.host_project == null && + length(var.shared_vpc_service_config.network_users) == 0 && length(var.shared_vpc_service_config.service_iam_grants) == 0 && - length(var.shared_vpc_service_config.service_iam_grants) == 0 + length(var.shared_vpc_service_config.service_identity_iam) == 0 && + length(var.shared_vpc_service_config.service_identity_subnet_iam) == 0 && + length(var.shared_vpc_service_config.network_subnet_users) == 0 ) - error_message = "You need to provide host_project when providing service_identity_iam or service_iam_grants" + error_message = "You need to provide host_project when providing Shared VPC host and subnet IAM permissions." } } @@ -326,9 +331,3 @@ variable "skip_delete" { type = bool default = false } - -variable "tag_bindings" { - description = "Tag bindings for this project, in key => tag value id format." - type = map(string) - default = null -} diff --git a/modules/project/versions.tf b/modules/project/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/project/versions.tf +++ b/modules/project/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/projects-data-source/versions.tf b/modules/projects-data-source/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/projects-data-source/versions.tf +++ b/modules/projects-data-source/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/pubsub/versions.tf b/modules/pubsub/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/pubsub/versions.tf +++ b/modules/pubsub/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/secret-manager/versions.tf b/modules/secret-manager/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/secret-manager/versions.tf +++ b/modules/secret-manager/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/service-directory/versions.tf b/modules/service-directory/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/service-directory/versions.tf +++ b/modules/service-directory/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/source-repository/versions.tf b/modules/source-repository/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/source-repository/versions.tf +++ b/modules/source-repository/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/vpc-sc/versions.tf b/modules/vpc-sc/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/vpc-sc/versions.tf +++ b/modules/vpc-sc/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/modules/workstation-cluster/versions.tf b/modules/workstation-cluster/versions.tf index 4d3bd967..4a283718 100644 --- a/modules/workstation-cluster/versions.tf +++ b/modules/workstation-cluster/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/tests/blueprints/factories/project_factory/examples/example.yaml b/tests/blueprints/factories/project_factory/examples/example.yaml index 4e636946..5595a729 100644 --- a/tests/blueprints/factories/project_factory/examples/example.yaml +++ b/tests/blueprints/factories/project_factory/examples/example.yaml @@ -102,14 +102,6 @@ values: environment: test team: foo timeouts: null - ? module.project-factory.module.projects["prj-app-2"].google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:cloudservices"] - : condition: [] - project: foo-host - role: roles/compute.networkUser - ? module.project-factory.module.projects["prj-app-2"].google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:container-engine"] - : condition: [] - project: foo-host - role: roles/compute.networkUser ? module.project-factory.module.projects["prj-app-2"].google_project_iam_member.shared_vpc_host_robots["roles/container.hostServiceAgentUser:container-engine"] : condition: [] project: foo-host @@ -118,6 +110,18 @@ values: : condition: [] project: foo-host role: roles/vpcaccess.user + ? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:cloudservices"] + : condition: [ ] + project: foo-host + role: roles/compute.networkUser + ? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_robots["europe-west1:prod-default-ew1:container-engine"] + : condition: [ ] + project: foo-host + role: roles/compute.networkUser + ? module.project-factory.module.projects["prj-app-2"].google_compute_subnetwork_iam_member.shared_vpc_host_subnets_iam["europe-west1:prod-default-ew1:group:team-1@example.com"] + : condition: [ ] + project: foo-host + role: roles/compute.networkUser module.project-factory.module.projects["prj-app-2"].google_project_service.project_services["compute.googleapis.com"]: disable_dependent_services: false disable_on_destroy: false @@ -148,6 +152,21 @@ values: project: test-pf-prj-app-2 service: storage.googleapis.com timeouts: null + module.project-factory.module.projects["prj-app-2"].google_org_policy_policy.default["compute.restrictSharedVpcSubnetworks"]: + name: projects/test-pf-prj-app-2/policies/compute.restrictSharedVpcSubnetworks + parent: projects/test-pf-prj-app-2 + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [ ] + deny_all: null + enforce: null + values: + - allowed_values: + - projects/foo-host/regions/europe-west1/subnetworks/prod-default-ew1 + denied_values: null module.project-factory.module.projects["prj-app-3"].data.google_storage_project_service_account.gcs_sa[0]: project: test-pf-prj-app-3 user_project: null @@ -223,14 +242,16 @@ values: counts: google_compute_shared_vpc_service_project: 1 + google_compute_subnetwork_iam_member: 3 google_essential_contacts_contact: 3 google_kms_crypto_key_iam_member: 1 google_project: 3 - google_project_iam_member: 6 + google_project_iam_member: 4 google_project_service: 11 google_service_account: 3 google_storage_project_service_account: 3 + google_org_policy_policy: 1 modules: 7 - resources: 31 + resources: 33 outputs: {} diff --git a/tests/examples_e2e/setup_module/versions.tf b/tests/examples_e2e/setup_module/versions.tf index 4d3bd967..4a283718 100644 --- a/tests/examples_e2e/setup_module/versions.tf +++ b/tests/examples_e2e/setup_module/versions.tf @@ -13,7 +13,7 @@ # limitations under the License. terraform { - required_version = ">= 1.4.4" + required_version = ">= 1.5.1" required_providers { google = { source = "hashicorp/google" diff --git a/tests/fast/stages/s2_networking_a_peering/stage.yaml b/tests/fast/stages/s2_networking_a_peering/stage.yaml index 7c9212b6..a3d11ec5 100644 --- a/tests/fast/stages/s2_networking_a_peering/stage.yaml +++ b/tests/fast/stages/s2_networking_a_peering/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 28 - resources: 145 + resources: 147 diff --git a/tests/fast/stages/s2_networking_b_vpn/stage.yaml b/tests/fast/stages/s2_networking_b_vpn/stage.yaml index 3c65fddf..3ddca617 100644 --- a/tests/fast/stages/s2_networking_b_vpn/stage.yaml +++ b/tests/fast/stages/s2_networking_b_vpn/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 30 - resources: 182 + resources: 184 diff --git a/tests/fast/stages/s2_networking_c_nva/stage.yaml b/tests/fast/stages/s2_networking_c_nva/stage.yaml index 43136448..6cfcb15c 100644 --- a/tests/fast/stages/s2_networking_c_nva/stage.yaml +++ b/tests/fast/stages/s2_networking_c_nva/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 42 - resources: 192 + resources: 194 diff --git a/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml b/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml index 701e186a..db0e5f38 100644 --- a/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml +++ b/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 21 - resources: 165 + resources: 169 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml index 66338505..ab5d08e0 100644 --- a/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml +++ b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 36 - resources: 203 + resources: 205 diff --git a/tests/modules/net_vpc/examples/pbr.yaml b/tests/modules/net_vpc/examples/pbr.yaml new file mode 100644 index 00000000..4255e4e6 --- /dev/null +++ b/tests/modules/net_vpc/examples/pbr.yaml @@ -0,0 +1,68 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +values: + module.vpc.google_compute_network.network[0]: + auto_create_subnetworks: false + delete_default_routes_on_create: false + description: Terraform-managed. + enable_ula_internal_ipv6: null + name: my-vpc + network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL + project: project-id + routing_mode: GLOBAL + timeouts: null + ? module.vpc.google_network_connectivity_policy_based_route.default["send-all-to-nva"] + : description: Terraform-managed. + filter: + - dest_range: 0.0.0.0/0 + ip_protocol: ALL + protocol_version: IPV4 + src_range: 10.0.0.0/8 + interconnect_attachment: + - region: europe-west8 + labels: null + name: my-vpc-send-all-to-nva + next_hop_ilb_ip: 10.0.0.253 + next_hop_other_routes: null + priority: 101 + project: project-id + timeouts: null + virtual_machine: [] + ? module.vpc.google_network_connectivity_policy_based_route.default["skip-pbr-for-nva"] + : description: Terraform-managed. + filter: + - dest_range: 0.0.0.0/0 + ip_protocol: ALL + protocol_version: IPV4 + src_range: 0.0.0.0/0 + interconnect_attachment: [] + labels: null + name: my-vpc-skip-pbr-for-nva + next_hop_ilb_ip: null + next_hop_other_routes: DEFAULT_ROUTING + priority: 100 + project: project-id + timeouts: null + virtual_machine: + - tags: + - nva + +counts: + google_compute_network: 1 + google_network_connectivity_policy_based_route: 2 + modules: 1 + resources: 3 + +outputs: {} diff --git a/tests/modules/organization/tags.tfvars b/tests/modules/organization/tags.tfvars index 2a4dcb42..5d942674 100644 --- a/tests/modules/organization/tags.tfvars +++ b/tests/modules/organization/tags.tfvars @@ -13,8 +13,8 @@ tags = { baz = { id = "tagKeys/1234567890" values = { - one = null - two = null + one = {} + two = {} } } foobar = { @@ -25,7 +25,7 @@ tags = { ] } values = { - one = null + one = {} two = { description = "Foobar 2." iam = { diff --git a/tests/modules/project/examples/shared-vpc-host-project-iam.yaml b/tests/modules/project/examples/shared-vpc-host-project-iam.yaml new file mode 100644 index 00000000..741cf8a3 --- /dev/null +++ b/tests/modules/project/examples/shared-vpc-host-project-iam.yaml @@ -0,0 +1,62 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +values: + module.host-project.google_compute_shared_vpc_host_project.shared_vpc_host[0]: + project: test-host + module.host-project.google_project.project[0]: + project_id: test-host + module.service-project.google_compute_shared_vpc_service_project.shared_vpc_service[0]: + host_project: test-host + service_project: test-service + module.service-project.google_project.project[0]: + project_id: test-service + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:cloudservices"]: + condition: [] + project: test-host + role: roles/compute.networkUser + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:container"]: + condition: [] + project: test-host + role: roles/compute.networkUser + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/container.hostServiceAgentUser:container"]: + condition: [] + project: test-host + role: roles/container.hostServiceAgentUser + module.service-project.google_project_iam_member.shared_vpc_host_iam["group:organization-admins@example.org"]: + condition: [ ] + project: test-host + role: roles/compute.networkUser + module.service-project.google_org_policy_policy.default["compute.restrictSharedVpcSubnetworks"]: + name: projects/test-service/policies/compute.restrictSharedVpcSubnetworks + parent: projects/test-service + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [ ] + deny_all: null + enforce: null + values: + - allowed_values: + - projects/host/regions/europe-west1/subnetworks/prod-default-ew1 + denied_values: null + +counts: + google_compute_shared_vpc_host_project: 1 + google_compute_shared_vpc_service_project: 1 + google_project: 2 + google_project_iam_member: 5 + google_org_policy_policy: 1 diff --git a/tests/modules/project/examples/shared-vpc-subnet-grants.yaml b/tests/modules/project/examples/shared-vpc-subnet-grants.yaml index f245615f..077abf1a 100644 --- a/tests/modules/project/examples/shared-vpc-subnet-grants.yaml +++ b/tests/modules/project/examples/shared-vpc-subnet-grants.yaml @@ -57,10 +57,10 @@ values: counts: google_compute_shared_vpc_host_project: 1 google_compute_shared_vpc_service_project: 1 - google_compute_subnetwork_iam_member: 1 + google_compute_subnetwork_iam_member: 2 google_project: 2 google_project_service: 1 modules: 2 - resources: 6 + resources: 7 outputs: {} diff --git a/tools/lockfile/default-versions_override.tf b/tools/lockfile/default-versions_override.tf new file mode 100644 index 00000000..0cf33525 --- /dev/null +++ b/tools/lockfile/default-versions_override.tf @@ -0,0 +1,27 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +# This file specifies the required version of the github provider used +# during testing. This has to be in an override file, otherwise the +# setup-tf-providers job fails because it copies default-versions.tf +# before running `terraform init` + +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 5.0" + } + } +} diff --git a/tools/lockfile/main.tf b/tools/lockfile/main.tf index 0aecc3ec..7614f5bc 100644 --- a/tools/lockfile/main.tf +++ b/tools/lockfile/main.tf @@ -15,7 +15,7 @@ data "archive_file" "bundle" {} resource "azuread_user" "default" {} resource "azurerm_resource_group" "default" {} -resource "github_branch" "default" {} +resource "github_branch" "default" { provider = github } resource "google_service_account" "sa1" {} resource "google_service_account" "sa2" { provider = google-beta } resource "local_file" "default" {}