diff --git a/modules/apigee/README.md b/modules/apigee/README.md index 7692d6f4b0..4a8f67d927 100644 --- a/modules/apigee/README.md +++ b/modules/apigee/README.md @@ -15,6 +15,7 @@ This module simplifies the creation of a Apigee resources (organization, environ - [New environment](#new-environment) - [New instance (VPC Peering Provisioning Mode)](#new-instance-vpc-peering-provisioning-mode) - [New instance (Non VPC Peering Provisioning Mode)](#new-instance-non-vpc-peering-provisioning-mode) + - [IAM](#iam) - [New endpoint attachment](#new-endpoint-attachment) - [Apigee add-ons](#apigee-add-ons) - [Variables](#variables) @@ -87,7 +88,6 @@ module "apigee" { When a new Apigee organization is created, it is automatically peered to the authorized network. You can prevent this from happening by using the `disable_vpc_peering` key in the `organization` variable, as shown below: - ```hcl module "apigee" { source = "./fabric/modules/apigee" @@ -117,7 +117,6 @@ module "apigee" { # tftest modules=1 resources=6 inventory=no-peering.yaml ``` - ### All resources (CLOUD) ```hcl @@ -147,9 +146,6 @@ module "apigee" { display_name = "APIs prod" description = "APIs prod" envgroups = ["prod"] - iam = { - "roles/viewer" = ["group:devops@myorg.com"] - } } } instances = { @@ -176,7 +172,7 @@ module "apigee" { } } } -# tftest modules=1 resources=15 +# tftest modules=1 resources=14 ``` ### All resources (HYBRID control plane) @@ -205,13 +201,10 @@ module "apigee" { display_name = "APIs prod" description = "APIs prod" envgroups = ["prod"] - iam = { - "roles/viewer" = ["group:devops@myorg.com"] - } } } } -# tftest modules=1 resources=8 +# tftest modules=1 resources=7 ``` ### New environment group @@ -281,6 +274,56 @@ module "apigee" { # tftest modules=1 resources=2 ``` +### IAM + +```hcl +module "apigee" { + source = "./fabric/modules/apigee" + project_id = "my-project" + organization = { + display_name = "My Organization" + description = "My Organization" + authorized_network = "my-vpc" + runtime_type = "CLOUD" + billing_type = "PAYG" + database_encryption_key = "123456789" + analytics_region = "europe-west1" + } + envgroups = { + test = ["test.example.com"] + prod = ["prod.example.com"] + } + environments = { + apis-test = { + display_name = "APIs test" + description = "APIs Test" + envgroups = ["test"] + iam = { + "roles/apigee.environmentAdmin" = ["group:apigee-env-admin@myorg.com"] + } + iam_bindings_additive = { + viewer = { + role = "roles/viewer" + member = "user:user1@myorg.com" + } + } + } + apis-prod = { + display_name = "APIs prod" + description = "APIs prod" + envgroups = ["prod"] + iam_bindings = { + apigee-env-admin = { + role = "roles/apigee.environmentAdmin" + members = ["group:apigee-env-admin@myorg.com"] + } + } + } + } +} +# tftest modules=1 resources=10 +``` + ### New endpoint attachment Endpoint attachments allow to implement [Apigee southbound network patterns](https://cloud.google.com/apigee/docs/api-platform/architecture/southbound-networking-patterns-endpoints#create-the-psc-attachments). @@ -316,13 +359,13 @@ module "apigee" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [project_id](variables.tf#L117) | Project ID. | string | ✓ | | +| [project_id](variables.tf#L125) | Project ID. | string | ✓ | | | [addons_config](variables.tf#L17) | Addons configuration. | object({…}) | | null | | [endpoint_attachments](variables.tf#L29) | Endpoint attachments. | map(object({…})) | | {} | | [envgroups](variables.tf#L39) | Environment groups (NAME => [HOSTNAMES]). | map(list(string)) | | {} | -| [environments](variables.tf#L46) | Environments. | map(object({…})) | | {} | -| [instances](variables.tf#L64) | Instances ([REGION] => [INSTANCE]). | map(object({…})) | | {} | -| [organization](variables.tf#L89) | Apigee organization. If set to null the organization must already exist. | object({…}) | | null | +| [environments](variables.tf#L46) | Environments. | map(object({…})) | | {} | +| [instances](variables.tf#L72) | Instances ([REGION] => [INSTANCE]). | map(object({…})) | | {} | +| [organization](variables.tf#L97) | Apigee organization. If set to null the organization must already exist. | object({…}) | | null | ## Outputs diff --git a/modules/apigee/iam.tf b/modules/apigee/iam.tf new file mode 100644 index 0000000000..fc6d96f206 --- /dev/null +++ b/modules/apigee/iam.tf @@ -0,0 +1,57 @@ +/** + * 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_apigee_environment_iam_binding" "authoritative" { + for_each = merge(concat([for k1, v1 in var.environments : { + for k2, v2 in v1.iam : "${k1}-${k2}" => { + environment = "${k1}" + role = k2 + members = v2 + } + }])...) + org_id = local.org_id + env_id = google_apigee_environment.environments[each.value.environment].name + role = each.value.role + members = each.value.members +} + +resource "google_apigee_environment_iam_binding" "bindings" { + for_each = merge(concat([for k1, v1 in var.environments : { + for k2, v2 in coalesce(v1.iam_bindings, {}) : "${k1}-${k2}" => { + environment = "${k1}" + role = v2.role + members = v2.members + } + }])...) + org_id = local.org_id + env_id = google_apigee_environment.environments[each.value.environment].name + role = each.value.role + members = each.value.members +} + +resource "google_apigee_environment_iam_member" "bindings" { + for_each = merge(concat([for k1, v1 in var.environments : { + for k2, v2 in coalesce(v1.iam_bindings_additive, {}) : "${k1}-${k2}" => { + environment = "${k1}" + role = v2.role + member = v2.member + } + }])...) + org_id = local.org_id + env_id = google_apigee_environment.environments[each.value.environment].name + role = each.value.role + member = each.value.member +} diff --git a/modules/apigee/main.tf b/modules/apigee/main.tf index 46f7655564..be571a8ae2 100644 --- a/modules/apigee/main.tf +++ b/modules/apigee/main.tf @@ -62,7 +62,7 @@ resource "google_apigee_environment" "environments" { resource "google_apigee_envgroup_attachment" "envgroup_attachments" { for_each = merge(concat([for k1, v1 in var.environments : { - for v2 in coalesce(v1.envgroups, []) : "${k1}-${v2}" => { + for v2 in v1.envgroups : "${k1}-${v2}" => { environment = k1 envgroup = v2 } @@ -72,20 +72,6 @@ resource "google_apigee_envgroup_attachment" "envgroup_attachments" { depends_on = [google_apigee_envgroup.envgroups] } -resource "google_apigee_environment_iam_binding" "binding" { - for_each = merge(concat([for k1, v1 in var.environments : { - for k2, v2 in coalesce(v1.iam, {}) : "${k1}-${k2}" => { - environment = "${k1}" - role = k2 - members = v2 - } - }])...) - org_id = local.org_id - env_id = google_apigee_environment.environments[each.value.environment].name - role = each.value.role - members = each.value.members -} - resource "google_apigee_instance" "instances" { for_each = var.instances name = coalesce(each.value.name, "instance-${each.key}") @@ -114,7 +100,7 @@ resource "google_apigee_nat_address" "apigee_nat" { resource "google_apigee_instance_attachment" "instance_attachments" { for_each = merge(concat([for k1, v1 in var.instances : { - for v2 in coalesce(v1.environments, []) : + for v2 in v1.environments : "${k1}-${v2}" => { instance = k1 environment = v2 diff --git a/modules/apigee/variables.tf b/modules/apigee/variables.tf index 78549507e5..7ec2cc2d64 100644 --- a/modules/apigee/variables.tf +++ b/modules/apigee/variables.tf @@ -54,8 +54,16 @@ variable "environments" { min_node_count = optional(number) max_node_count = optional(number) })) - iam = optional(map(list(string))) - envgroups = optional(list(string)) + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(object({ + role = string + members = list(string) + })), {}) + iam_bindings_additive = optional(map(object({ + role = string + member = string + })), {}) + envgroups = optional(list(string), []) })) default = {} nullable = false @@ -72,7 +80,7 @@ variable "instances" { disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) enable_nat = optional(bool, false) - environments = optional(list(string)) + environments = optional(list(string), []) })) validation { condition = alltrue([ diff --git a/tests/modules/apigee/all_psc_mode.yaml b/tests/modules/apigee/all_psc_mode.yaml index c31c713ae7..e702821385 100644 --- a/tests/modules/apigee/all_psc_mode.yaml +++ b/tests/modules/apigee/all_psc_mode.yaml @@ -41,7 +41,7 @@ values: description: APIs Test display_name: APIs test name: apis-test - google_apigee_environment_iam_binding.binding["apis-prod-roles/viewer"]: + google_apigee_environment_iam_binding.authoritative["apis-prod-roles/viewer"]: condition: [] env_id: apis-prod members: diff --git a/tests/modules/apigee/all_vpc_mode.yaml b/tests/modules/apigee/all_vpc_mode.yaml index 2d39429c75..b5ebdec1bc 100644 --- a/tests/modules/apigee/all_vpc_mode.yaml +++ b/tests/modules/apigee/all_vpc_mode.yaml @@ -42,7 +42,7 @@ values: description: APIs Test display_name: APIs test name: apis-test - google_apigee_environment_iam_binding.binding["apis-prod-roles/viewer"]: + google_apigee_environment_iam_binding.authoritative["apis-prod-roles/viewer"]: condition: [] env_id: apis-prod members: