Merge branch 'master' into jd/serverless-program

This commit is contained in:
Julio Diez 2023-10-16 18:08:24 +02:00
commit 7d8f569cd1
47 changed files with 1289 additions and 553 deletions

View File

@ -4,7 +4,48 @@ All notable changes to this project will be documented in this file.
<!-- markdownlint-disable MD024 -->
## [Unreleased]
<!-- None < 2023-09-18 07:03:09+00:00 -->
<!-- None < 2023-10-04 10:23:37+00:00 -->
### BLUEPRINTS
- [[#1748](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1748)] Bump golang.org/x/net from 0.7.0 to 0.17.0 in /blueprints/cloud-operations/unmanaged-instances-healthcheck/function/restarter ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 2023-10-12 05:41:41+00:00 -->
- [[#1747](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1747)] Bump golang.org/x/net from 0.7.0 to 0.17.0 in /blueprints/cloud-operations/unmanaged-instances-healthcheck/function/healthchecker ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 2023-10-12 05:21:10+00:00 -->
- [[#1735](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1735)] Make deletion protection consistent across all modules ([juliocc](https://github.com/juliocc)) <!-- 2023-10-05 15:31:08+00:00 -->
### DOCUMENTATION
- [[#1743](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1743)] Billing account module ([ludoo](https://github.com/ludoo)) <!-- 2023-10-15 15:02:50+00:00 -->
### FAST
- [[#1760](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1760)] Add support for psa peered domains to fast stages ([ludoo](https://github.com/ludoo)) <!-- 2023-10-16 06:57:18+00:00 -->
- [[#1759](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1759)] Minor edits to FAST network stage READMEs ([ludoo](https://github.com/ludoo)) <!-- 2023-10-15 16:14:48+00:00 -->
- [[#1743](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1743)] Billing account module ([ludoo](https://github.com/ludoo)) <!-- 2023-10-15 15:02:50+00:00 -->
- [[#1735](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1735)] Make deletion protection consistent across all modules ([juliocc](https://github.com/juliocc)) <!-- 2023-10-05 15:31:08+00:00 -->
- [[#1734](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1734)] Update to lint.sh and wording to some tf ([bluPhy](https://github.com/bluPhy)) <!-- 2023-10-05 06:32:08+00:00 -->
- [[#1733](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1733)] Fix typo in FAST stage 2 README ([bluPhy](https://github.com/bluPhy)) <!-- 2023-10-04 22:22:44+00:00 -->
### MODULES
- [[#1762](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1762)] Make subnets depend on proxy only subnets ([juliocc](https://github.com/juliocc)) <!-- 2023-10-16 11:39:52+00:00 -->
- [[#1757](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1757)] Add autoclass to GCS ([jeroenmonteban](https://github.com/jeroenmonteban)) <!-- 2023-10-16 07:45:10+00:00 -->
- [[#1756](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1756)] Exposed stack_type variable in compute_vm module ([luigi-bitonti](https://github.com/luigi-bitonti)) <!-- 2023-10-16 06:28:57+00:00 -->
- [[#1743](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1743)] Billing account module ([ludoo](https://github.com/ludoo)) <!-- 2023-10-15 15:02:50+00:00 -->
- [[#1752](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1752)] Add outputs to BigQuery dataset module ([devuonocar](https://github.com/devuonocar)) <!-- 2023-10-13 15:02:48+00:00 -->
- [[#1754](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1754)] Fix typo in GKE nodepool taints ([ludoo](https://github.com/ludoo)) <!-- 2023-10-12 12:04:15+00:00 -->
- [[#1746](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1746)] Module autopilot bug fixes ([luigi-bitonti](https://github.com/luigi-bitonti)) <!-- 2023-10-12 10:40:29+00:00 -->
- [[#1745](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1745)] Add missing fields to Cloud Storage bucket ([devuonocar](https://github.com/devuonocar)) <!-- 2023-10-10 20:40:30+00:00 -->
- [[#1744](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1744)] Append "s" to pubsub backoff times ([juliocc](https://github.com/juliocc)) <!-- 2023-10-10 10:32:20+00:00 -->
- [[#1741](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1741)] Add PSA peered domains support to `net-vpc` ([juliocc](https://github.com/juliocc)) <!-- 2023-10-06 15:31:33+00:00 -->
- [[#1737](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1737)] Enforce mandatory types in all variables ([juliocc](https://github.com/juliocc)) <!-- 2023-10-06 09:44:34+00:00 -->
- [[#1732](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1732)] Added FQDN Network Policy feature on GKE Cluster ([luigi-bitonti](https://github.com/luigi-bitonti)) <!-- 2023-10-06 08:05:54+00:00 -->
- [[#1735](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1735)] Make deletion protection consistent across all modules ([juliocc](https://github.com/juliocc)) <!-- 2023-10-05 15:31:08+00:00 -->
- [[#1726](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1726)] Add materialized views for bigquery ([devuonocar](https://github.com/devuonocar)) <!-- 2023-10-04 12:25:57+00:00 -->
### TOOLS
- [[#1737](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1737)] Enforce mandatory types in all variables ([juliocc](https://github.com/juliocc)) <!-- 2023-10-06 09:44:34+00:00 -->
- [[#1734](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1734)] Update to lint.sh and wording to some tf ([bluPhy](https://github.com/bluPhy)) <!-- 2023-10-05 06:32:08+00:00 -->
## [27.0.0] - 2023-10-04

View File

@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin
Currently available modules:
- **foundational** - [billing budget](./modules/billing-budget), [Cloud Identity group](./modules/cloud-identity-group/), [folder](./modules/folder), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [organization](./modules/organization), [project](./modules/project), [projects-data-source](./modules/projects-data-source)
- **foundational** - [billing account](./modules/billing-account), [Cloud Identity group](./modules/cloud-identity-group/), [folder](./modules/folder), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [organization](./modules/organization), [project](./modules/project), [projects-data-source](./modules/projects-data-source)
- **networking** - [DNS](./modules/dns), [DNS Response Policy](./modules/dns-response-policy/), [Cloud Endpoints](./modules/endpoints), [address reservation](./modules/net-address), [NAT](./modules/net-cloudnat), [VLAN Attachment](./modules/net-vlan-attachment/), [External Application LB](./modules/net-lb-app-ext/), [External Passthrough Network LB](./modules/net-lb-ext), [Firewall policy](./modules/net-firewall-policy), [Internal Application LB](./modules/net-lb-app-int), [Internal Passthrough Network LB](./modules/net-lb-int), [Internal Proxy Network LB](./modules/net-lb-proxy-int), [IPSec over Interconnect](./modules/net-ipsec-over-interconnect), [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [VPN static](./modules/net-vpn-static), [Service Directory](./modules/service-directory), [Secure Web Proxy](./modules/net-swp)
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid), [GKE cluster](./modules/gke-cluster-standard), [GKE hub](./modules/gke-hub), [GKE nodepool](./modules/gke-nodepool), [GCVE private cloud](./modules/gcve-private-cloud)
- **data** - <!-- [AlloyDB instance](./modules/alloydb-instance), --> [BigQuery dataset](./modules/bigquery-dataset), [Bigtable instance](./modules/bigtable-instance), [Dataplex](./modules/dataplex), [Dataplex DataScan](./modules/dataplex-datascan/), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag), [Datafusion](./modules/datafusion), [Dataproc](./modules/dataproc), [GCS](./modules/gcs), [Pub/Sub](./modules/pubsub)

View File

@ -74,12 +74,3 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
role = "roles/billing.admin"
member = each.key
}
resource "google_billing_account_iam_member" "billing_ext_cost_manager" {
for_each = toset(
local.billing_mode == "resource" ? local.billing_ext_admins : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.costsManager"
member = each.key
}

View File

@ -34,8 +34,7 @@ locals {
authoritative = []
additive = (
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -66,8 +65,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -111,8 +109,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -129,8 +126,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -148,8 +144,7 @@ locals {
# TODO: align additive roles with the README
additive = (
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}

View File

@ -147,15 +147,12 @@ This configuration is battle-tested, and flexible enough to lend itself to simpl
### VPCs
VPCs are defined in separate files, one for `landing` and one for each of `prod` and `dev`.
Each file contains the same resources, described in the following paragraphs.
The **project** ([`project`](../../../modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)".
These files contain different resources:
The **VPC** ([`net-vpc`](../../../modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets), and can be easily customised to fit your needs.
Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs.
**Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
- **project** ([`projects`](../../../modules/project)): the "[host projects](https://cloud.google.com/vpc/docs/shared-vpc)" containing the VPCs and enabling the required APIs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manages the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy for the trusted landing VPC. Non-infrastructural subnets are created leveraging resource factories. Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by the variable `psa_ranges` if managed services are needed.
- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
### VPNs
@ -282,17 +279,12 @@ terraform apply
#### Private Google Access
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment.
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment:
For PGA to work:
- DNS response policies in the landing project implement rules for all supported domains reachable via PGA
- routes for the private and restricted ranges are defined in all VPCs
- Private Google Access should be enabled on the subnet. \
Subnets created by the `net-vpc` module are PGA-enabled by default.
- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \
Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed.
- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain), as implemented in module `googleapis-private-zone` in [`dns-landing.tf`](./dns-landing.tf)
To enable PGA access from on premises advertise the private/restricted ranges via the `landing-to-onprem-primary-vpn` variable, using router or tunnel custom advertisements.
## Customizations
@ -406,10 +398,10 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [factories_config](variables.tf#L80) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; dns_policy_rules_file &#61; optional&#40;string, &#34;data&#47;dns-policy-rules.yaml&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;net-default&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L121) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [peering_configs](variables-peerings.tf#L19) | Peering configurations. | <code title="object&#40;&#123;&#10; dev &#61; optional&#40;object&#40;&#123;&#10; export &#61; optional&#40;bool, true&#41;&#10; import &#61; optional&#40;bool, true&#41;&#10; public_export &#61; optional&#40;bool&#41;&#10; public_import &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; prod &#61; optional&#40;object&#40;&#123;&#10; export &#61; optional&#40;bool, true&#41;&#10; import &#61; optional&#40;bool, true&#41;&#10; public_export &#61; optional&#40;bool&#41;&#10; public_import &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L155) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L167) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L181) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L157) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L169) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L183) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@ -139,14 +139,16 @@ variable "psa_ranges" {
description = "IP ranges used for Private Service Access (CloudSQL, etc.)."
type = object({
dev = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
prod = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
})
default = null

View File

@ -153,15 +153,11 @@ This configuration is battle-tested, and flexible enough to lend itself to simpl
### VPCs
VPCs are defined in separate files, one for `landing` and one for each of `prod` and `dev`.
Each file contains the same resources, described in the following paragraphs.
These files contain different resources:
The **project** ([`project`](../../../modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)".
The **VPC** ([`net-vpc`](../../../modules/net-vpc)) manages the DNS inbound policy (for Landing), explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets), and can be easily customised to fit your needs.
Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs.
**Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
- **project** ([`projects`](../../../modules/project)): the "[host projects](https://cloud.google.com/vpc/docs/shared-vpc)" containing the VPCs and enabling the required APIs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manages the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy for the trusted landing VPC. Non-infrastructural subnets are created leveraging resource factories. Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by the variable `psa_ranges` if managed services are needed.
- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
### VPNs
@ -296,17 +292,12 @@ terraform apply
#### Private Google Access
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment.
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment:
For PGA to work:
- DNS response policies in the landing project implement rules for all supported domains reachable via PGA
- routes for the private and restricted ranges are defined in all VPCs
- Private Google Access should be enabled on the subnet. \
Subnets created by the `net-vpc` module are PGA-enabled by default.
- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \
Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed.
- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain), as implemented in module `googleapis-private-zone` in [`dns-landing.tf`](./dns-landing.tf)
To enable PGA access from on premises advertise the private/restricted ranges via the `landing-to-onprem-primary-vpn` variable, using router or tunnel custom advertisements.
## Customizations
@ -430,11 +421,11 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [dns](variables.tf#L72) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L80) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; dns_policy_rules_file &#61; optional&#40;string, &#34;data&#47;dns-policy-rules.yaml&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;net-default&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L121) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L155) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L167) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L157) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L169) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_configs](variables-vpn.tf#L17) | Hub to spokes VPN configurations. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; asn &#61; number&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; landing &#61; object&#40;&#123;&#10; asn &#61; number&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; asn &#61; number&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#123;&#10; asn &#61; 65501&#10; &#125;&#10; landing &#61; &#123;&#10; asn &#61; 65500&#10; &#125;&#10; prod &#61; &#123;&#10; asn &#61; 65502&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_primary_config](variables.tf#L181) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_primary_config](variables.tf#L183) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@ -139,14 +139,16 @@ variable "psa_ranges" {
description = "IP ranges used for Private Service Access (CloudSQL, etc.)."
type = object({
dev = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
prod = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
})
default = null

View File

@ -125,19 +125,7 @@ This stage uses a dedicated /11 block (10.64.0.0/11), which should be sized to t
The /11 block is evenly split in eight, smaller /16 blocks, assigned to different areas of the GCP network: *landing untrusted europe-west1*, *landing untrusted europe-west4*, *landing trusted europe-west1*, *landing untrusted europe-west4*, *development europe-west1*, *development europe-west4*, *production europe-west1*, *production europe-west4*.
The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed.
Spoke VPCs also define and reserve three "special" CIDR ranges, derived from their respective /16, dedicated to
- [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access):
- The second-last /24 range is used for PSA (CloudSQL, Postrgres)
- The third-last /24 range is used for PSA (CloudSQL, MySQL)
- [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal):
- The last /24 range
The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed. The last three /24 ranges can be used for [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access)via the `psa_ranges` variable, or for [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal) subnets via the factory.
This is a summary of the subnets allocated by default in this setup:
@ -148,23 +136,23 @@ This is a summary of the subnets allocated by default in this setup:
| landing-untrusted-default-ew1 | Untrusted landing subnet - europe-west1 | 10.128.0.0/24 |
| landing-untrusted-default-ew4 | Untrusted landing subnet - europe-west4 | 10.128.32.0/24 |
| dev-default-ew1 | Dev spoke subnet - europe-west1 | 10.68.0.0/24 |
| dev-default-ew1 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west1 | 10.68.253.0/24 |
| dev-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west1 | 10.68.254.0/24 |
| dev-default-ew1 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west1 | 10.68.255.0/24 |
| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.253.0/24 |
| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.254.0/24 |
| dev-default-ew1 | Free (L7 ILB) - europe-west1 | 10.68.255.0/24 |
| dev-default-ew4 | Dev spoke subnet - europe-west4 | 10.84.0.0/24 |
| dev-default-ew4 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west4 | 10.84.253.0/24 |
| dev-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west4 | 10.84.254.0/24 |
| dev-default-ew4 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west4 | 10.84.255.0/24 |
| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.253.0/24 |
| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.254.0/24 |
| dev-default-ew4 | Free (L7 ILB) - europe-west4 | 10.84.255.0/24 |
| prod-default-ew1 | Prod spoke subnet - europe-west1 | 10.72.0.0/24 |
| prod-default-ew1 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west1 | 10.72.253.0/24 |
| prod-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west1 | 10.72.254.0/24 |
| prod-default-ew1 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west1 | 10.72.255.0/24 |
| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.253.0/24 |
| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.254.0/24 |
| prod-default-ew1 | Free (L7 ILB) - europe-west1 | 10.72.255.0/24 |
| prod-default-ew4 | Prod spoke subnet - europe-west4 | 10.88.0.0/24 |
| prod-default-ew4 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west4 | 10.88.253.0/24 |
| prod-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west4 | 10.88.254.0/24 |
| prod-default-ew4 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west4 | 10.88.255.0/24 |
| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.253.0/24 |
| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.254.0/24 |
| prod-default-ew4 | Free (L7 ILB) - europe-west4 | 10.88.255.0/24 |
These subnets can advertised to on-premises as an aggregate /11 range (10.64.0.0/11). Refer to the `var.vpn_onprem_primary_config.router_config` and `var.vpn_onprem_secondary_config.router_config` variables to configure it.
These subnets can be advertised to on-premises as an aggregate /11 range (10.64.0.0/11). Refer to the `var.vpn_onprem_primary_config.router_config` and `var.vpn_onprem_secondary_config.router_config` variables to configure it.
Routes in GCP are either automatically created (for example, when a subnet is added to a VPC), manually created via static routes, dynamically exchanged through VPC peerings, or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) when a BGP session is established. BGP sessions can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements.
@ -231,10 +219,7 @@ VPCs are defined in separate files, one for `landing` (trusted and untrusted), o
These files contain different resources:
- **project** ([`projects`](../../../modules/project)): the "[host projects](https://cloud.google.com/vpc/docs/shared-vpc)" containing the VPCs and enabling the required APIs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manage the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy (for the trusted landing VPC). Subnets are created leveraging "resource factories": the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs.
Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manages the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy for the trusted landing VPC. Non-infrastructural subnets are created leveraging resource factories. Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by the variable `psa_ranges` if managed services are needed.
- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) (in the untrusted landing VPC only): it manages the networking infrastructure required to enable the Internet egress.
### VPNs
@ -364,17 +349,12 @@ terraform apply
#### Private Google Access
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment.
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment:
For PGA to work:
- DNS response policies in the landing project implement rules for all supported domains reachable via PGA
- routes for the private and restricted ranges are defined in all VPCs except untrusted
- Private Google Access should be enabled on the subnet. \
Subnets created by the `net-vpc` module are PGA-enabled by default.
- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \
Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed.
- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain), as implemented in module `googleapis-private-zone` in [`dns-landing.tf`](./dns-landing.tf)
To enable PGA access from on premises advertise the private/restricted ranges via the `vpn_onprem_primary_config` and `vpn_onprem_secondary_config` variables, using router or tunnel custom advertisements.
## Customizations
@ -488,11 +468,11 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [gcp_ranges](variables.tf#L111) | GCP address ranges in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp_dev_primary &#61; &#34;10.68.0.0&#47;16&#34;&#10; gcp_dev_secondary &#61; &#34;10.84.0.0&#47;16&#34;&#10; gcp_landing_trusted_primary &#61; &#34;10.64.0.0&#47;17&#34;&#10; gcp_landing_trusted_secondary &#61; &#34;10.80.0.0&#47;17&#34;&#10; gcp_landing_untrusted_primary &#61; &#34;10.64.127.0&#47;17&#34;&#10; gcp_landing_untrusted_secondary &#61; &#34;10.80.127.0&#47;17&#34;&#10; gcp_prod_primary &#61; &#34;10.72.0.0&#47;16&#34;&#10; gcp_prod_secondary &#61; &#34;10.88.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [onprem_cidr](variables.tf#L126) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L144) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L161) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L178) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L190) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L204) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_secondary_config](variables.tf#L247) | VPN gateway configuration for onprem interconnection in the secondary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L161) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L180) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L192) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L206) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_secondary_config](variables.tf#L249) | VPN gateway configuration for onprem interconnection in the secondary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@ -162,14 +162,16 @@ variable "psa_ranges" {
description = "IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format."
type = object({
dev = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
prod = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
})
default = null

View File

@ -112,15 +112,12 @@ This configuration is battle-tested, and flexible enough to lend itself to simpl
### VPCs
VPCs are defined in separate files, one for each of `prod` and `dev`.
Each file contains the same resources, described in the following paragraphs.
The **project** ([`project`](../../../modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)".
These files contain different resources:
The **VPC** ([`net-vpc`](../../../modules/net-vpc)) manages the DNS inbound policy, explicit routes for `{private,restricted}.googleapis.com`, and its **subnets**. Subnets are created leveraging a "resource factory" paradigm, where the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets), and can be easily customised to fit your needs.
Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs.
**Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
- **project** ([`project`](../../../modules/project)) contains the VPC, and enables the required APIs and sets itself as a "[host project](https://cloud.google.com/vpc/docs/shared-vpc)".
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manages the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy for the trusted landing VPC. Non-infrastructural subnets are created leveraging resource factories. Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by the variable `psa_ranges` if managed services are needed.
- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) manages the networking infrastructure required to enable internet egress.
### VPNs
@ -244,17 +241,12 @@ terraform apply
#### Private Google Access
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment.
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment:
For PGA to work:
- DNS response policies in the landing project implement rules for all supported domains reachable via PGA
- routes for the private and restricted ranges are defined in all VPCs
- Private Google Access should be enabled on the subnet. \
Subnets created by the `net-vpc` module are PGA-enabled by default.
- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \
Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC has explicit routes set in case the `0.0.0.0/0` route is changed.
- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain)
To enable PGA access from on premises advertise the private/restricted ranges via the `vpn_onprem_dev_primary_config` and `vpn_onprem_prod_primary_config` variables, using router or tunnel custom advertisements.
## Customizations
@ -348,11 +340,11 @@ Regions are defined via the `regions` variable which sets up a mapping between t
| [dns](variables.tf#L72) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#91;&#34;10.0.1.1&#34;&#93;&#10; dev &#61; &#91;&#34;10.0.2.1&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [factories_config](variables.tf#L81) | Configuration for network resource factories. | <code title="object&#40;&#123;&#10; data_dir &#61; optional&#40;string, &#34;data&#34;&#41;&#10; dns_policy_rules_file &#61; optional&#40;string, &#34;data&#47;dns-policy-rules.yaml&#34;&#41;&#10; firewall_policy_name &#61; optional&#40;string, &#34;net-default&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; data_dir &#61; &#34;data&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L122) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L139) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L156) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L166) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_dev_primary_config](variables.tf#L180) | VPN gateway configuration for onprem interconnection from dev in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_prod_primary_config](variables.tf#L223) | VPN gateway configuration for onprem interconnection from prod in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L139) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L158) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L168) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_dev_primary_config](variables.tf#L182) | VPN gateway configuration for onprem interconnection from dev in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_prod_primary_config](variables.tf#L225) | VPN gateway configuration for onprem interconnection from prod in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@ -140,14 +140,16 @@ variable "psa_ranges" {
description = "IP ranges used for Private Service Access (e.g. CloudSQL)."
type = object({
dev = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
prod = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
})
default = null

View File

@ -148,19 +148,7 @@ This stage uses a dedicated /11 block (10.64.0.0/11), which should be sized to t
The /11 block is evenly split in eight, smaller /16 blocks, assigned to different areas of the GCP network: *landing untrusted europe-west1*, *landing untrusted europe-west4*, *landing trusted europe-west1*, *landing untrusted europe-west4*, *development europe-west1*, *development europe-west4*, *production europe-west1*, *production europe-west4*.
The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed.
Spoke VPCs also define and reserve three "special" CIDR ranges, derived from their respective /16, dedicated to
- [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access):
- The second-last /24 range is used for PSA (CloudSQL, Postrgres)
- The third-last /24 range is used for PSA (CloudSQL, MySQL)
- [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal):
- The last /24 range
The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed. The last three /24 ranges can be used for [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access)via the `psa_ranges` variable, or for [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal) subnets via the factory.
This is a summary of the subnets allocated by default in this setup:
@ -171,23 +159,23 @@ This is a summary of the subnets allocated by default in this setup:
| landing-untrusted-default-ew1 | Untrusted landing subnet - europe-west1 | 10.128.0.0/24 |
| landing-untrusted-default-ew4 | Untrusted landing subnet - europe-west4 | 10.128.32.0/24 |
| dev-default-ew1 | Dev spoke subnet - europe-west1 | 10.68.0.0/24 |
| dev-default-ew1 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west1 | 10.68.253.0/24 |
| dev-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west1 | 10.68.254.0/24 |
| dev-default-ew1 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west1 | 10.68.255.0/24 |
| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.253.0/24 |
| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.254.0/24 |
| dev-default-ew1 | Free (L7 ILB) - europe-west1 | 10.68.255.0/24 |
| dev-default-ew4 | Dev spoke subnet - europe-west4 | 10.84.0.0/24 |
| dev-default-ew4 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west4 | 10.84.253.0/24 |
| dev-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west4 | 10.84.254.0/24 |
| dev-default-ew4 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west4 | 10.84.255.0/24 |
| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.253.0/24 |
| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.254.0/24 |
| dev-default-ew4 | Free (L7 ILB) - europe-west4 | 10.84.255.0/24 |
| prod-default-ew1 | Prod spoke subnet - europe-west1 | 10.72.0.0/24 |
| prod-default-ew1 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west1 | 10.72.253.0/24 |
| prod-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west1 | 10.72.254.0/24 |
| prod-default-ew1 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west1 | 10.72.255.0/24 |
| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.253.0/24 |
| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.254.0/24 |
| prod-default-ew1 | Free (L7 ILB) - europe-west1 | 10.72.255.0/24 |
| prod-default-ew4 | Prod spoke subnet - europe-west4 | 10.88.0.0/24 |
| prod-default-ew4 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west4 | 10.88.253.0/24 |
| prod-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west4 | 10.88.254.0/24 |
| prod-default-ew4 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west4 | 10.88.255.0/24 |
| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.253.0/24 |
| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.254.0/24 |
| prod-default-ew4 | Free (L7 ILB) - europe-west4 | 10.88.255.0/24 |
These subnets can advertised to on-premises as an aggregate /11 range (10.64.0.0/11). Refer to the `var.vpn_onprem_primary_config.router_config` and `var.vpn_onprem_secondary_config.router_config` variables to configure it.
These subnets can be advertised to on-premises as an aggregate /11 range (10.64.0.0/11). Refer to the `var.vpn_onprem_primary_config.router_config` and `var.vpn_onprem_secondary_config.router_config` variables to configure it.
Routes in GCP are either automatically created (for example, when a subnet is added to a VPC), manually created via static routes, dynamically exchanged through VPC peerings, or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) when a BGP session is established. BGP sessions can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements.
@ -252,10 +240,7 @@ VPCs are defined in separate files, one for `landing` (trusted and untrusted), o
These files contain different resources:
- **project** ([`projects`](../../../modules/project)): the "[host projects](https://cloud.google.com/vpc/docs/shared-vpc)" containing the VPCs and enabling the required APIs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manage the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy (for the trusted landing VPC). Subnets are created leveraging "resource factories": the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs.
Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs.
- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manages the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy for the trusted landing VPC. Non-infrastructural subnets are created leveraging resource factories. Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by the variable `psa_ranges` if managed services are needed.
- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) (in the untrusted landing VPC only): it manages the networking infrastructure required to enable the Internet egress.
### VPNs
@ -387,17 +372,12 @@ terraform apply
#### Private Google Access
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment.
[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment:
For PGA to work:
- DNS response policies in the landing project implement rules for all supported domains reachable via PGA
- routes for the private and restricted ranges are defined in all VPCs except untrusted
- Private Google Access should be enabled on the subnet. \
Subnets created by the `net-vpc` module are PGA-enabled by default.
- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \
Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed.
- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain), as implemented in module `googleapis-private-zone` in [`dns-landing.tf`](./dns-landing.tf)
To enable PGA access from on premises advertise the private/restricted ranges via the `vpn_onprem_primary_config` and `vpn_onprem_secondary_config` variables, using router or tunnel custom advertisements.
## Customizations
@ -515,12 +495,12 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
| [ncc_asn](variables.tf#L126) | The NCC Cloud Routers ASN configuration. | <code>map&#40;number&#41;</code> | | <code title="&#123;&#10; nva_primary &#61; 64513&#10; nva_secondary &#61; 64514&#10; trusted &#61; 64515&#10; untrusted &#61; 64512&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [onprem_cidr](variables.tf#L137) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L155) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L172) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L189) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L201) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L215) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_secondary_config](variables.tf#L258) | VPN gateway configuration for onprem interconnection in the secondary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [zones](variables.tf#L301) | Zones in which NVAs are deployed. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;b&#34;, &#34;c&#34;&#93;</code> | |
| [psa_ranges](variables.tf#L172) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | <code title="object&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [regions](variables.tf#L191) | Region definitions. | <code title="object&#40;&#123;&#10; primary &#61; string&#10; secondary &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; primary &#61; &#34;europe-west1&#34;&#10; secondary &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L203) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>1-resman</code> |
| [vpn_onprem_primary_config](variables.tf#L217) | VPN gateway configuration for onprem interconnection in the primary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpn_onprem_secondary_config](variables.tf#L260) | VPN gateway configuration for onprem interconnection in the secondary region. | <code title="object&#40;&#123;&#10; peer_external_gateways &#61; map&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; router_config &#61; object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; peer_gateway &#61; optional&#40;string, &#34;default&#34;&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [zones](variables.tf#L303) | Zones in which NVAs are deployed. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;b&#34;, &#34;c&#34;&#93;</code> | |
## Outputs

View File

@ -173,14 +173,16 @@ variable "psa_ranges" {
description = "IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format."
type = object({
dev = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
prod = object({
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
ranges = map(string)
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
})
})
default = null

View File

@ -30,14 +30,14 @@ These modules are used in the examples included in this repository. If you are u
## Foundational modules
- [billing budget](./billing-budget)
- [Billing account](./billing-account)
- [Cloud Identity group](./cloud-identity-group/)
- [folder](./folder)
- [service accounts](./iam-service-account)
- [logging bucket](./logging-bucket)
- [organization](./organization)
- [project](./project)
- [projects-data-source](./projects-data-source)
- [Folder](./folder)
- [Service accounts](./iam-service-account)
- [Logging bucket](./logging-bucket)
- [Organization](./organization)
- [Project](./project)
- [Projects (data source)](./projects-data-source)
## Networking modules

View File

@ -0,0 +1,237 @@
# Billing Account Module
This module allows managing resources and policies related to a billing account:
- IAM bindings
- log sinks
- billing budgets and their notifications
Managing billing-related resources via application default credentials [requires a billing project to be set](https://cloud.google.com/docs/authentication/troubleshoot-adc#user-creds-client-based). To configure one via Terraform you can use a snippet similar to this one:
```hcl
provider "google" {
billing_project = "my-project"
user_project_override = true
}
# tftest skip
```
<!-- BEGIN TOC -->
- [Examples](#examples)
- [IAM bindings](#iam-bindings)
- [Log sinks](#log-sinks)
- [Billing budgets](#billing-budgets)
- [PubSub update rules](#pubsub-update-rules)
- [Monitoring channels](#monitoring-channels)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples
### IAM bindings
Billing account IAM bindings implement [the same interface](../__docs/20230816-iam-refactor.md) used for all other modules.
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
group_iam = {
"billing-admins@example.org" = ["roles/billing.admin"]
}
iam = {
"roles/billing.admin" = [
"serviceAccount:foo@myprj.iam.gserviceaccount.com"
]
}
iam_bindings = {
conditional-admin = {
members = [
"serviceAccount:pf-dev@myprj.iam.gserviceaccount.com"
]
role = "roles/billing.admin"
condition = {
title = "pf-dev-conditional-billing-admin"
expression = (
"resource.matchTag('123456/environment', 'development')"
)
}
}
}
iam_bindings_additive = {
sa-net-iac-user = {
member = "serviceAccount:net-iac-0@myprj.iam.gserviceaccount.com"
role = "roles/billing.user"
}
}
}
# tftest modules=1 resources=3 inventory=iam.yaml
```
### Log sinks
Billing account log sinks use the same format used for log sinks in the resource manager modules (organization, folder, project).
```hcl
module "log-bucket-all" {
source = "./fabric/modules/logging-bucket"
parent_type = "project"
parent = "myprj"
id = "billing-account-all"
}
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
logging_sinks = {
all = {
destination = module.log-bucket-all.id
type = "logging"
}
}
}
# tftest modules=2 resources=3 inventory=logging.yaml
```
### Billing budgets
Billing budgets expose all the attributes of the underlying resource, and allow using external notification channels, or creating them via this same module.
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
}
}
}
# tftest modules=1 resources=1 inventory=budget-simple.yaml
```
#### PubSub update rules
Update rules can notify pubsub topics.
```hcl
module "pubsub-billing-topic" {
source = "./fabric/modules/pubsub"
project_id = "my-prj"
name = "budget-default"
}
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
update_rules = {
default = {
pubsub_topic = module.pubsub-billing-topic.id
}
}
}
}
}
# tftest modules=2 resources=2 inventory=budget-pubsub.yaml
```
#### Monitoring channels
Monitoring channels can be referenced in update rules either by passing in an existing channel id, or by using a reference to a key in the `budget_notification_channels` variable, that allows managing ad hoc monitoring channels.
<!-- markdownlint-disable MD034 -->
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budget_notification_channels = {
billing-default = {
project_id = "tf-playground-simple"
type = "email"
labels = {
email_address = "gcp-billing-admins@example.com"
}
}
}
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
update_rules = {
default = {
disable_default_iam_recipients = true
monitoring_notification_channels = ["billing-default"]
}
}
}
}
}
# tftest modules=1 resources=2 inventory=budget-monitoring-channel.yaml
```
<!-- markdownlint-enable -->
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [id](variables.tf#L165) | Billing account id. | <code>string</code> | ✓ | |
| [budget_notification_channels](variables.tf#L17) | Notification channels used by budget alerts. | <code title="map&#40;object&#40;&#123;&#10; project_id &#61; string&#10; type &#61; string&#10; description &#61; optional&#40;string&#41;&#10; display_name &#61; optional&#40;string&#41;&#10; enabled &#61; optional&#40;bool, true&#41;&#10; force_delete &#61; optional&#40;bool&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; sensitive_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; auth_token &#61; optional&#40;string&#41;&#10; password &#61; optional&#40;string&#41;&#10; service_key &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10; user_labels &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | <code title="map&#40;object&#40;&#123;&#10; amount &#61; object&#40;&#123;&#10; currency_code &#61; optional&#40;string&#41;&#10; nanos &#61; optional&#40;number&#41;&#10; units &#61; optional&#40;number&#41;&#10; use_last_period &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#10; display_name &#61; optional&#40;string&#41;&#10; filter &#61; optional&#40;object&#40;&#123;&#10; credit_types_treatment &#61; optional&#40;object&#40;&#123;&#10; exclude_all &#61; optional&#40;bool&#41;&#10; include_specified &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; label &#61; optional&#40;object&#40;&#123;&#10; key &#61; string&#10; value &#61; string&#10; &#125;&#41;&#41;&#10; period &#61; optional&#40;object&#40;&#123;&#10; calendar &#61; optional&#40;string&#41;&#10; custom &#61; optional&#40;object&#40;&#123;&#10; start_date &#61; object&#40;&#123;&#10; day &#61; number&#10; month &#61; number&#10; year &#61; number&#10; &#125;&#41;&#10; end_date &#61; optional&#40;object&#40;&#123;&#10; day &#61; number&#10; month &#61; number&#10; year &#61; number&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; projects &#61; optional&#40;list&#40;string&#41;&#41;&#10; resource_ancestors &#61; optional&#40;list&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; subaccounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; threshold_rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; percent &#61; number&#10; forecasted_spend &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; update_rules &#61; optional&#40;map&#40;object&#40;&#123;&#10; disable_default_iam_recipients &#61; optional&#40;bool&#41;&#10; monitoring_notification_channels &#61; optional&#40;list&#40;string&#41;&#41;&#10; pubsub_topic &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [group_iam](variables.tf#L121) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L128) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L135) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L150) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L170) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;object&#40;&#123;&#10; filter &#61; string&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; filter &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [projects](variables.tf#L203) | Projects associated with this billing account. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [billing_budget_ids](outputs.tf#L17) | Billing budget ids. | |
| [monitoring_notification_channel_ids](outputs.tf#L25) | Monitoring notification channel ids. | |
<!-- END TFDOC -->

View File

@ -0,0 +1,126 @@
/**
* 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.
*/
resource "google_monitoring_notification_channel" "default" {
for_each = var.budget_notification_channels
description = each.value.description
display_name = coalesce(
each.value.display_name, "Budget email notification ${each.key}."
)
project = each.value.project_id
enabled = each.value.enabled
force_delete = each.value.force_delete
type = each.value.type
labels = each.value.labels
user_labels = each.value.user_labels
dynamic "sensitive_labels" {
for_each = toset(coalesce(each.value.sensitive_labels, []))
content {
auth_token = sensitive_labels.value.auth_token
password = sensitive_labels.value.password
service_key = sensitive_labels.value.service_key
}
}
}
resource "google_billing_budget" "default" {
for_each = var.budgets
billing_account = var.id
display_name = each.value.display_name
dynamic "amount" {
for_each = each.value.amount.use_last_period == true ? [""] : []
content {
last_period_amount = true
}
}
dynamic "amount" {
for_each = each.value.amount.use_last_period != true ? [""] : []
content {
specified_amount {
currency_code = each.value.amount.currency_code
nanos = each.value.amount.nanos
units = each.value.amount.units
}
}
}
budget_filter {
calendar_period = try(each.value.filter.period.calendar, null)
credit_types_treatment = (
try(each.value.filter.credit_types_treatment.exclude_all, null) == true
? "EXCLUDE_ALL_CREDITS"
: (
try(each.value.filter.credit_types_treatment.include_specified, null) != null
? "INCLUDE_SPECIFIED_CREDITS"
: "INCLUDE_ALL_CREDITS"
)
)
labels = each.value.filter.label == null ? null : {
(each.value.filter.label.key) = each.value.filter.label.value
}
projects = each.value.filter.projects
resource_ancestors = each.value.filter.resource_ancestors
services = each.value.filter.services
subaccounts = each.value.filter.subaccounts
dynamic "custom_period" {
for_each = try(each.value.filter.period.custom, null) != null ? [""] : []
content {
start_date {
day = each.value.filter.period.custom.start_date.day
month = each.value.filter.period.custom.start_date.month
year = each.value.filter.period.custom.start_date.year
}
dynamic "end_date" {
for_each = try(each.value.filter.period.custom.end_date, null) != null ? [""] : []
content {
day = each.value.filter.period.custom.end_date.day
month = each.value.filter.period.custom.end_date.month
year = each.value.filter.period.custom.end_date.year
}
}
}
}
}
dynamic "threshold_rules" {
for_each = toset(each.value.threshold_rules)
iterator = rule
content {
threshold_percent = rule.value.percent
spend_basis = (
rule.value.forecasted_spend == true
? "FORECASTED_SPEND"
: "CURRENT_SPEND"
)
}
}
dynamic "all_updates_rule" {
for_each = each.value.update_rules
iterator = rule
content {
pubsub_topic = rule.value.pubsub_topic
schema_version = "1.0"
disable_default_iam_recipients = rule.value.disable_default_iam_recipients
monitoring_notification_channels = (
rule.value.monitoring_notification_channels == null
? null
: [
for v in rule.value.monitoring_notification_channels : try(
google_monitoring_notification_channel.default[v].id, v
)
]
)
}
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# tfdoc:file:description IAM bindings.
locals {
_group_iam_roles = distinct(flatten(values(var.group_iam)))
_group_iam = {
for r in local._group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
role => concat(
try(var.iam[role], []),
try(local._group_iam[role], [])
)
}
}
resource "google_billing_account_iam_binding" "authoritative" {
for_each = local.iam
billing_account_id = var.id
role = each.key
members = each.value
}
resource "google_billing_account_iam_binding" "bindings" {
for_each = var.iam_bindings
billing_account_id = var.id
role = each.value.role
members = each.value.members
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}
resource "google_billing_account_iam_member" "bindings" {
for_each = var.iam_bindings_additive
billing_account_id = var.id
role = each.value.role
member = each.value.member
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}

View File

@ -0,0 +1,92 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# tfdoc:file:description Log sinks and supporting resources.
locals {
sink_bindings = {
for type in ["bigquery", "pubsub", "logging", "storage"] :
type => {
for name, sink in var.logging_sinks :
name => sink
if sink.type == type
}
}
}
resource "google_logging_billing_account_sink" "sink" {
for_each = var.logging_sinks
name = each.key
description = coalesce(each.value.description, "${each.key} (Terraform-managed).")
billing_account = var.id
destination = "${each.value.type}.googleapis.com/${each.value.destination}"
filter = each.value.filter
disabled = each.value.disabled
dynamic "bigquery_options" {
for_each = each.value.type == "biquery" && each.value.bq_partitioned_table != false ? [""] : []
content {
use_partitioned_tables = each.value.bq_partitioned_table
}
}
dynamic "exclusions" {
for_each = each.value.exclusions
iterator = exclusion
content {
name = exclusion.key
filter = exclusion.value.filter
description = exclusion.value.description
disabled = exclusion.value.disabled
}
}
}
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
for_each = local.sink_bindings["storage"]
bucket = each.value.destination
role = "roles/storage.objectCreator"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
for_each = local.sink_bindings["bigquery"]
project = split("/", each.value.destination)[1]
dataset_id = split("/", each.value.destination)[3]
role = "roles/bigquery.dataEditor"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
for_each = local.sink_bindings["pubsub"]
project = split("/", each.value.destination)[1]
topic = split("/", each.value.destination)[3]
role = "roles/pubsub.publisher"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_project_iam_member" "bucket-sinks-binding" {
for_each = local.sink_bindings["logging"]
project = split("/", each.value.destination)[1]
role = "roles/logging.bucketWriter"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
condition {
title = "${each.key} bucket writer"
description = "Grants bucketWriter to ${google_logging_billing_account_sink.sink[each.key].writer_identity} used by log sink ${each.key} on billing account ${var.id}"
expression = "resource.name.endsWith('${each.value.destination}')"
}
}

View File

@ -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,12 +14,8 @@
* limitations under the License.
*/
output "budget" {
description = "Budget resource."
value = google_billing_budget.budget
}
output "id" {
description = "Fully qualified budget id."
value = google_billing_budget.budget.id
resource "google_billing_project_info" "default" {
for_each = toset(var.projects)
billing_account = var.id
project = each.key
}

View File

@ -0,0 +1,31 @@
/**
* 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.
*/
output "billing_budget_ids" {
description = "Billing budget ids."
value = {
for k, v in google_billing_budget.default :
k => v.id
}
}
output "monitoring_notification_channel_ids" {
description = "Monitoring notification channel ids."
value = {
for k, v in google_monitoring_notification_channel.default :
k => v.id
}
}

View File

@ -0,0 +1,208 @@
/**
* 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 "budget_notification_channels" {
description = "Notification channels used by budget alerts."
type = map(object({
project_id = string
type = string
description = optional(string)
display_name = optional(string)
enabled = optional(bool, true)
force_delete = optional(bool)
labels = optional(map(string))
sensitive_labels = optional(list(object({
auth_token = optional(string)
password = optional(string)
service_key = optional(string)
})))
user_labels = optional(map(string))
}))
nullable = false
default = {}
validation {
condition = alltrue([
for k, v in var.budget_notification_channels : contains([
"campfire", "email", "google_chat", "hipchat", "pagerduty",
"pubsub", "slack", "sms", "webhook_basicauth", "webhook_tokenauth"
], v.type)
])
error_message = "Invalid notification channel type."
}
}
variable "budgets" {
description = "Billing budgets. Notification channels are either keys in corresponding variable, or external ids."
type = map(object({
amount = object({
currency_code = optional(string)
nanos = optional(number)
units = optional(number)
use_last_period = optional(bool)
})
display_name = optional(string)
filter = optional(object({
credit_types_treatment = optional(object({
exclude_all = optional(bool)
include_specified = optional(list(string))
}))
label = optional(object({
key = string
value = string
}))
period = optional(object({
calendar = optional(string)
custom = optional(object({
start_date = object({
day = number
month = number
year = number
})
end_date = optional(object({
day = number
month = number
year = number
}))
}))
}))
projects = optional(list(string))
resource_ancestors = optional(list(string))
services = optional(list(string))
subaccounts = optional(list(string))
}))
threshold_rules = optional(list(object({
percent = number
forecasted_spend = optional(bool)
})), [])
update_rules = optional(map(object({
disable_default_iam_recipients = optional(bool)
monitoring_notification_channels = optional(list(string))
pubsub_topic = optional(string)
})), {})
}))
nullable = false
default = {}
validation {
condition = alltrue([
for k, v in var.budgets : v.amount != null && (
try(v.amount.use_last_period, null) == true ||
try(v.amount.units, null) != null
)
])
error_message = "Each budget needs to have amount units specified, or use last period."
}
validation {
condition = alltrue(flatten([
for k, v in var.budgets : [
for kk, vv in v.update_rules : [
vv.monitoring_notification_channels != null
||
vv.pubsub_topic != null
]
]
]))
error_message = "Budget notification rules need either a pubsub topic or monitoring channels defined."
}
}
variable "group_iam" {
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
type = map(list(string))
default = {}
nullable = false
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
nullable = false
}
variable "iam_bindings" {
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
type = map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "iam_bindings_additive" {
description = "Individual additive IAM bindings. Keys are arbitrary."
type = map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "id" {
description = "Billing account id."
type = string
}
variable "logging_sinks" {
description = "Logging sinks to create for the organization."
type = map(object({
destination = string
type = string
bq_partitioned_table = optional(bool)
description = optional(string)
disabled = optional(bool, false)
exclusions = optional(map(object({
filter = string
description = optional(string)
disabled = optional(bool)
})), {})
filter = optional(string)
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.logging_sinks :
contains(["bigquery", "logging", "pubsub", "storage"], v.type)
])
error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'."
}
validation {
condition = alltrue([
for k, v in var.logging_sinks :
v.bq_partitioned_table != true || v.type == "bigquery"
])
error_message = "Can only set bq_partitioned_table when type is `bigquery`."
}
}
variable "projects" {
description = "Projects associated with this billing account."
type = list(string)
nullable = false
default = []
}

View File

@ -25,5 +25,3 @@ terraform {
}
}
}

View File

@ -1,89 +0,0 @@
# Google Cloud Billing Budget Module
This module allows creating a Cloud Billing budget for a set of services and projects.
To create billing budgets you need one of the following IAM roles on the target billing account:
* Billing Account Administrator
* Billing Account Costs Manager
## Examples
### Simple email notification
Send a notification to an email when a set of projects reach $100 of spend.
```hcl
module "budget" {
source = "./fabric/modules/billing-budget"
billing_account = var.billing_account_id
name = "$100 budget"
amount = 100
thresholds = {
current = [0.5, 0.75, 1.0]
forecasted = [1.0]
}
projects = [
"projects/123456789000",
"projects/123456789111"
]
email_recipients = {
project_id = "my-project"
emails = ["user@example.com"]
}
}
# tftest modules=1 resources=2 inventory=email.yaml
```
### Pubsub notification
Send a notification to a PubSub topic the total spend of a billing account reaches the previous month's spend.
```hcl
module "budget" {
source = "./fabric/modules/billing-budget"
billing_account = var.billing_account_id
name = "previous period budget"
amount = 0
thresholds = {
current = [1.0]
forecasted = []
}
pubsub_topic = module.pubsub.id
}
module "pubsub" {
source = "./fabric/modules/pubsub"
project_id = var.project_id
name = "budget-topic"
}
# tftest modules=2 resources=2 inventory=pubsub.yaml
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | ✓ | |
| [name](variables.tf#L50) | Budget name. | <code>string</code> | ✓ | |
| [thresholds](variables.tf#L85) | Thresholds percentages at which alerts are sent. Must be a value between 0 and 1. | <code title="object&#40;&#123;&#10; current &#61; list&#40;number&#41;&#10; forecasted &#61; list&#40;number&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [amount](variables.tf#L17) | Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend. | <code>number</code> | | <code>0</code> |
| [credit_treatment](variables.tf#L28) | How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported. | <code>string</code> | | <code>&#34;INCLUDE_ALL_CREDITS&#34;</code> |
| [email_recipients](variables.tf#L41) | Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project. | <code title="object&#40;&#123;&#10; project_id &#61; string&#10; emails &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [notification_channels](variables.tf#L55) | Monitoring notification channels where to send updates. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [notify_default_recipients](variables.tf#L61) | Notify Billing Account Administrators and Billing Account Users IAM roles for the target account. | <code>bool</code> | | <code>false</code> |
| [projects](variables.tf#L67) | List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [pubsub_topic](variables.tf#L73) | The ID of the Cloud Pub/Sub topic where budget related messages will be published. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L79) | List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services. | <code>list&#40;string&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [budget](outputs.tf#L17) | Budget resource. | |
| [id](outputs.tf#L22) | Fully qualified budget id. | |
<!-- END TFDOC -->

View File

@ -1,95 +0,0 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
spend_basis = {
current = "CURRENT_SPEND"
forecasted = "FORECASTED_SPEND"
}
threshold_pairs = flatten([
for type, values in var.thresholds : [
for value in values : {
spend_basis = local.spend_basis[type]
threshold_percent = value
}
]
])
notification_channels = concat(
[for channel in google_monitoring_notification_channel.email_channels : channel.id],
coalesce(var.notification_channels, [])
)
}
resource "google_monitoring_notification_channel" "email_channels" {
for_each = toset(try(var.email_recipients.emails, []))
display_name = "${var.name} budget email notification (${each.value})"
type = "email"
project = var.email_recipients.project_id
labels = {
email_address = each.value
}
user_labels = {}
}
resource "google_billing_budget" "budget" {
billing_account = var.billing_account
display_name = var.name
budget_filter {
projects = var.projects
credit_types_treatment = var.credit_treatment
services = var.services
}
dynamic "amount" {
for_each = var.amount == 0 ? [1] : []
content {
last_period_amount = true
}
}
dynamic "amount" {
for_each = var.amount != 0 ? [1] : []
content {
dynamic "specified_amount" {
for_each = var.amount != 0 ? [1] : []
content {
units = var.amount
}
}
}
}
dynamic "threshold_rules" {
for_each = local.threshold_pairs
iterator = threshold
content {
threshold_percent = threshold.value.threshold_percent
spend_basis = threshold.value.spend_basis
}
}
all_updates_rule {
monitoring_notification_channels = local.notification_channels
pubsub_topic = var.pubsub_topic
# disable_default_iam_recipients can only be set if
# monitoring_notification_channels is nonempty
disable_default_iam_recipients = try(length(var.notification_channels), 0) > 0 && !var.notify_default_recipients
schema_version = "1.0"
}
}

View File

@ -1,95 +0,0 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "amount" {
description = "Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend."
type = number
default = 0
}
variable "billing_account" {
description = "Billing account id."
type = string
}
variable "credit_treatment" {
description = "How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported."
type = string
default = "INCLUDE_ALL_CREDITS"
validation {
condition = (
var.credit_treatment == "INCLUDE_ALL_CREDITS" ||
var.credit_treatment == "EXCLUDE_ALL_CREDITS"
)
error_message = "Argument credit_treatment must be INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS."
}
}
variable "email_recipients" {
description = "Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project."
type = object({
project_id = string
emails = list(string)
})
default = null
}
variable "name" {
description = "Budget name."
type = string
}
variable "notification_channels" {
description = "Monitoring notification channels where to send updates."
type = list(string)
default = null
}
variable "notify_default_recipients" {
description = "Notify Billing Account Administrators and Billing Account Users IAM roles for the target account."
type = bool
default = false
}
variable "projects" {
description = "List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account."
type = list(string)
default = null
}
variable "pubsub_topic" {
description = "The ID of the Cloud Pub/Sub topic where budget related messages will be published."
type = string
default = null
}
variable "services" {
description = "List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services."
type = list(string)
default = null
}
variable "thresholds" {
description = "Thresholds percentages at which alerts are sent. Must be a value between 0 and 1."
type = object({
current = list(number)
forecasted = list(number)
})
validation {
condition = length(var.thresholds.current) > 0 || length(var.thresholds.forecasted) > 0
error_message = "Must specify at least one budget threshold."
}
}

View File

@ -683,9 +683,9 @@ module "instance" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L235) | Instance name. | <code>string</code> | ✓ | |
| [network_interfaces](variables.tf#L240) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | <code title="list&#40;object&#40;&#123;&#10; nat &#61; optional&#40;bool, false&#41;&#10; network &#61; string&#10; subnetwork &#61; string&#10; addresses &#61; optional&#40;object&#40;&#123;&#10; internal &#61; optional&#40;string&#41;&#10; external &#61; optional&#40;string&#41;&#10; &#125;&#41;, null&#41;&#10; alias_ips &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; nic_type &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L277) | Project id. | <code>string</code> | ✓ | |
| [zone](variables.tf#L369) | Compute zone. | <code>string</code> | ✓ | |
| [network_interfaces](variables.tf#L240) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | <code title="list&#40;object&#40;&#123;&#10; network &#61; string&#10; subnetwork &#61; string&#10; alias_ips &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; nat &#61; optional&#40;bool, false&#41;&#10; nic_type &#61; optional&#40;string&#41;&#10; stack_type &#61; optional&#40;string&#41;&#10; addresses &#61; optional&#40;object&#40;&#123;&#10; internal &#61; optional&#40;string&#41;&#10; external &#61; optional&#40;string&#41;&#10; &#125;&#41;, null&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L278) | Project id. | <code>string</code> | ✓ | |
| [zone](variables.tf#L370) | Compute zone. | <code>string</code> | ✓ | |
| [attached_disk_defaults](variables.tf#L17) | Defaults for attached disks options. | <code title="object&#40;&#123;&#10; auto_delete &#61; optional&#40;bool, false&#41;&#10; mode &#61; string&#10; replica_zone &#61; string&#10; type &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; auto_delete &#61; true&#10; mode &#61; &#34;READ_WRITE&#34;&#10; replica_zone &#61; null&#10; type &#61; &#34;pd-balanced&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [attached_disks](variables.tf#L37) | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; device_name &#61; optional&#40;string&#41;&#10; size &#61; string&#10; snapshot_schedule &#61; optional&#40;string&#41;&#10; source &#61; optional&#40;string&#41;&#10; source_type &#61; optional&#40;string&#41;&#10; options &#61; optional&#40;&#10; object&#40;&#123;&#10; auto_delete &#61; optional&#40;bool, false&#41;&#10; mode &#61; optional&#40;string, &#34;READ_WRITE&#34;&#41;&#10; replica_zone &#61; optional&#40;string&#41;&#10; type &#61; optional&#40;string, &#34;pd-balanced&#34;&#41;&#10; &#125;&#41;,&#10; &#123;&#10; auto_delete &#61; true&#10; mode &#61; &#34;READ_WRITE&#34;&#10; replica_zone &#61; null&#10; type &#61; &#34;pd-balanced&#34;&#10; &#125;&#10; &#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [boot_disk](variables.tf#L83) | Boot disk properties. | <code title="object&#40;&#123;&#10; auto_delete &#61; optional&#40;bool, true&#41;&#10; snapshot_schedule &#61; optional&#40;string&#41;&#10; source &#61; optional&#40;string&#41;&#10; initialize_params &#61; optional&#40;object&#40;&#123;&#10; image &#61; optional&#40;string, &#34;projects&#47;debian-cloud&#47;global&#47;images&#47;family&#47;debian-11&#34;&#41;&#10; size &#61; optional&#40;number, 10&#41;&#10; type &#61; optional&#40;string, &#34;pd-balanced&#34;&#41;&#10; &#125;&#41;&#41;&#10; use_independent_disk &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; initialize_params &#61; &#123;&#125;&#10;&#125;">&#123;&#8230;&#125;</code> |
@ -703,13 +703,13 @@ module "instance" {
| [labels](variables.tf#L217) | Instance labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [metadata](variables.tf#L223) | Instance metadata. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [min_cpu_platform](variables.tf#L229) | Minimum CPU platform. | <code>string</code> | | <code>null</code> |
| [options](variables.tf#L255) | Instance options. | <code title="object&#40;&#123;&#10; allow_stopping_for_update &#61; optional&#40;bool, true&#41;&#10; deletion_protection &#61; optional&#40;bool, false&#41;&#10; spot &#61; optional&#40;bool, false&#41;&#10; termination_action &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; allow_stopping_for_update &#61; true&#10; deletion_protection &#61; false&#10; spot &#61; false&#10; termination_action &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [scratch_disks](variables.tf#L282) | Scratch disks configuration. | <code title="object&#40;&#123;&#10; count &#61; number&#10; interface &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; count &#61; 0&#10; interface &#61; &#34;NVME&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_account](variables.tf#L294) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | <code title="object&#40;&#123;&#10; auto_create &#61; optional&#40;bool, false&#41;&#10; email &#61; optional&#40;string&#41;&#10; scopes &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [shielded_config](variables.tf#L304) | Shielded VM configuration of the instances. | <code title="object&#40;&#123;&#10; enable_secure_boot &#61; bool&#10; enable_vtpm &#61; bool&#10; enable_integrity_monitoring &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [snapshot_schedules](variables.tf#L314) | Snapshot schedule resource policies that can be attached to disks. | <code title="map&#40;object&#40;&#123;&#10; schedule &#61; object&#40;&#123;&#10; daily &#61; optional&#40;object&#40;&#123;&#10; days_in_cycle &#61; number&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#10; hourly &#61; optional&#40;object&#40;&#123;&#10; hours_in_cycle &#61; number&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#10; weekly &#61; optional&#40;list&#40;object&#40;&#123;&#10; day &#61; string&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#10; description &#61; optional&#40;string&#41;&#10; retention_policy &#61; optional&#40;object&#40;&#123;&#10; max_retention_days &#61; number&#10; on_source_disk_delete_keep &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; snapshot_properties &#61; optional&#40;object&#40;&#123;&#10; chain_name &#61; optional&#40;string&#41;&#10; guest_flush &#61; optional&#40;bool&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; storage_locations &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L357) | Tag bindings for this instance, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [tags](variables.tf#L363) | Instance network tags for firewall rule targets. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [options](variables.tf#L256) | Instance options. | <code title="object&#40;&#123;&#10; allow_stopping_for_update &#61; optional&#40;bool, true&#41;&#10; deletion_protection &#61; optional&#40;bool, false&#41;&#10; spot &#61; optional&#40;bool, false&#41;&#10; termination_action &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; allow_stopping_for_update &#61; true&#10; deletion_protection &#61; false&#10; spot &#61; false&#10; termination_action &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [scratch_disks](variables.tf#L283) | Scratch disks configuration. | <code title="object&#40;&#123;&#10; count &#61; number&#10; interface &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; count &#61; 0&#10; interface &#61; &#34;NVME&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [service_account](variables.tf#L295) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | <code title="object&#40;&#123;&#10; auto_create &#61; optional&#40;bool, false&#41;&#10; email &#61; optional&#40;string&#41;&#10; scopes &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [shielded_config](variables.tf#L305) | Shielded VM configuration of the instances. | <code title="object&#40;&#123;&#10; enable_secure_boot &#61; bool&#10; enable_vtpm &#61; bool&#10; enable_integrity_monitoring &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [snapshot_schedules](variables.tf#L315) | Snapshot schedule resource policies that can be attached to disks. | <code title="map&#40;object&#40;&#123;&#10; schedule &#61; object&#40;&#123;&#10; daily &#61; optional&#40;object&#40;&#123;&#10; days_in_cycle &#61; number&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#10; hourly &#61; optional&#40;object&#40;&#123;&#10; hours_in_cycle &#61; number&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#10; weekly &#61; optional&#40;list&#40;object&#40;&#123;&#10; day &#61; string&#10; start_time &#61; string&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#10; description &#61; optional&#40;string&#41;&#10; retention_policy &#61; optional&#40;object&#40;&#123;&#10; max_retention_days &#61; number&#10; on_source_disk_delete_keep &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; snapshot_properties &#61; optional&#40;object&#40;&#123;&#10; chain_name &#61; optional&#40;string&#41;&#10; guest_flush &#61; optional&#40;bool&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; storage_locations &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tag_bindings](variables.tf#L358) | Tag bindings for this instance, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [tags](variables.tf#L364) | Instance network tags for firewall rule targets. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
## Outputs

View File

@ -237,6 +237,8 @@ resource "google_compute_instance" "default" {
network = config.value.network
subnetwork = config.value.subnetwork
network_ip = try(config.value.addresses.internal, null)
nic_type = config.value.nic_type
stack_type = config.value.stack_type
dynamic "access_config" {
for_each = config.value.nat ? [""] : []
content {
@ -251,7 +253,6 @@ resource "google_compute_instance" "default" {
ip_cidr_range = config_alias.value
}
}
nic_type = config.value.nic_type
}
}
@ -374,6 +375,8 @@ resource "google_compute_instance_template" "default" {
network = config.value.network
subnetwork = config.value.subnetwork
network_ip = try(config.value.addresses.internal, null)
nic_type = config.value.nic_type
stack_type = config.value.stack_type
dynamic "access_config" {
for_each = config.value.nat ? [""] : []
content {
@ -388,7 +391,6 @@ resource "google_compute_instance_template" "default" {
ip_cidr_range = config_alias.value
}
}
nic_type = config.value.nic_type
}
}

View File

@ -240,15 +240,16 @@ variable "name" {
variable "network_interfaces" {
description = "Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed."
type = list(object({
nat = optional(bool, false)
network = string
subnetwork = string
alias_ips = optional(map(string), {})
nat = optional(bool, false)
nic_type = optional(string)
stack_type = optional(string)
addresses = optional(object({
internal = optional(string)
external = optional(string)
}), null)
alias_ips = optional(map(string), {})
nic_type = optional(string)
}))
}

View File

@ -272,7 +272,7 @@ module "folder" {
| name | description | resources |
|---|---|---|
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_folder_iam_binding</code> · <code>google_folder_iam_member</code> |
| [iam.tf](./iam.tf) | IAM bindings. | <code>google_folder_iam_binding</code> · <code>google_folder_iam_member</code> |
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_folder_iam_audit_config</code> · <code>google_logging_folder_exclusion</code> · <code>google_logging_folder_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_firewall_policy_association</code> · <code>google_essential_contacts_contact</code> · <code>google_folder</code> |
| [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | <code>google_org_policy_policy</code> |
@ -295,7 +295,7 @@ module "folder" {
| [id](variables.tf#L83) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L89) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the folder. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L141) | Folder name. | <code>string</code> | | <code>null</code> |
| [org_policies](variables.tf#L147) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L174) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
# tfdoc:file:description IAM bindings, roles and audit logging resources.
# tfdoc:file:description IAM bindings.
locals {
_group_iam_roles = distinct(flatten(values(var.group_iam)))

View File

@ -109,7 +109,7 @@ variable "logging_exclusions" {
}
variable "logging_sinks" {
description = "Logging sinks to create for the organization."
description = "Logging sinks to create for the folder."
type = map(object({
bq_partitioned_table = optional(bool)
description = optional(string)

View File

@ -178,29 +178,30 @@ module "bucket" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L158) | Bucket name suffix. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L213) | Bucket project id. | <code>string</code> | ✓ | |
| [cors](variables.tf#L17) | CORS configuration for the bucket. Defaults to null. | <code title="object&#40;&#123;&#10; origin &#61; optional&#40;list&#40;string&#41;&#41;&#10; method &#61; optional&#40;list&#40;string&#41;&#41;&#10; response_header &#61; optional&#40;list&#40;string&#41;&#41;&#10; max_age_seconds &#61; optional&#40;number&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [custom_placement_config](variables.tf#L28) | The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI_REGIONAL, the parameters are empty. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [default_event_based_hold](variables.tf#L34) | Enable event based hold to new objects added to specific bucket, defaults to false. | <code>bool</code> | | <code>null</code> |
| [encryption_key](variables.tf#L40) | KMS key that will be used for encryption. | <code>string</code> | | <code>null</code> |
| [force_destroy](variables.tf#L46) | Optional map to set force destroy keyed by name, defaults to false. | <code>bool</code> | | <code>false</code> |
| [iam](variables.tf#L52) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L58) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L73) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L88) | Labels to be attached to all buckets. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lifecycle_rules](variables.tf#L94) | Bucket lifecycle rule. | <code title="map&#40;object&#40;&#123;&#10; action &#61; object&#40;&#123;&#10; type &#61; string&#10; storage_class &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10; condition &#61; object&#40;&#123;&#10; age &#61; optional&#40;number&#41;&#10; created_before &#61; optional&#40;string&#41;&#10; custom_time_before &#61; optional&#40;string&#41;&#10; days_since_custom_time &#61; optional&#40;number&#41;&#10; days_since_noncurrent_time &#61; optional&#40;number&#41;&#10; matches_prefix &#61; optional&#40;list&#40;string&#41;&#41;&#10; matches_storage_class &#61; optional&#40;list&#40;string&#41;&#41; &#35; STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE, DURABLE_REDUCED_AVAILABILITY&#10; matches_suffix &#61; optional&#40;list&#40;string&#41;&#41;&#10; noncurrent_time_before &#61; optional&#40;string&#41;&#10; num_newer_versions &#61; optional&#40;number&#41;&#10; with_state &#61; optional&#40;string&#41; &#35; &#34;LIVE&#34;, &#34;ARCHIVED&#34;, &#34;ANY&#34;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L143) | Bucket location. | <code>string</code> | | <code>&#34;EU&#34;</code> |
| [logging_config](variables.tf#L149) | Bucket logging configuration. | <code title="object&#40;&#123;&#10; log_bucket &#61; string&#10; log_object_prefix &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [notification_config](variables.tf#L163) | GCS Notification configuration. | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; payload_format &#61; string&#10; topic_name &#61; string&#10; sa_email &#61; string&#10; event_types &#61; optional&#40;list&#40;string&#41;&#41;&#10; custom_attributes &#61; optional&#40;map&#40;string&#41;&#41;&#10; object_name_prefix &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [objects_to_upload](variables.tf#L177) | Objects to be uploaded to bucket. | <code title="map&#40;object&#40;&#123;&#10; name &#61; string&#10; metadata &#61; optional&#40;map&#40;string&#41;&#41;&#10; content &#61; optional&#40;string&#41;&#10; source &#61; optional&#40;string&#41;&#10; cache_control &#61; optional&#40;string&#41;&#10; content_disposition &#61; optional&#40;string&#41;&#10; content_encoding &#61; optional&#40;string&#41;&#10; content_language &#61; optional&#40;string&#41;&#10; content_type &#61; optional&#40;string&#41;&#10; event_based_hold &#61; optional&#40;bool&#41;&#10; temporary_hold &#61; optional&#40;bool&#41;&#10; detect_md5hash &#61; optional&#40;string&#41;&#10; storage_class &#61; optional&#40;string&#41;&#10; kms_key_name &#61; optional&#40;string&#41;&#10; customer_encryption &#61; optional&#40;object&#40;&#123;&#10; encryption_algorithm &#61; optional&#40;string&#41;&#10; encryption_key &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L203) | Optional prefix used to generate the bucket name. | <code>string</code> | | <code>null</code> |
| [requester_pays](variables.tf#L218) | Enables Requester Pays on a storage bucket. | <code>bool</code> | | <code>null</code> |
| [retention_policy](variables.tf#L224) | Bucket retention policy. | <code title="object&#40;&#123;&#10; retention_period &#61; number&#10; is_locked &#61; optional&#40;bool&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [storage_class](variables.tf#L233) | Bucket storage class. | <code>string</code> | | <code>&#34;MULTI_REGIONAL&#34;</code> |
| [uniform_bucket_level_access](variables.tf#L243) | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | <code>bool</code> | | <code>true</code> |
| [versioning](variables.tf#L249) | Enable versioning, defaults to false. | <code>bool</code> | | <code>false</code> |
| [website](variables.tf#L255) | Bucket website. | <code title="object&#40;&#123;&#10; main_page_suffix &#61; optional&#40;string&#41;&#10; not_found_page &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [name](variables.tf#L164) | Bucket name suffix. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L219) | Bucket project id. | <code>string</code> | ✓ | |
| [autoclass](variables.tf#L17) | Enable autoclass to automatically transition objects to appropriate storage classes based on their access pattern. If set to true, storage_class must be set to STANDARD. Defaults to false. | <code>bool</code> | | <code>false</code> |
| [cors](variables.tf#L23) | CORS configuration for the bucket. Defaults to null. | <code title="object&#40;&#123;&#10; origin &#61; optional&#40;list&#40;string&#41;&#41;&#10; method &#61; optional&#40;list&#40;string&#41;&#41;&#10; response_header &#61; optional&#40;list&#40;string&#41;&#41;&#10; max_age_seconds &#61; optional&#40;number&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [custom_placement_config](variables.tf#L34) | The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI_REGIONAL, the parameters are empty. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [default_event_based_hold](variables.tf#L40) | Enable event based hold to new objects added to specific bucket, defaults to false. | <code>bool</code> | | <code>null</code> |
| [encryption_key](variables.tf#L46) | KMS key that will be used for encryption. | <code>string</code> | | <code>null</code> |
| [force_destroy](variables.tf#L52) | Optional map to set force destroy keyed by name, defaults to false. | <code>bool</code> | | <code>false</code> |
| [iam](variables.tf#L58) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L64) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L79) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L94) | Labels to be attached to all buckets. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [lifecycle_rules](variables.tf#L100) | Bucket lifecycle rule. | <code title="map&#40;object&#40;&#123;&#10; action &#61; object&#40;&#123;&#10; type &#61; string&#10; storage_class &#61; optional&#40;string&#41;&#10; &#125;&#41;&#10; condition &#61; object&#40;&#123;&#10; age &#61; optional&#40;number&#41;&#10; created_before &#61; optional&#40;string&#41;&#10; custom_time_before &#61; optional&#40;string&#41;&#10; days_since_custom_time &#61; optional&#40;number&#41;&#10; days_since_noncurrent_time &#61; optional&#40;number&#41;&#10; matches_prefix &#61; optional&#40;list&#40;string&#41;&#41;&#10; matches_storage_class &#61; optional&#40;list&#40;string&#41;&#41; &#35; STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE, DURABLE_REDUCED_AVAILABILITY&#10; matches_suffix &#61; optional&#40;list&#40;string&#41;&#41;&#10; noncurrent_time_before &#61; optional&#40;string&#41;&#10; num_newer_versions &#61; optional&#40;number&#41;&#10; with_state &#61; optional&#40;string&#41; &#35; &#34;LIVE&#34;, &#34;ARCHIVED&#34;, &#34;ANY&#34;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L149) | Bucket location. | <code>string</code> | | <code>&#34;EU&#34;</code> |
| [logging_config](variables.tf#L155) | Bucket logging configuration. | <code title="object&#40;&#123;&#10; log_bucket &#61; string&#10; log_object_prefix &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [notification_config](variables.tf#L169) | GCS Notification configuration. | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; payload_format &#61; string&#10; topic_name &#61; string&#10; sa_email &#61; string&#10; event_types &#61; optional&#40;list&#40;string&#41;&#41;&#10; custom_attributes &#61; optional&#40;map&#40;string&#41;&#41;&#10; object_name_prefix &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [objects_to_upload](variables.tf#L183) | Objects to be uploaded to bucket. | <code title="map&#40;object&#40;&#123;&#10; name &#61; string&#10; metadata &#61; optional&#40;map&#40;string&#41;&#41;&#10; content &#61; optional&#40;string&#41;&#10; source &#61; optional&#40;string&#41;&#10; cache_control &#61; optional&#40;string&#41;&#10; content_disposition &#61; optional&#40;string&#41;&#10; content_encoding &#61; optional&#40;string&#41;&#10; content_language &#61; optional&#40;string&#41;&#10; content_type &#61; optional&#40;string&#41;&#10; event_based_hold &#61; optional&#40;bool&#41;&#10; temporary_hold &#61; optional&#40;bool&#41;&#10; detect_md5hash &#61; optional&#40;string&#41;&#10; storage_class &#61; optional&#40;string&#41;&#10; kms_key_name &#61; optional&#40;string&#41;&#10; customer_encryption &#61; optional&#40;object&#40;&#123;&#10; encryption_algorithm &#61; optional&#40;string&#41;&#10; encryption_key &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L209) | Optional prefix used to generate the bucket name. | <code>string</code> | | <code>null</code> |
| [requester_pays](variables.tf#L224) | Enables Requester Pays on a storage bucket. | <code>bool</code> | | <code>null</code> |
| [retention_policy](variables.tf#L230) | Bucket retention policy. | <code title="object&#40;&#123;&#10; retention_period &#61; number&#10; is_locked &#61; optional&#40;bool&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [storage_class](variables.tf#L239) | Bucket storage class. | <code>string</code> | | <code>&#34;MULTI_REGIONAL&#34;</code> |
| [uniform_bucket_level_access](variables.tf#L249) | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | <code>bool</code> | | <code>true</code> |
| [versioning](variables.tf#L255) | Enable versioning, defaults to false. | <code>bool</code> | | <code>false</code> |
| [website](variables.tf#L261) | Bucket website. | <code title="object&#40;&#123;&#10; main_page_suffix &#61; optional&#40;string&#41;&#10; not_found_page &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs

View File

@ -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.
@ -33,6 +33,13 @@ resource "google_storage_bucket" "bucket" {
enabled = var.versioning
}
dynamic "autoclass" {
for_each = var.autoclass == null ? [] : [""]
content {
enabled = var.autoclass
}
}
dynamic "website" {
for_each = var.website == null ? [] : [""]

View File

@ -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,12 @@
* limitations under the License.
*/
variable "autoclass" {
description = "Enable autoclass to automatically transition objects to appropriate storage classes based on their access pattern. If set to true, storage_class must be set to STANDARD. Defaults to false."
type = bool
default = false
}
variable "cors" {
description = "CORS configuration for the bucket. Defaults to null."
type = object({

View File

@ -571,12 +571,12 @@ module "vpc" {
| [project_id](outputs.tf#L58) | Project ID containing the network. Use this when you need to create resources *after* the VPC is fully set up (e.g. subnets created, shared VPC service projects attached, Private Service Networking configured). | |
| [self_link](outputs.tf#L71) | Network self link. | |
| [subnet_ids](outputs.tf#L83) | Map of subnet IDs keyed by name. | |
| [subnet_ips](outputs.tf#L88) | Map of subnet address ranges keyed by name. | |
| [subnet_ipv6_external_prefixes](outputs.tf#L95) | Map of subnet external IPv6 prefixes keyed by name. | |
| [subnet_regions](outputs.tf#L103) | Map of subnet regions keyed by name. | |
| [subnet_secondary_ranges](outputs.tf#L110) | Map of subnet secondary ranges keyed by name. | |
| [subnet_self_links](outputs.tf#L121) | Map of subnet self links keyed by name. | |
| [subnets](outputs.tf#L126) | Subnet resources. | |
| [subnets_proxy_only](outputs.tf#L131) | L7 ILB or L7 Regional LB subnet resources. | |
| [subnets_psc](outputs.tf#L136) | Private Service Connect subnet resources. | |
| [subnet_ips](outputs.tf#L92) | Map of subnet address ranges keyed by name. | |
| [subnet_ipv6_external_prefixes](outputs.tf#L99) | Map of subnet external IPv6 prefixes keyed by name. | |
| [subnet_regions](outputs.tf#L107) | Map of subnet regions keyed by name. | |
| [subnet_secondary_ranges](outputs.tf#L114) | Map of subnet secondary ranges keyed by name. | |
| [subnet_self_links](outputs.tf#L125) | Map of subnet self links keyed by name. | |
| [subnets](outputs.tf#L134) | Subnet resources. | |
| [subnets_proxy_only](outputs.tf#L143) | L7 ILB or L7 Regional LB subnet resources. | |
| [subnets_psc](outputs.tf#L148) | Private Service Connect subnet resources. | |
<!-- END TFDOC -->

View File

@ -83,6 +83,10 @@ output "self_link" {
output "subnet_ids" {
description = "Map of subnet IDs keyed by name."
value = { for k, v in google_compute_subnetwork.subnetwork : k => v.id }
depends_on = [
# allows correct destruction of internal application load balancers
google_compute_subnetwork.proxy_only
]
}
output "subnet_ips" {
@ -121,11 +125,19 @@ output "subnet_secondary_ranges" {
output "subnet_self_links" {
description = "Map of subnet self links keyed by name."
value = { for k, v in google_compute_subnetwork.subnetwork : k => v.self_link }
depends_on = [
# allows correct destruction of internal application load balancers
google_compute_subnetwork.proxy_only
]
}
output "subnets" {
description = "Subnet resources."
value = { for k, v in google_compute_subnetwork.subnetwork : k => v }
depends_on = [
# allows correct destruction of internal application load balancers
google_compute_subnetwork.proxy_only
]
}
output "subnets_proxy_only" {
@ -136,4 +148,4 @@ output "subnets_proxy_only" {
output "subnets_psc" {
description = "Private Service Connect subnet resources."
value = { for k, v in google_compute_subnetwork.psc : k => v }
}
}

View File

@ -18,7 +18,7 @@ counts:
google_logging_organization_sink: 2
google_organization_iam_binding: 20
google_organization_iam_custom_role: 3
google_organization_iam_member: 17
google_organization_iam_member: 13
google_project: 3
google_project_iam_binding: 9
google_project_iam_member: 3

View File

@ -0,0 +1,60 @@
# 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.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule:
- disable_default_iam_recipients: true
pubsub_topic: null
schema_version: '1.0'
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
module.billing-account.google_monitoring_notification_channel.default["billing-default"]:
description: null
display_name: Budget email notification billing-default.
enabled: true
force_delete: false
labels:
email_address: gcp-billing-admins@example.com
project: tf-playground-simple
sensitive_labels: []
timeouts: null
type: email
user_labels: null
counts:
google_billing_budget: 1
google_monitoring_notification_channel: 1
modules: 1
resources: 2
outputs: {}

View File

@ -0,0 +1,56 @@
# 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.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule:
- disable_default_iam_recipients: false
monitoring_notification_channels: null
pubsub_topic: projects/my-prj/topics/budget-default
schema_version: '1.0'
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
module.pubsub-billing-topic.google_pubsub_topic.default:
kms_key_name: null
labels: null
message_retention_duration: null
name: budget-default
project: my-prj
timeouts: null
counts:
google_billing_budget: 1
google_pubsub_topic: 1
modules: 2
resources: 2
outputs: {}

View File

@ -0,0 +1,44 @@
# 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.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule: []
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
counts:
google_billing_budget: 1
modules: 1
resources: 1
outputs: {}

View File

@ -0,0 +1,45 @@
# 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.billing-account.google_billing_account_iam_binding.authoritative["roles/billing.admin"]:
billing_account_id: 012345-ABCDEF-012345
condition: []
members:
- group:billing-admins@example.org
- serviceAccount:foo@myprj.iam.gserviceaccount.com
role: roles/billing.admin
module.billing-account.google_billing_account_iam_binding.bindings["conditional-admin"]:
billing_account_id: 012345-ABCDEF-012345
condition:
- description: null
expression: resource.matchTag('123456/environment', 'development')
title: pf-dev-conditional-billing-admin
members:
- serviceAccount:pf-dev@myprj.iam.gserviceaccount.com
role: roles/billing.admin
module.billing-account.google_billing_account_iam_member.bindings["sa-net-iac-user"]:
billing_account_id: 012345-ABCDEF-012345
condition: []
member: serviceAccount:net-iac-0@myprj.iam.gserviceaccount.com
role: roles/billing.user
counts:
google_billing_account_iam_binding: 2
google_billing_account_iam_member: 1
modules: 1
resources: 3
outputs: {}

View File

@ -0,0 +1,43 @@
# 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.billing-account.google_logging_billing_account_sink.sink["all"]:
billing_account: 012345-ABCDEF-012345
description: all (Terraform-managed).
disabled: false
exclusions: []
filter: null
name: all
module.billing-account.google_project_iam_member.bucket-sinks-binding["all"]:
condition:
- title: all bucket writer
role: roles/logging.bucketWriter
module.log-bucket-all.google_logging_project_bucket_config.bucket[0]:
bucket_id: billing-account-all
cmek_settings: []
enable_analytics: false
location: global
locked: null
project: myprj
retention_days: 30
counts:
google_logging_billing_account_sink: 1
google_logging_project_bucket_config: 1
google_project_iam_member: 1
modules: 2
resources: 3
outputs: {}

View File

@ -33,6 +33,8 @@ values:
uniform_bucket_level_access: true
versioning:
- enabled: false
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_binding.authoritative["roles/storage.admin"]:
bucket: my-bucket
condition: []

View File

@ -33,6 +33,8 @@ values:
uniform_bucket_level_access: true
versioning:
- enabled: false
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_member.bindings["storage-admin-with-delegated_roles"]:
bucket: my-bucket
condition:

View File

@ -33,6 +33,8 @@ values:
uniform_bucket_level_access: true
versioning:
- enabled: false
autoclass:
- enabled: false
module.bucket.google_storage_bucket_iam_binding.bindings["storage-admin-with-delegated_roles"]:
bucket: my-bucket
condition:

View File

@ -34,6 +34,8 @@ values:
uniform_bucket_level_access: true
versioning:
- enabled: true
autoclass:
- enabled: false
counts:
google_storage_bucket: 1