Merge pull request #75 from terraform-google-modules/ludo-service-directory

New service directory module and sd zone type in DNS module
This commit is contained in:
Roberto Jung Drebes
2020-05-13 13:58:59 +02:00
committed by GitHub
8 changed files with 402 additions and 18 deletions

View File

@@ -1,6 +1,8 @@
# Google Cloud DNS Module
This module allows simple management of Google Cloud DNS zones and records. It supports creating public, private, forwarding, and peering zones. For DNSSEC configuration, refer to the [`dns_managed_zone` documentation](https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html#dnssec_config).
This module allows simple management of Google Cloud DNS zones and records. It supports creating public, private, forwarding, peering and service directory based zones.
For DNSSEC configuration, refer to the [`dns_managed_zone` documentation](https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html#dnssec_config).
## Example
@@ -32,9 +34,10 @@ module "private-dns" {
| *description* | Domain description. | <code title="">string</code> | | <code title="">Terraform managed.</code> |
| *dnssec_config* | DNSSEC configuration: kind, non_existence, state. | <code title="">any</code> | | <code title="">{}</code> |
| *forwarders* | List of target name servers, only valid for 'forwarding' zone types. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *peer_network* | Peering network self link, only valid for 'peering' zone types. | <code title="">string</code> | | <code title=""></code> |
| *peer_network* | Peering network self link, only valid for 'peering' zone types. | <code title="">string</code> | | <code title="">null</code> |
| *recordsets* | List of DNS record objects to manage. | <code title="list&#40;object&#40;&#123;&#10;name &#61; string&#10;type &#61; string&#10;ttl &#61; number&#10;records &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">list(object({...}))</code> | | <code title="">[]</code> |
| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering'. | <code title="">string</code> | | <code title="">private</code> |
| *service_directory_namespace* | Service directory namespace id (URL), only valid for 'service-directory' zone types. | <code title="">string</code> | | <code title="">null</code> |
| *type* | Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'. | <code title="">string</code> | | <code title="">private</code> |
## Outputs

View File

@@ -38,14 +38,11 @@ resource "google_dns_managed_zone" "non-public" {
dynamic forwarding_config {
for_each = (
var.type == "forwarding" && var.forwarders != null
? { config = var.forwarders }
: {}
var.type == "forwarding" && var.forwarders != null ? [""] : []
)
iterator = config
content {
dynamic "target_name_servers" {
for_each = config.value
for_each = var.forwarders
iterator = address
content {
ipv4_address = address.value
@@ -56,14 +53,11 @@ resource "google_dns_managed_zone" "non-public" {
dynamic peering_config {
for_each = (
var.type == "peering" && var.peer_network != null
? { config = var.peer_network }
: {}
var.type == "peering" && var.peer_network != null ? [""] : []
)
iterator = config
content {
target_network {
network_url = config.value
network_url = var.peer_network
}
}
}
@@ -78,6 +72,19 @@ resource "google_dns_managed_zone" "non-public" {
}
}
dynamic service_directory_config {
for_each = (
var.type == "service-directory" && var.service_directory_namespace != null
? [""]
: []
)
content {
namespace {
namespace_url = var.service_directory_namespace
}
}
}
}
resource "google_dns_managed_zone" "public" {

View File

@@ -30,9 +30,6 @@ variable "description" {
default = "Terraform managed."
}
# TODO(ludoo): add link to DNSSEC documentation in README
# https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html#dnssec_config
variable "default_key_specs_key" {
description = "DNSSEC default key signing specifications: algorithm, key_length, key_type, kind."
type = any
@@ -71,7 +68,7 @@ variable "name" {
variable "peer_network" {
description = "Peering network self link, only valid for 'peering' zone types."
type = string
default = ""
default = null
}
variable "project_id" {
@@ -90,8 +87,14 @@ variable "recordsets" {
default = []
}
variable "service_directory_namespace" {
description = "Service directory namespace id (URL), only valid for 'service-directory' zone types."
type = string
default = null
}
variable "type" {
description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering'."
description = "Type of zone to create, valid values are 'public', 'private', 'forwarding', 'peering', 'service-directory'."
type = string
default = "private"
}

View File

@@ -0,0 +1,125 @@
# Google Cloud Service Directory Module
This module allows managing a single [Service Directory](https://cloud.google.com/service-directory) namespace, including multiple services, endpoints and IAM bindings at the namespace and service levels.
It can be used in conjunction with the [DNS](../dns) module to create [service-directory based DNS zones](https://cloud.google.com/service-directory/docs/configuring-service-directory-zone, offloading IAM control of `A` and `SRV` records at the namespace or service level to Service Directory. The last examples shows how to wire the two modules together.
## Examples
### Namespace with IAM
```hcl
module "service-directory" {
source = "./modules/service-directory"
project_id = "my-project"
location = "europe-west1"
name = "sd-1"
iam_members = {
"roles/servicedirectory.editor" = [
"serviceAccount:namespace-editor@example.com"
]
}
iam_roles = [
"roles/servicedirectory.editor"
]
}
```
### Services with IAM and endpoints
```hcl
module "service-directory" {
source = "./modules/service-directory"
project_id = "my-project"
location = "europe-west1"
name = "sd-1"
services = {
one = {
endpoints = ["first", "second"]
metadata = null
}
}
service_iam_members = {
one = {
"roles/servicedirectory.editor" = [
"serviceAccount:service-editor.example.com"
]
}
}
service_iam_roles = {
one = ["roles/servicedirectory.editor"]
}
endpoint_config = {
"one/first" = { address = "127.0.0.1", port = 80, metadata = {} }
"one/second" = { address = "127.0.0.2", port = 80, metadata = {} }
}
}
```
### DNS based zone
Wiring a service directory namespace to a private DNS zone allows querying the namespace, and delegating control of DNS records at the namespace or service level. This effectively allows fine grained ACL control of Cloud DNS zones.
```hcl
module "service-directory" {
source = "./modules/service-directory"
project_id = "my-project"
location = "europe-west1"
name = "apps"
iam_members = {
"roles/servicedirectory.editor" = [
"serviceAccount:namespace-editor@example.com"
]
}
iam_roles = [
"roles/servicedirectory.editor"
]
services = {
app1 = { endpoints = ["one"], metadata = null }
}
endpoint_config = {
"app1/one" = { address = "127.0.0.1", port = 80, metadata = {} }
}
}
module "dns-sd" {
source = "./modules/dns"
project_id = "my-project"
type = "service-directory"
name = "apps"
domain = "apps.example.org."
client_networks = [local.vpc_self_link]
service_directory_namespace = module.service-directory.id
}
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| location | Namespace location. | <code title="">string</code> | ✓ | |
| name | Namespace name. | <code title="">string</code> | ✓ | |
| project_id | Project used for resources. | <code title="">string</code> | ✓ | |
| *endpoint_config* | Map of endpoint attributes, keys are in service/endpoint format. | <code title="map&#40;object&#40;&#123;&#10;address &#61; string&#10;port &#61; number&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *iam_members* | IAM members for each namespace role. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *iam_roles* | IAM roles for the namespace. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *labels* | Labels. | <code title="map&#40;string&#41;">map(string)</code> | | <code title="">{}</code> |
| *service_iam_members* | IAM members for each service and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">{}</code> |
| *service_iam_roles* | IAM roles for each service. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">{}</code> |
| *services* | Service configuration, using service names as keys. | <code title="map&#40;object&#40;&#123;&#10;endpoints &#61; list&#40;string&#41;&#10;metadata &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| endpoints | Endpoint resources. | |
| id | Namespace id (short name). | |
| name | Namespace name (long name). | |
| namespace | Namespace resource. | |
| service_id | Service ids (short names). | |
| service_names | Service ids (long names). | |
| services | Service resources. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,81 @@
/**
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
endpoint_list = flatten([
for name, attrs in var.services : [
for endpoint in attrs.endpoints : { service : name, endpoint : endpoint }
]
])
endpoints = {
for ep in local.endpoint_list : "${ep.service}/${ep.endpoint}" => ep
}
iam_pairs = var.service_iam_roles == null ? [] : flatten([
for name, roles in var.service_iam_roles :
[for role in roles : { name = name, role = role }]
])
iam_keypairs = {
for pair in local.iam_pairs :
"${pair.name}-${pair.role}" => pair
}
iam_members = (
var.service_iam_members == null ? {} : var.service_iam_members
)
}
resource "google_service_directory_namespace" "default" {
provider = google-beta
project = var.project_id
namespace_id = var.name
location = var.location
labels = var.labels
}
resource "google_service_directory_namespace_iam_binding" "default" {
provider = google-beta
for_each = toset(var.iam_roles)
name = google_service_directory_namespace.default.name
role = each.value
members = lookup(var.iam_members, each.value, [])
}
resource "google_service_directory_service" "default" {
provider = google-beta
for_each = var.services
namespace = google_service_directory_namespace.default.id
service_id = each.key
metadata = each.value.metadata
}
resource "google_service_directory_service_iam_binding" "default" {
provider = google-beta
for_each = local.iam_keypairs
name = google_service_directory_service.default[each.value.name].name
role = each.value.role
members = lookup(
lookup(local.iam_members, each.value.name, {}), each.value.role, []
)
}
resource "google_service_directory_endpoint" "default" {
provider = google-beta
for_each = local.endpoints
endpoint_id = each.value.endpoint
service = google_service_directory_service.default[each.value.service].id
metadata = try(var.endpoint_config[each.key].metadata, null)
address = try(var.endpoint_config[each.key].address, null)
port = try(var.endpoint_config[each.key].port, null)
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
output "endpoints" {
description = "Endpoint resources."
value = google_service_directory_endpoint.default
}
output "id" {
description = "Namespace id (short name)."
value = google_service_directory_namespace.default.id
}
output "name" {
description = "Namespace name (long name)."
value = google_service_directory_namespace.default.name
}
output "namespace" {
description = "Namespace resource."
value = google_service_directory_namespace.default
depends_on = [
google_service_directory_namespace_iam_binding.default
]
}
output "services" {
description = "Service resources."
value = google_service_directory_service.default
depends_on = [
google_service_directory_service_iam_binding.default
]
}
output "service_id" {
description = "Service ids (short names)."
value = {
for k, v in google_service_directory_service.default : k => v.id
}
depends_on = [
google_service_directory_service_iam_binding.default
]
}
output "service_names" {
description = "Service ids (long names)."
value = {
for k, v in google_service_directory_service.default : k => v.name
}
depends_on = [
google_service_directory_service_iam_binding.default
]
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# we need a separate variable as address will be dynamic in most cases
variable "endpoint_config" {
description = "Map of endpoint attributes, keys are in service/endpoint format."
type = map(object({
address = string
port = number
metadata = map(string)
}))
default = {}
}
variable "iam_members" {
description = "IAM members for each namespace role."
type = map(list(string))
default = {}
}
variable "iam_roles" {
description = "IAM roles for the namespace."
type = list(string)
default = []
}
variable "labels" {
description = "Labels."
type = map(string)
default = {}
}
variable "location" {
description = "Namespace location."
type = string
}
variable "name" {
description = "Namespace name."
type = string
}
variable "project_id" {
description = "Project used for resources."
type = string
}
variable "service_iam_members" {
description = "IAM members for each service and role."
type = map(map(list(string)))
default = {}
}
variable "service_iam_roles" {
description = "IAM roles for each service."
type = map(list(string))
default = {}
}
variable "services" {
description = "Service configuration, using service names as keys."
type = map(object({
endpoints = list(string)
metadata = map(string)
}))
default = {}
}

View File

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