Factories refactor (#1843)

* factories refactor doc

* Adds file schema and filesystem organization

* Update 20231106-factories.md

* move factories out of blueprints and create new factories  README

* align factory in billing-account module

* align factory in dataplex-datascan module

* align factory in billing-account module

* align factory in net-firewall-policy module

* align factory in dns-response-policy module

* align factory in net-vpc-firewall module

* align factory in net-vpc module

* align factory variable names in FAST

* remove decentralized firewall blueprint

* bump terraform version

* bump module versions

* update top-level READMEs

* move project factory to modules

* fix variable names and tests

* tfdoc

* remove changelog link

* add project factory to top-level README

* fix cludrun eventarc diff

* fix README

* fix cludrun eventarc diff

---------

Co-authored-by: Simone Ruffilli <sruffilli@google.com>
This commit is contained in:
Ludovico Magnocavallo
2024-02-26 11:16:52 +01:00
committed by GitHub
parent 8e86f0e108
commit 6941313c7d
188 changed files with 917 additions and 2292 deletions

View File

@@ -0,0 +1,89 @@
# AlloyDB cluster and instance with read replicas
This module manages the creation of AlloyDB cluster and configuration with/without automated backup policy, Primary node instance and Read Node Pools.
## Simple example
This example shows how to create Alloydb cluster and instance with multiple read pools in GCP project.
```hcl
module "alloydb" {
source = "./fabric/modules/alloydb-instance"
project_id = "myproject"
cluster_id = "alloydb-cluster-all"
location = "europe-west2"
labels = {}
display_name = ""
initial_user = {
user = "alloydb-cluster-full",
password = "alloydb-cluster-password"
}
network_self_link = "projects/myproject/global/networks/default"
automated_backup_policy = null
primary_instance_config = {
instance_id = "primary-instance-1",
instance_type = "PRIMARY",
machine_cpu_count = 2,
database_flags = {},
display_name = "alloydb-primary-instance"
}
read_pool_instance = [
{
instance_id = "read-instance-1",
display_name = "read-instance-1",
instance_type = "READ_POOL",
node_count = 1,
database_flags = {},
machine_cpu_count = 1
},
{
instance_id = "read-instance-2",
display_name = "read-instance-2",
instance_type = "READ_POOL",
node_count = 1,
database_flags = {},
machine_cpu_count = 1
}
]
}
# tftest skip
```
## TODO
- [ ] Add IAM support
- [ ] support password in output
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [cluster_id](variables.tf#L35) | The ID of the alloydb cluster. | <code>string</code> | ✓ | |
| [network_self_link](variables.tf#L83) | Network ID where the AlloyDb cluster will be deployed. | <code>string</code> | ✓ | |
| [primary_instance_config](variables.tf#L88) | Primary cluster configuration that supports read and write operations. | <code title="object&#40;&#123;&#10; instance_id &#61; string,&#10; display_name &#61; optional&#40;string&#41;,&#10; database_flags &#61; optional&#40;map&#40;string&#41;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; annotations &#61; optional&#40;map&#40;string&#41;&#41;&#10; gce_zone &#61; optional&#40;string&#41;&#10; availability_type &#61; optional&#40;string&#41;&#10; machine_cpu_count &#61; optional&#40;number, 2&#41;,&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [project_id](variables.tf#L110) | The ID of the project in which to provision resources. | <code>string</code> | ✓ | |
| [automated_backup_policy](variables.tf#L17) | The automated backup policy for this cluster. | <code title="object&#40;&#123;&#10; location &#61; optional&#40;string&#41;&#10; backup_window &#61; optional&#40;string&#41;&#10; enabled &#61; optional&#40;bool&#41;&#10; weekly_schedule &#61; optional&#40;object&#40;&#123;&#10; days_of_week &#61; optional&#40;list&#40;string&#41;&#41;&#10; start_times &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;,&#10; quantity_based_retention_count &#61; optional&#40;number&#41;&#10; time_based_retention_count &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; backup_encryption_key_name &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [display_name](variables.tf#L44) | Human readable display name for the Alloy DB Cluster. | <code>string</code> | | <code>null</code> |
| [encryption_key_name](variables.tf#L50) | The fully-qualified resource name of the KMS key for cluster encryption. | <code>string</code> | | <code>null</code> |
| [initial_user](variables.tf#L56) | Alloy DB Cluster Initial User Credentials. | <code title="object&#40;&#123;&#10; user &#61; optional&#40;string&#41;,&#10; password &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [labels](variables.tf#L65) | User-defined labels for the alloydb cluster. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [location](variables.tf#L71) | Location where AlloyDb cluster will be deployed. | <code>string</code> | | <code>&#34;europe-west2&#34;</code> |
| [network_name](variables.tf#L77) | The network name of the project in which to provision resources. | <code>string</code> | | <code>&#34;multiple-readpool&#34;</code> |
| [read_pool_instance](variables.tf#L115) | List of Read Pool Instances to be created. | <code title="list&#40;object&#40;&#123;&#10; instance_id &#61; string&#10; display_name &#61; string&#10; node_count &#61; optional&#40;number, 1&#41;&#10; database_flags &#61; optional&#40;map&#40;string&#41;&#41;&#10; availability_type &#61; optional&#40;string&#41;&#10; gce_zone &#61; optional&#40;string&#41;&#10; machine_cpu_count &#61; optional&#40;number, 2&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [cluster](outputs.tf#L17) | Cluster created. | ✓ |
| [cluster_id](outputs.tf#L23) | ID of the Alloy DB Cluster created. | |
| [primary_instance](outputs.tf#L28) | Primary instance created. | |
| [primary_instance_id](outputs.tf#L33) | ID of the primary instance created. | |
| [read_pool_instance_ids](outputs.tf#L38) | IDs of the read instances created. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,160 @@
/**
* 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.
*/
locals {
quantity_based_retention_count = (
var.automated_backup_policy != null ? (var.automated_backup_policy.quantity_based_retention_count != null ? [var.automated_backup_policy.quantity_based_retention_count] : []) : []
)
read_pool_instance = (
var.read_pool_instance != null ?
{ for read_pool_instances in var.read_pool_instance : read_pool_instances.instance_id => read_pool_instances } : {}
)
time_based_retention_count = (
var.automated_backup_policy != null ? (var.automated_backup_policy.time_based_retention_count != null ? [var.automated_backup_policy.time_based_retention_count] : []) : []
)
}
resource "google_alloydb_cluster" "default" {
cluster_id = var.cluster_id
location = var.location
network = var.network_self_link
display_name = var.display_name
project = var.project_id
labels = var.labels
dynamic "automated_backup_policy" {
for_each = var.automated_backup_policy == null ? [] : [""]
content {
location = var.automated_backup_policy.location
backup_window = var.automated_backup_policy.backup_window
enabled = var.automated_backup_policy.enabled
labels = var.automated_backup_policy.labels
weekly_schedule {
days_of_week = automated_backup_policy.value.weekly_schedule.days_of_week
dynamic "start_times" {
for_each = { for i, time in automated_backup_policy.value.weekly_schedule.start_times : i => {
hours = tonumber(split(":", time)[0])
minutes = tonumber(split(":", time)[1])
seconds = tonumber(split(":", time)[2])
nanos = tonumber(split(":", time)[3])
}
}
content {
hours = start_times.value.hours
minutes = start_times.value.minutes
seconds = start_times.value.seconds
nanos = start_times.value.nanos
}
}
}
dynamic "quantity_based_retention" {
for_each = local.quantity_based_retention_count
content {
count = quantity_based_retention.value
}
}
dynamic "time_based_retention" {
for_each = local.time_based_retention_count
content {
retention_period = time_based_retention.value
}
}
dynamic "encryption_config" {
for_each = automated_backup_policy.value.backup_encryption_key_name == null ? [] : ["encryption_config"]
content {
kms_key_name = automated_backup_policy.value.backup_encryption_key_name
}
}
}
}
dynamic "initial_user" {
for_each = var.initial_user == null ? [] : ["initial_user"]
content {
user = var.initial_user.user
password = var.initial_user.password
}
}
dynamic "encryption_config" {
for_each = var.encryption_key_name == null ? [] : ["encryption_config"]
content {
kms_key_name = var.encryption_key_name
}
}
}
resource "google_alloydb_instance" "primary" {
cluster = google_alloydb_cluster.default.name
instance_id = var.primary_instance_config.instance_id
instance_type = "PRIMARY"
display_name = var.primary_instance_config.display_name
database_flags = var.primary_instance_config.database_flags
labels = var.primary_instance_config.labels
annotations = var.primary_instance_config.annotations
gce_zone = var.primary_instance_config.availability_type == "ZONAL" ? var.primary_instance_config.gce_zone : null
availability_type = var.primary_instance_config.availability_type
machine_config {
cpu_count = var.primary_instance_config.machine_cpu_count
}
}
resource "google_alloydb_instance" "read_pool" {
for_each = local.read_pool_instance
cluster = google_alloydb_cluster.default.name
instance_id = each.key
instance_type = "READ_POOL"
availability_type = each.value.availability_type
gce_zone = each.value.availability_type == "ZONAL" ? each.value.availability_type.gce_zone : null
read_pool_config {
node_count = each.value.node_count
}
database_flags = each.value.database_flags
machine_config {
cpu_count = each.value.machine_cpu_count
}
depends_on = [google_alloydb_instance.primary, google_compute_network.default, google_compute_global_address.private_ip_alloc, google_service_networking_connection.vpc_connection]
}
resource "google_compute_network" "default" {
name = var.network_name
}
resource "google_compute_global_address" "private_ip_alloc" {
name = "adb-all"
address_type = "INTERNAL"
purpose = "VPC_PEERING"
prefix_length = 16
network = google_compute_network.default.id
}
resource "google_service_networking_connection" "vpc_connection" {
network = google_compute_network.default.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name]
}

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.
*/
output "cluster" {
description = "Cluster created."
value = resource.google_alloydb_cluster.default.display_name
sensitive = true
}
output "cluster_id" {
description = "ID of the Alloy DB Cluster created."
value = google_alloydb_cluster.default.cluster_id
}
output "primary_instance" {
description = "Primary instance created."
value = resource.google_alloydb_instance.primary.display_name
}
output "primary_instance_id" {
description = "ID of the primary instance created."
value = google_alloydb_instance.primary.instance_id
}
output "read_pool_instance_ids" {
description = "IDs of the read instances created."
value = [
for rd, details in google_alloydb_instance.read_pool : details.instance_id
]
}

View File

@@ -0,0 +1,127 @@
/**
* 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 "automated_backup_policy" {
description = "The automated backup policy for this cluster."
type = object({
location = optional(string)
backup_window = optional(string)
enabled = optional(bool)
weekly_schedule = optional(object({
days_of_week = optional(list(string))
start_times = list(string)
})),
quantity_based_retention_count = optional(number)
time_based_retention_count = optional(string)
labels = optional(map(string))
backup_encryption_key_name = optional(string)
})
default = null
}
variable "cluster_id" {
description = "The ID of the alloydb cluster."
type = string
validation {
condition = can(regex("^[a-z0-9-]+$", var.cluster_id))
error_message = "ERROR: Cluster ID must contain only Letters(lowercase), number, and hyphen."
}
}
variable "display_name" {
description = "Human readable display name for the Alloy DB Cluster."
type = string
default = null
}
variable "encryption_key_name" {
description = "The fully-qualified resource name of the KMS key for cluster encryption."
type = string
default = null
}
variable "initial_user" {
description = "Alloy DB Cluster Initial User Credentials."
type = object({
user = optional(string),
password = string
})
default = null
}
variable "labels" {
description = "User-defined labels for the alloydb cluster."
type = map(string)
default = {}
}
variable "location" {
description = "Location where AlloyDb cluster will be deployed."
type = string
default = "europe-west2"
}
variable "network_name" {
description = "The network name of the project in which to provision resources."
type = string
default = "multiple-readpool"
}
variable "network_self_link" {
description = "Network ID where the AlloyDb cluster will be deployed."
type = string
}
variable "primary_instance_config" {
description = "Primary cluster configuration that supports read and write operations."
type = object({
instance_id = string,
display_name = optional(string),
database_flags = optional(map(string))
labels = optional(map(string))
annotations = optional(map(string))
gce_zone = optional(string)
availability_type = optional(string)
machine_cpu_count = optional(number, 2),
})
validation {
condition = can(regex("^(2|4|8|16|32|64)$", var.primary_instance_config.machine_cpu_count))
error_message = "cpu count must be one of [2 4 8 16 32 64]."
}
validation {
condition = can(regex("^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$", var.primary_instance_config.instance_id))
error_message = "Primary Instance ID should satisfy the following pattern ^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$."
}
}
variable "project_id" {
description = "The ID of the project in which to provision resources."
type = string
}
variable "read_pool_instance" {
description = "List of Read Pool Instances to be created."
type = list(object({
instance_id = string
display_name = string
node_count = optional(number, 1)
database_flags = optional(map(string))
availability_type = optional(string)
gce_zone = optional(string)
machine_cpu_count = optional(number, 2)
}))
default = []
}

View File

@@ -0,0 +1,27 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
terraform {
required_version = ">= 1.7.4"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 5.12.0, < 6.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 5.12.0, < 6.0.0" # tftest
}
}
}

View File

@@ -0,0 +1,79 @@
# Google Cloud BQ Factory
This module allows creation and management of BigQuery datasets tables and views by defining them in well-formatted YAML files. YAML abstraction for BQ can simplify users onboarding and also makes creation of tables easier compared to HCL.
This factory is based on the [BQ dataset module](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/modules/bigquery-dataset) which currently only supports tables and views. As soon as external table and materialized view support is added, this factory will be enhanced accordingly.
You can create as many files as you like, the code will loop through it and create everything accordingly.
## Example
### Terraform code
In this section we show how to create tables and views from a file structure similar to the one shown below.
```bash
bigquery
├── tables
│ ├── table_a.yaml
│ ├── table_b.yaml
├── views
│ ├── view_a.yaml
│ ├── view_b.yaml
```
First we create the table definition in `bigquery/tables/countries.yaml`.
```yaml
# tftest-file id=table path=bigquery/tables/countries.yaml
dataset: my_dataset
table: countries
deletion_protection: true
labels:
env: prod
schema:
- name: country
type: STRING
- name: population
type: INT64
```
And a view in `bigquery/views/population.yaml`.
```yaml
# tftest-file id=view path=bigquery/views/population.yaml
dataset: my_dataset
view: department
query: SELECT SUM(population) from my_dataset.countries
labels:
env: prod
```
With this file structure, we can use the factory as follows:
```hcl
module "bq" {
source = "./fabric/modules/__experimental_deprecated/bigquery-factory"
project_id = var.project_id
tables_path = "bigquery/tables"
views_path = "bigquery/views"
}
# tftest modules=2 resources=3 files=table,view
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L17) | Project ID. | <code>string</code> | ✓ | |
| [tables_path](variables.tf#L22) | Relative path for the folder storing table data. | <code>string</code> | ✓ | |
| [views_path](variables.tf#L27) | Relative path for the folder storing view data. | <code>string</code> | ✓ | |
<!-- END TFDOC -->
## TODO
- [ ] add external table support
- [ ] add materialized view support

View File

@@ -0,0 +1,71 @@
/**
* 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.
*/
locals {
views = {
for f in fileset(var.views_path, "**/*.yaml") :
trimsuffix(f, ".yaml") => yamldecode(file("${var.views_path}/${f}"))
}
tables = {
for f in fileset(var.tables_path, "**/*.yaml") :
trimsuffix(f, ".yaml") => yamldecode(file("${var.tables_path}/${f}"))
}
all_datasets = distinct(concat(
[for x in values(local.tables) : x.dataset],
[for x in values(local.views) : x.dataset]
))
datasets = {
for dataset in local.all_datasets :
dataset => {
"views" = {
for k, v in local.views :
v.view => {
friendly_name = v.view
labels = try(v.labels, null)
query = v.query
use_legacy_sql = try(v.use_legacy_sql, false)
deletion_protection = try(v.deletion_protection, false)
}
if v.dataset == dataset
},
"tables" = {
for k, v in local.tables :
v.table => {
friendly_name = v.table
labels = try(v.labels, null)
options = try(v.options, null)
partitioning = try(v.partitioning, null)
schema = jsonencode(v.schema)
use_legacy_sql = try(v.use_legacy_sql, false)
deletion_protection = try(v.deletion_protection, false)
}
if v.dataset == dataset
}
}
}
}
module "bq" {
source = "../../../modules/bigquery-dataset"
for_each = local.datasets
project_id = var.project_id
id = each.key
views = try(each.value.views, null)
tables = try(each.value.tables, null)
}

View File

@@ -0,0 +1,30 @@
/**
* 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 "project_id" {
description = "Project ID."
type = string
}
variable "tables_path" {
description = "Relative path for the folder storing table data."
type = string
}
variable "views_path" {
description = "Relative path for the folder storing view data."
type = string
}

View File

@@ -0,0 +1,69 @@
# Google Cloud Identity Group Factory
This module allows creation and management of Cloud Identity Groups by defining them in well formatted `yaml` files.
Yaml abstraction for Groups can simplify groups creation and members management. Yaml can be simpler and clearer comparing to HCL.
## Example
### Terraform code
```hcl
module "groups" {
source = "./fabric/modules/__experimental_deprecated/cloud-identity-group-factory"
customer_id = "customers/C0xxxxxxx"
data_dir = "data"
}
# tftest modules=2 resources=3 files=group1
```
```yaml
# tftest-file id=group1 path=data/group1@example.com.yaml
display_name: Group 1
description: Group 1
members:
- user1@example.com
managers:
- user2@example.com
```
### Configuration Structure
Groups configuration should be placed in a set of yaml files. The name of the file identify the name of the group.
```bash
├── data
├── group1@domain.com.yaml
   ├── group2@domain.com.yaml
```
### Group definition format and structure
Within each file, the group entry structure is following:
```yaml
display_name: Group 1 # Group display name.
description: Group 1 description # Group description.
members: # List of group members.
- user_1@example.com
- user_2@example.com
managers: # List of group managers.
- manager_1@example.com
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [customer_id](variables.tf#L17) | Directory customer ID in the form customers/C0xxxxxxx. | <code>string</code> | ✓ | |
| [data_dir](variables.tf#L22) | Relative path for the folder storing configuration data. | <code>string</code> | ✓ | |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [group_id](outputs.tf#L17) | Group name => Group ID mapping. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,33 @@
/**
* 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 {
groups = {
for f in fileset("${var.data_dir}", "**/*.yaml") :
trimsuffix(f, ".yaml") => yamldecode(file("${var.data_dir}/${f}"))
}
}
module "group" {
source = "../../../modules/cloud-identity-group"
for_each = local.groups
customer_id = var.customer_id
name = each.key
display_name = try(each.value.display_name, null)
description = try(each.value.description, null)
members = try(each.value.members, [])
managers = try(each.value.managers, [])
}

View File

@@ -0,0 +1,23 @@
/**
* 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.
*/
output "group_id" {
description = "Group name => Group ID mapping."
value = {
for k in module.group :
k.name => k.id
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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 "customer_id" {
description = "Directory customer ID in the form customers/C0xxxxxxx."
type = string
}
variable "data_dir" {
description = "Relative path for the folder storing configuration data."
type = string
}

View File

@@ -0,0 +1,35 @@
# Google Cloud DNS Inbound Policy Addresses
This module allows discovering the addresses reserved in subnets when [DNS Inbound Policies](https://cloud.google.com/dns/docs/policies) are configured.
Since it's currently impossible to fetch those addresses using a GCP data source (see [this issue](https://github.com/hashicorp/terraform-provider-google/issues/3753) for more details), the workaround used here is to derive the authorization token from the Google provider, and do a direct HTTP call to the Compute API.
## Examples
```hcl
module "dns-policy-addresses" {
source = "./fabric/modules/__experimental/net-dns-policy-addresses"
project_id = "myproject"
regions = ["europe-west1", "europe-west3"]
}
# tftest skip (uses data sources)
```
The output is a map with lists of addresses of type `DNS_RESOLVER` for each region specified in variables.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L17) | Project id. | <code>string</code> | ✓ | |
| [regions](variables.tf#L22) | Regions to fetch addresses from. | <code>list&#40;string&#41;</code> | | <code>&#91;&#34;europe-west1&#34;&#93;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [addresses](outputs.tf#L24) | DNS inbound policy addresses per region. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,32 @@
/**
* 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 {
url = format(
"https://content-compute.googleapis.com/compute/v1/projects/%s",
var.project_id
)
}
data "google_client_config" "current" {}
data "http" "addresses" {
for_each = toset(var.regions)
url = "${local.url}/regions/${each.key}/addresses?filter=purpose%20%3D%20%22DNS_RESOLVER%22"
request_headers = {
Authorization = "Bearer ${data.google_client_config.current.access_token}"
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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 {
region_addresses = {
for k, v in data.http.addresses : k => try(jsondecode(v.body), {})
}
}
output "addresses" {
description = "DNS inbound policy addresses per region."
value = {
for k, v in local.region_addresses : k => [
for i in try(v.items, []) : i.address
]
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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 "project_id" {
description = "Project id."
type = string
}
variable "regions" {
description = "Regions to fetch addresses from."
nullable = false
type = list(string)
default = ["europe-west1"]
}

View File

@@ -0,0 +1,48 @@
# Network Endpoint Group Module
This modules allows creating zonal network endpoint groups.
Note: this module will integrated into a general-purpose load balancing module in the future.
## Example
```hcl
module "neg" {
source = "./fabric/modules/__experimental/net-neg/"
project_id = "myproject"
name = "myneg"
network = var.vpc.self_link
subnetwork = var.subnet.self_link
zone = "europe-west1-b"
endpoints = [
for instance in module.vm.instances :
{
instance = instance.name
port = 80
ip_address = instance.network_interface[0].network_ip
}
]
}
# tftest skip
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [endpoints](variables.tf#L17) | List of (instance, port, address) of the NEG. | <code title="list&#40;object&#40;&#123;&#10; instance &#61; string&#10; port &#61; number&#10; ip_address &#61; string&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [name](variables.tf#L26) | NEG name. | <code>string</code> | ✓ | |
| [network](variables.tf#L31) | Name or self link of the VPC used for the NEG. Use the self link for Shared VPC. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L36) | NEG project id. | <code>string</code> | ✓ | |
| [subnetwork](variables.tf#L41) | VPC subnetwork name or self link. | <code>string</code> | ✓ | |
| [zone](variables.tf#L46) | NEG zone. | <code>string</code> | ✓ | |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [id](outputs.tf#L17) | Network endpoint group ID. | |
| [self_lnk](outputs.tf#L22) | Network endpoint group self link. | |
| [size](outputs.tf#L27) | Size of the network endpoint group. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,33 @@
/**
* 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.
*/
resource "google_compute_network_endpoint_group" "group" {
project = var.project_id
name = var.name
network = var.network
subnetwork = var.subnetwork
zone = var.zone
}
resource "google_compute_network_endpoint" "endpoint" {
for_each = { for endpoint in var.endpoints : endpoint.instance => endpoint }
project = var.project_id
network_endpoint_group = google_compute_network_endpoint_group.group.name
instance = each.value.instance
port = each.value.port
ip_address = each.value.ip_address
zone = var.zone
}

View File

@@ -0,0 +1,30 @@
/**
* 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.
*/
output "id" {
description = "Network endpoint group ID."
value = google_compute_network_endpoint_group.group.name
}
output "self_lnk" {
description = "Network endpoint group self link."
value = google_compute_network_endpoint_group.group.self_link
}
output "size" {
description = "Size of the network endpoint group."
value = google_compute_network_endpoint_group.group.size
}

View File

@@ -0,0 +1,49 @@
/**
* 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 "endpoints" {
description = "List of (instance, port, address) of the NEG."
type = list(object({
instance = string
port = number
ip_address = string
}))
}
variable "name" {
description = "NEG name."
type = string
}
variable "network" {
description = "Name or self link of the VPC used for the NEG. Use the self link for Shared VPC."
type = string
}
variable "project_id" {
description = "NEG project id."
type = string
}
variable "subnetwork" {
description = "VPC subnetwork name or self link."
type = string
}
variable "zone" {
description = "NEG zone."
type = string
}

View File

@@ -0,0 +1,27 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
terraform {
required_version = ">= 1.7.4"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 5.12.0, < 6.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 5.12.0, < 6.0.0" # tftest
}
}
}

View File

@@ -0,0 +1,27 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
terraform {
required_version = ">= 1.7.4"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 5.12.0, < 6.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 5.12.0, < 6.0.0" # tftest
}
}
}