Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

☁️ Google Cloud Apigee Environment IAM Bindings Terraform Module

Grants additive, environment-scoped IAM role bindings via google_apigee_environment_iam_member, targeting hashicorp/google ~> 7.0.

Terraform Provider Module Version Module Type Resources Posture


🧩 Overview

  • Grants one IAM role to one principal per map entry, via google_apigee_environment_iam_member.member β€” additive and non-authoritative.
  • Never creates google_apigee_environment_iam_binding (authoritative for a role) or google_apigee_environment_iam_policy (authoritative for the entire environment policy).
  • No keystone this β€” this is an aggregation module. Every resource is named by role: member.
  • Supports an optional per-binding IAM Condition for time-bound or context-scoped access.
  • Mirrors the exact pattern already established by terraform-google-project-iam-bindings.

πŸ’‘ Why it matters: environment-level access delegation is a real, common need in Apigee β€” a dev team frequently needs deploy/manage rights scoped to just their environment (dev, staging) without touching prod. This module's additive form lets multiple teams grant different roles independently without a shared-state apply silently deleting each other's access, unlike the authoritative _iam_binding/_iam_policy forms.


❀️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


πŸ—ΊοΈ Where this fits

flowchart LR
 ORG["terraform-google-apigee-organization<br/>(same batch, required)"]:::keystone
 ENV["terraform-google-apigee-environment<br/>(same batch, required)"]:::keystone
 THIS["terraform-google-apigee-environment-iam-bindings<br/>(this module β€” aggregation)"]:::thismodule

 ORG -.->|"id -> org_id"| THIS
 ENV -.->|"id -> env_id"| THIS

 classDef thismodule fill:#4285F4,color:#FFFFFF,stroke:#174EA6,stroke-width:1px;
 classDef keystone fill:#174EA6,color:#FFFFFF,stroke:#174EA6,stroke-width:1px;
Loading

🧬 What this builds

flowchart TB
 subgraph Inputs
 IDS["var.org_id / var.env_id"]
 BIND["var.bindings"]
 end

 KEYSTONE["google_apigee_environment_iam_member.member<br/>(for_each, no keystone this)"]:::keystone

 subgraph Outputs
 BID["binding_ids"]
 BET["binding_etags"]
 end

 IDS --> KEYSTONE
 BIND --> KEYSTONE

 KEYSTONE --> BID
 KEYSTONE --> BET

 classDef keystone fill:#174EA6,color:#FFFFFF,stroke:#174EA6,stroke-width:1px;
Loading

Resource inventory

Resource Cardinality Notes
google_apigee_environment_iam_member.member for_each over var.bindings (0..N) One resource instance per map entry; additive grant only

βœ… Provider / Versions

Item Value
Terraform >= 1.12.0
google provider ~> 7.0
Provider block None β€” the caller configures google

Schema notes that bite:

  • org_id and env_id are both required with no fallback β€” an environment IAM binding needs both the organization and environment context explicitly.
  • condition is part of a binding's identity, not just metadata β€” changing title/description/expression out-of-band destroys the old binding and creates a new one.
  • No self_link β€” this resource family's only identity form is the composite id.
  • No labels argument and no timeouts block exist on this resource's schema at all β€” both deliberately absent from variables.tf.

πŸ”‘ Required IAM Roles

  • roles/apigee.admin β€” or a scoped role granting apigee.environments.setIamPolicy / apigee.environments.getIamPolicy, required to create/update/delete google_apigee_environment_iam_member bindings.

☁️ GCP Prerequisites

  • apigee.googleapis.com enabled.
  • Both the target organization and environment must already exist.
  • No quota concerns beyond standard IAM policy rate limits.

πŸ“ Module Structure

terraform-google-apigee-environment-iam-bindings/
β”œβ”€β”€ providers.tf # required_providers + required_version
β”œβ”€β”€ variables.tf # org_id, env_id, bindings (map(object({role, member, condition})))
β”œβ”€β”€ main.tf # google_apigee_environment_iam_member.member, for_each over var.bindings
β”œβ”€β”€ outputs.tf # binding_ids, binding_etags
β”œβ”€β”€ README.md
β”œβ”€β”€ SCOPE.md
└── examples/
 └── basic/

βš™οΈ Quick Start

module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "dev-team-env-admin" = {
      role   = "roles/apigee.environmentAdmin"
      member = "group:api-platform-dev@financialpartners.com"
    }
  }
}

ℹ️ The caller configures the google provider block with valid ADC, Workload Identity Federation, or a service-account key per our authentication model.


πŸ”Œ Cross-Module Contract

Consumes

Input Type Source module
org_id string terraform-google-apigee-organization (required β€” consumes its .id output)
env_id string terraform-google-apigee-environment (required β€” consumes its .id output)
(a member value may equally be a caller-supplied user:, group:, or domain: principal) string none β€” caller-supplied literal

Emits

Output Description Consumed by
binding_ids Map (keyed identically to var.bindings) of each binding's composite id None in this batch
binding_etags Map (keyed identically to var.bindings) of each binding's etag None

No self_link.


πŸ“š Example Library

1 Β· Minimal grant to a single user
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "jane-env-viewer" = {
      role   = "roles/apigee.readOnlyAdmin"
      member = "user:jane@financialpartners.com"
    }
  }
}

πŸ’‘ The empty call (bindings = {}) grants nothing β€” the secure default is zero grants.

2 Β· Grant environment admin to a dev team group
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment_dev.id

  bindings = {
    "dev-team-env-admin" = {
      role   = "roles/apigee.environmentAdmin"
      member = "group:api-platform-dev@financialpartners.com"
    }
  }
}
3 Β· Grant deployer role to a CI/CD service account
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "ci-deployer" = {
      role   = "roles/apigee.deployer"
      member = "serviceAccount:apigee-ci@casey-prod-apigee.iam.gserviceaccount.com"
    }
  }
}
4 Β· Multiple bindings in one apply
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "dev-team-env-admin" = { role = "roles/apigee.environmentAdmin", member = "group:api-platform-dev@financialpartners.com" }
    "sre-readonly"       = { role = "roles/apigee.readOnlyAdmin", member = "group:sre@financialpartners.com" }
    "ci-deployer"        = { role = "roles/apigee.deployer", member = "serviceAccount:apigee-ci@casey-prod-apigee.iam.gserviceaccount.com" }
  }
}
5 Β· Time-bound access via IAM Condition
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "temp-deployer-2026" = {
      role   = "roles/apigee.deployer"
      member = "user:jane@financialpartners.com"
      condition = {
        title       = "expires_after_2026_12_31"
        description = "Temporary elevated access, expires end of 2026"
        expression  = "request.time < timestamp(\"2027-01-01T00:00:00Z\")"
      }
    }
  }
}

⚠️ Changing title, description, or expression after initial apply causes Terraform to destroy this binding and create a new one β€” it is part of the binding's identity.

6 Β· Domain-wide grant
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "domain-readonly" = {
      role   = "roles/apigee.readOnlyAdmin"
      member = "domain:financialpartners.com"
    }
  }
}

πŸ”’ Domain restriction org policies can reject this at apply time even though terraform plan shows no conflict.

7 Β· Workload Identity Federation principal
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "gha-deployer" = {
      role   = "roles/apigee.deployer"
      member = "principal://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/gha-pool/subject/repo:FinancialPartnerscasey/apigee-proxies:ref:refs/heads/main"
    }
  }
}

πŸ’‘ The preferred CI/CD authentication pattern β€” no long-lived service account key material.

8 Β· Same role, multiple principals
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "viewer-alice" = { role = "roles/apigee.readOnlyAdmin", member = "user:alice@financialpartners.com" }
    "viewer-bob"   = { role = "roles/apigee.readOnlyAdmin", member = "user:bob@financialpartners.com" }
  }
}

πŸ’‘ One google_apigee_environment_iam_member per (role, member) pair β€” a role granted to five principals means five map entries, not one entry with a list.

9 Β· Removing a binding safely
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "viewer-alice" = { role = "roles/apigee.readOnlyAdmin", member = "user:alice@financialpartners.com" }
    # "viewer-bob" removed β€” only this entry's grant is revoked on the next apply
  }
}
10 Β· Referencing binding outputs downstream
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "ci-deployer" = {
      role   = "roles/apigee.deployer"
      member = "serviceAccount:apigee-ci@casey-prod-apigee.iam.gserviceaccount.com"
    }
  }
}

output "ci_deployer_binding_id" {
  value = module.apigee_environment_iam_bindings.binding_ids["ci-deployer"]
}
11 Β· Reusing the same bindings map across environments
locals {
  standard_readonly_bindings = {
    "sre-readonly" = { role = "roles/apigee.readOnlyAdmin", member = "group:sre@financialpartners.com" }
  }
}

module "apigee_environment_iam_bindings_dev" {
  source   = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"
  org_id   = module.apigee_org.id
  env_id   = module.apigee_environment_dev.id
  bindings = local.standard_readonly_bindings
}

module "apigee_environment_iam_bindings_prod" {
  source   = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"
  org_id   = module.apigee_org.id
  env_id   = module.apigee_environment_prod.id
  bindings = local.standard_readonly_bindings
}
12 Β· Custom role grant
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "custom-role-grant" = {
      role   = "organizations/123456789012/roles/customApigeeAuditor"
      member = "group:api-audit@financialpartners.com"
    }
  }
}
13 Β· Basic Role + Condition rejection (what not to do)
# This will pass `terraform plan` but be REJECTED by the API at apply time β€”
# GCP does not allow IAM Conditions on Basic Roles (owner/editor/viewer).
module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "bad-conditional-editor" = {
      role   = "roles/editor"
      member = "user:jane@financialpartners.com"
      condition = {
        title      = "temp"
        expression = "request.time < timestamp(\"2027-01-01T00:00:00Z\")"
      }
    }
  }
}
14 Β· Staging environment scoped grant
module "apigee_environment_iam_bindings_staging" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment_staging.id

  bindings = {
    "qa-team-deployer" = {
      role   = "roles/apigee.deployer"
      member = "group:qa@financialpartners.com"
    }
  }
}
15 Β· πŸ—οΈ End-to-end composition
module "apigee_org" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-organization.git?ref=v1.0.0"

  project_id          = "casey-prod-apigee"
  analytics_region    = "us-central1"
  disable_vpc_peering = true
}

module "apigee_environment" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  name   = "prod"
}

module "apigee_environment_iam_bindings" {
  source = "git::https://github.com/microsoftexpert/terraform-google-apigee-environment-iam-bindings.git?ref=v1.0.0"

  org_id = module.apigee_org.id
  env_id = module.apigee_environment.id

  bindings = {
    "dev-team-env-admin" = {
      role   = "roles/apigee.environmentAdmin"
      member = "group:api-platform-dev@financialpartners.com"
    }
    "ci-deployer" = {
      role   = "roles/apigee.deployer"
      member = "serviceAccount:apigee-ci@casey-prod-apigee.iam.gserviceaccount.com"
    }
  }
}

πŸ“₯ Inputs

Name Type Default Required Notes
org_id string β€” Yes Consumes the organization's .id output
env_id string β€” Yes Consumes the environment's .id output
bindings map(object({...})) {} No Empty map grants nothing (secure default)
Full object schemas
variable "org_id" {
  type = string
}

variable "env_id" {
  type = string
}

variable "bindings" {
  type = map(object({
    role   = string
    member = string
    condition = optional(object({
      expression  = string
      title       = string
      description = optional(string)
    }))
  }))
  default = {}
}

No labels or timeouts variable exists on this module β€” the resource's schema exposes neither.


🧾 Outputs

Output Description
binding_ids Map (keyed identically to var.bindings) of each binding's composite id
binding_etags Map (keyed identically to var.bindings) of each binding's etag

No self_link output β€” confirmed absent from this resource.


🧠 Architecture Notes

  • org_id/env_id are both required together β€” an environment IAM binding needs both parent contexts explicitly, unlike most Apigee resources which take only one _id-suffixed reference.
  • IAM propagation delay: changes can take up to ~60 seconds to become effective.
  • condition is part of a binding's identity β€” changing it out-of-band destroys/recreates.
  • No labels/timeouts β€” confirmed absent from this resource's schema.

🧱 Design Principles

Concern Secure default Opt-out (explicit)
Authoritative IAM resources This module only ever creates google_apigee_environment_iam_member (additive) N/A β€” _iam_binding/_iam_policy intentionally excluded
Default grants var.bindings defaults to {} Caller supplies explicit map entries
Broad/public principals Not defaulted or suggested anywhere Caller supplies explicitly, aware of the risk

πŸš€ Runbook

terraform init -backend=false
terraform validate
terraform fmt -check

Pin ?ref=v1.0.0 β€” never a branch. This library is plan-only; a human applies from CI with valid credentials.


πŸ§ͺ Testing

validate/fmt -check confirm structural and syntactic correctness only. They cannot catch GCP-side rejections β€” e.g. an IAM Condition on a Basic Role, or a domain restriction org policy blocking a member value, surface only at apply time against a real project.


πŸ’¬ Example Output

binding_ids = {
 "dev-team-env-admin" = "organizations/casey-prod-apigee/environments/prod roles/apigee.environmentAdmin group:api-platform-dev@financialpartners.com"
}
binding_etags = {
 "dev-team-env-admin" = "BwYzX1abcde="
}

πŸ” Troubleshooting

Symptom Cause Fix
A resource in the same apply fails with transient permission-denied IAM propagation delay Re-apply, or add a wait step
An unrelated binding is destroyed/recreated after editing condition condition is part of the binding's identity Expected behavior
Apply rejected with an IAM Condition / Basic Role error GCP disallows condition on Basic Roles Use a predefined non-Basic or custom role instead

πŸ”— Related Docs

About

Terraform module: terraform-google-apigee-environment-iam-bindings

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages