Add support for creating multiple workforce identity pools (#3846)

* Added support for multiple workforce identity pools

* Fixed organization module workforce identity federation outputs

* tfdoc

---------

Co-authored-by: Ludovico Magnocavallo <ludomagno@google.com>
Co-authored-by: Julio Castillo <jccb@google.com>
This commit is contained in:
kovagoadam
2026-04-14 08:55:18 +02:00
committed by GitHub
parent f7d74a6b1f
commit 1050daff71
8 changed files with 278 additions and 250 deletions

View File

@@ -99,8 +99,8 @@ module "organization" {
tags_config = {
ignore_iam = true
}
workforce_identity_config = try(
local.organization.workforce_identity_config, null
workforce_identity_pools = try(
local.organization.workforce_identity_pools, null
)
}

View File

@@ -353,177 +353,180 @@
}
}
},
"workforce_identity_config": {
"workforce_identity_pools": {
"type": "object",
"additionalProperties": false,
"properties": {
"pool_name": {
"type": "string"
},
"display_name": {
"type": "string"
},
"description": {
"type": "string"
},
"disabled": {
"type": "boolean"
},
"session_duration": {
"type": "string"
},
"access_restrictions": {
"patternProperties": {
"^[a-z][a-z0-9-]+[a-z0-9]$": {
"type": "object",
"additionalProperties": false,
"properties": {
"allowed_services": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"domain": {
"type": "string"
}
}
}
"display_name": {
"type": "string"
},
"disable_programmatic_signin": {
"description": {
"type": "string"
},
"disabled": {
"type": "boolean"
}
}
},
"providers": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z][a-z0-9-]+[a-z0-9]$": {
},
"session_duration": {
"type": "string"
},
"access_restrictions": {
"type": "object",
"additionalProperties": false,
"properties": {
"description": {
"type": "string"
"allowed_services": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"domain": {
"type": "string"
}
}
}
},
"display_name": {
"type": "string"
},
"attribute_condition": {
"type": "string"
},
"attribute_mapping": {
"type": "object"
},
"attribute_mapping_template": {
"type": "string",
"enum": [
"azuread",
"okta"
]
},
"disabled": {
"disable_programmatic_signin": {
"type": "boolean"
},
"identity_provider": {
}
}
},
"providers": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z][a-z0-9-]+[a-z0-9]$": {
"type": "object",
"oneOf": [
{
"properties": {
"oidc": {
"type": "object",
"description": "OpenID Connect configuration.",
"additionalProperties": false,
"properties": {
"description": {
"type": "string"
},
"display_name": {
"type": "string"
},
"attribute_condition": {
"type": "string"
},
"attribute_mapping": {
"type": "object"
},
"attribute_mapping_template": {
"type": "string",
"enum": [
"azuread",
"okta"
]
},
"disabled": {
"type": "boolean"
},
"identity_provider": {
"type": "object",
"oneOf": [
{
"properties": {
"issuer_uri": {
"type": "string",
"description": "The URI of the OIDC issuer."
},
"client_id": {
"type": "string",
"description": "The client ID."
},
"client_secret": {
"type": "string",
"description": "The client secret (optional)."
},
"jwks_json": {
"type": "string",
"description": "JSON Web Key Set as a JSON string (optional)."
},
"web_sso_config": {
"oidc": {
"type": "object",
"description": "Optional Web SSO configuration for OIDC.",
"description": "OpenID Connect configuration.",
"properties": {
"response_type": {
"issuer_uri": {
"type": "string",
"default": "CODE",
"enum": [
"CODE",
"ID_TOKEN"
]
"description": "The URI of the OIDC issuer."
},
"assertion_claims_behavior": {
"client_id": {
"type": "string",
"default": "ONLY_ID_TOKEN_CLAIMS",
"enum": [
"MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS",
"ONLY_ID_TOKEN_CLAIMS"
]
"description": "The client ID."
},
"additional_scopes": {
"type": "array",
"items": {
"type": "string"
}
"client_secret": {
"type": "string",
"description": "The client secret (optional)."
},
"jwks_json": {
"type": "string",
"description": "JSON Web Key Set as a JSON string (optional)."
},
"web_sso_config": {
"type": "object",
"description": "Optional Web SSO configuration for OIDC.",
"properties": {
"response_type": {
"type": "string",
"default": "CODE",
"enum": [
"CODE",
"ID_TOKEN"
]
},
"assertion_claims_behavior": {
"type": "string",
"default": "ONLY_ID_TOKEN_CLAIMS",
"enum": [
"MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS",
"ONLY_ID_TOKEN_CLAIMS"
]
},
"additional_scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [],
"additionalProperties": false
}
},
"required": [],
"required": [
"issuer_uri",
"client_id"
],
"additionalProperties": false
}
},
"required": [
"issuer_uri",
"client_id"
"oidc"
],
"additionalProperties": false
}
},
"required": [
"oidc"
],
"additionalProperties": false
},
{
"properties": {
"saml": {
"type": "object",
"description": "SAML configuration.",
},
{
"properties": {
"idp_metadata_xml": {
"type": "string",
"description": "SAML IdP metadata XML."
"saml": {
"type": "object",
"description": "SAML configuration.",
"properties": {
"idp_metadata_xml": {
"type": "string",
"description": "SAML IdP metadata XML."
}
},
"required": [
"idp_metadata_xml"
],
"additionalProperties": false
}
},
"required": [
"idp_metadata_xml"
"saml"
],
"additionalProperties": false
}
},
"required": [
"saml"
],
"additionalProperties": false
}
]
},
"oauth2_client_config": {
"type": "object",
"additionalProperties": false,
"properties": {
"extended_attributes": {
"$ref": "#/$defs/wfif_oauth2_client_attrs"
]
},
"extra_attributes": {
"$ref": "#/$defs/wfif_oauth2_client_attrs"
"oauth2_client_config": {
"type": "object",
"additionalProperties": false,
"properties": {
"extended_attributes": {
"$ref": "#/$defs/wfif_oauth2_client_attrs"
},
"extra_attributes": {
"$ref": "#/$defs/wfif_oauth2_client_attrs"
}
}
}
}
}

View File

@@ -98,36 +98,37 @@
- **pam_entitlements**: *reference([pam_entitlements](#refs-pam_entitlements))*
- **tags**: *object*
<br>*additional properties: object*
- **workforce_identity_config**: *object*
- **workforce_identity_pools**: *object*
<br>*additional properties: false*
- **pool_name**: *string*
- **display_name**: *string*
- **description**: *string*
- **disabled**: *boolean*
- **session_duration**: *string*
- **access_restrictions**: *object*
- **`^[a-z][a-z0-9-]+[a-z0-9]$`**: *object*
<br>*additional properties: false*
- **allowed_services**: *array*
- items: *object*
<br>*additional properties: false*
- **domain**: *string*
- **disable_programmatic_signin**: *boolean*
- **providers**: *object*
<br>*additional properties: false*
- **`^[a-z][a-z0-9-]+[a-z0-9]$`**: *object*
- **display_name**: *string*
- **description**: *string*
- **disabled**: *boolean*
- **session_duration**: *string*
- **access_restrictions**: *object*
<br>*additional properties: false*
- **description**: *string*
- **display_name**: *string*
- **attribute_condition**: *string*
- **attribute_mapping**: *object*
- **attribute_mapping_template**: *string*
<br>*enum: ['azuread', 'okta']*
- **disabled**: *boolean*
- **identity_provider**: *object*
- **oauth2_client_config**: *object*
- **allowed_services**: *array*
- items: *object*
<br>*additional properties: false*
- **domain**: *string*
- **disable_programmatic_signin**: *boolean*
- **providers**: *object*
<br>*additional properties: false*
- **`^[a-z][a-z0-9-]+[a-z0-9]$`**: *object*
<br>*additional properties: false*
- **extended_attributes**: *reference([wfif_oauth2_client_attrs](#refs-wfif_oauth2_client_attrs))*
- **extra_attributes**: *reference([wfif_oauth2_client_attrs](#refs-wfif_oauth2_client_attrs))*
- **description**: *string*
- **display_name**: *string*
- **attribute_condition**: *string*
- **attribute_mapping**: *object*
- **attribute_mapping_template**: *string*
<br>*enum: ['azuread', 'okta']*
- **disabled**: *boolean*
- **identity_provider**: *object*
- **oauth2_client_config**: *object*
<br>*additional properties: false*
- **extended_attributes**: *reference([wfif_oauth2_client_attrs](#refs-wfif_oauth2_client_attrs))*
- **extra_attributes**: *reference([wfif_oauth2_client_attrs](#refs-wfif_oauth2_client_attrs))*
## Definitions

View File

@@ -911,60 +911,60 @@ Auto-population of provider attributes is supported via the `attribute_mapping_t
module "org" {
source = "./fabric/modules/organization"
organization_id = var.organization_id
workforce_identity_config = {
# optional, defaults to 'default'
pool_name = "test-pool"
display_name = "Test Pool"
description = "Workforce pool for testing."
providers = {
saml-basic = {
attribute_mapping_template = "azuread"
identity_provider = {
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
}
saml-full = {
attribute_mapping = {
"google.subject" = "assertion.sub"
}
identity_provider = {
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
oauth2_client_config = {
extra_attributes = {
issuer_uri = "https://login.microsoftonline.com/abcdef/v2.0"
client_id = "client-id"
client_secret = "client-secret"
attributes_type = "AZURE_AD_GROUPS_ID"
query_filter = "mail:gcp"
}
}
}
oidc-full = {
attribute_mapping = {
"google.subject" = "assertion.sub"
}
identity_provider = {
oidc = {
issuer_uri = "https://sts.windows.net/abcd01234/"
client_id = "https://analysis.windows.net/powerbi/connector/GoogleBigQuery"
client_secret = "client-secret"
web_sso_config = {
response_type = "CODE"
assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS"
workforce_identity_pools = {
"test-pool" = {
display_name = "Test Pool"
description = "Workforce pool for testing."
providers = {
saml-basic = {
attribute_mapping_template = "azuread"
identity_provider = {
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
}
oauth2_client_config = {
extra_attributes = {
issuer_uri = "https://login.microsoftonline.com/abcd01234/v2.0"
client_id = "client-id"
client_secret = "client-secret"
attributes_type = "AZURE_AD_GROUPS_MAIL"
saml-full = {
attribute_mapping = {
"google.subject" = "assertion.sub"
}
identity_provider = {
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
oauth2_client_config = {
extra_attributes = {
issuer_uri = "https://login.microsoftonline.com/abcdef/v2.0"
client_id = "client-id"
client_secret = "client-secret"
attributes_type = "AZURE_AD_GROUPS_ID"
query_filter = "mail:gcp"
}
}
}
oidc-full = {
attribute_mapping = {
"google.subject" = "assertion.sub"
}
identity_provider = {
oidc = {
issuer_uri = "https://sts.windows.net/abcd01234/"
client_id = "https://analysis.windows.net/powerbi/connector/GoogleBigQuery"
client_secret = "client-secret"
web_sso_config = {
response_type = "CODE"
assertion_claims_behavior = "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS"
}
}
}
oauth2_client_config = {
extra_attributes = {
issuer_uri = "https://login.microsoftonline.com/abcd01234/v2.0"
client_id = "client-id"
client_secret = "client-secret"
attributes_type = "AZURE_AD_GROUPS_MAIL"
}
}
}
}
@@ -1033,7 +1033,7 @@ module "org" {
| [tag_bindings](variables-tags.tf#L89) | Tag bindings for this organization, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [tags](variables-tags.tf#L96) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tags_config](variables-tags.tf#L161) | Fine-grained control on tag resource and IAM creation. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [workforce_identity_config](variables-identity-providers.tf#L17) | Workforce Identity Federation pool and providers. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [workforce_identity_pools](variables-identity-providers.tf#L17) | Workforce Identity Federation pools and providers. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs
@@ -1055,6 +1055,7 @@ module "org" {
| [sink_writer_identities](outputs.tf#L125) | Writer identities created for each sink. | |
| [tag_keys](outputs.tf#L133) | Tag key resources. | |
| [tag_values](outputs.tf#L142) | Tag value resources. | |
| [workforce_identity_provider_names](outputs.tf#L150) | Workforce Identity provider names. | |
| [workforce_identity_providers](outputs.tf#L157) | Workforce Identity provider attributes. | |
| [workforce_identity_pool_ids](outputs.tf#L150) | Workforce identity pool ids. | |
| [workforce_identity_provider_names](outputs.tf#L157) | Workforce Identity provider names. | |
| [workforce_identity_providers](outputs.tf#L164) | Workforce Identity provider attributes. | |
<!-- END TFDOC -->

View File

@@ -35,23 +35,32 @@ locals {
"attribute.user_email" = "assertion.attributes.email[0]"
}
}
wfif_providers = merge([
for k, v in var.workforce_identity_pools : {
for pk, pv in v.providers : "${k}/${pk}" => merge(pv, {
provider_id = pk
pool = k
})
}
]...)
}
resource "google_iam_workforce_pool" "default" {
count = var.workforce_identity_config == null ? 0 : 1
for_each = var.workforce_identity_pools
parent = var.organization_id
location = "global"
workforce_pool_id = var.workforce_identity_config.pool_name
description = var.workforce_identity_config.description
disabled = var.workforce_identity_config.disabled
display_name = var.workforce_identity_config.display_name
session_duration = var.workforce_identity_config.session_duration
workforce_pool_id = each.key
description = each.value.description
disabled = each.value.disabled
display_name = each.value.display_name
session_duration = each.value.session_duration
dynamic "access_restrictions" {
for_each = var.workforce_identity_config.access_restrictions != null ? [""] : []
for_each = each.value.access_restrictions != null ? [""] : []
content {
disable_programmatic_signin = var.workforce_identity_config.access_restrictions.disable_programmatic_signin
disable_programmatic_signin = each.value.access_restrictions.disable_programmatic_signin
dynamic "allowed_services" {
for_each = coalesce(var.workforce_identity_config.access_restrictions.allowed_services, [])
for_each = coalesce(each.value.access_restrictions.allowed_services, [])
content {
domain = allowed_services.value.domain
}
@@ -61,8 +70,8 @@ resource "google_iam_workforce_pool" "default" {
}
resource "google_iam_workforce_pool_provider" "default" {
for_each = try(var.workforce_identity_config.providers, {})
provider_id = each.key
for_each = local.wfif_providers
provider_id = each.value.provider_id
attribute_condition = each.value.attribute_condition
description = each.value.description
disabled = each.value.disabled
@@ -71,8 +80,10 @@ resource "google_iam_workforce_pool_provider" "default" {
try(local.wfif_attribute_mappings[each.value.attribute_mapping_template], {}),
each.value.attribute_mapping
)
location = google_iam_workforce_pool.default[0].location
workforce_pool_id = google_iam_workforce_pool.default[0].workforce_pool_id
location = "global"
workforce_pool_id = (
google_iam_workforce_pool.default[each.value.pool].workforce_pool_id
)
dynamic "saml" {
for_each = each.value.identity_provider.saml == null ? [] : [""]
content {

View File

@@ -147,6 +147,13 @@ output "tag_values" {
}
}
output "workforce_identity_pool_ids" {
description = "Workforce identity pool ids."
value = {
for k, v in google_iam_workforce_pool.default : k => v.name
}
}
output "workforce_identity_provider_names" {
description = "Workforce Identity provider names."
value = {
@@ -157,9 +164,9 @@ output "workforce_identity_provider_names" {
output "workforce_identity_providers" {
description = "Workforce Identity provider attributes."
value = {
for k, v in google_iam_workforce_pool_provider.default : k => {
name = v.name
pool = try(google_iam_workforce_pool.default[0].name, null)
for k, v in local.wfif_providers : k => {
name = google_iam_workforce_pool_provider.default[k].name
pool = google_iam_workforce_pool.default[v.pool].name
}
}
}

View File

@@ -14,10 +14,9 @@
* limitations under the License.
*/
variable "workforce_identity_config" {
description = "Workforce Identity Federation pool and providers."
type = object({
pool_name = optional(string, "default")
variable "workforce_identity_pools" {
description = "Workforce Identity Federation pools and providers."
type = map(object({
description = optional(string)
disabled = optional(bool)
display_name = optional(string)
@@ -69,12 +68,12 @@ variable "workforce_identity_config" {
}))
}), {})
})), {})
})
nullable = true
default = null
}))
nullable = false
default = {}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : contains(
for v in try(var.workforce_identity_pools.providers, {}) : contains(
["azuread", "okta"],
coalesce(v.attribute_mapping_template, "azuread")
)
@@ -83,7 +82,7 @@ variable "workforce_identity_config" {
}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : (
for v in try(var.workforce_identity_pools.providers, {}) : (
(try(v.identity_provider.oidc, null) == null ? 0 : 1) +
(try(v.identity_provider.saml, null) == null ? 0 : 1)
) == 1
@@ -92,7 +91,7 @@ variable "workforce_identity_config" {
}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : contains(
for v in try(var.workforce_identity_pools.providers, {}) : contains(
["CODE", "ID_TOKEN"],
coalesce(try(
v.identity_provider.oidc.web_sso_config.response_type, null
@@ -103,7 +102,7 @@ variable "workforce_identity_config" {
}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : contains(
for v in try(var.workforce_identity_pools.providers, {}) : contains(
["MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS", "ONLY_ID_TOKEN_CLAIMS"],
coalesce(try(
v.identity_provider.oidc.web_sso_config.assertion_claims_behavior, null
@@ -114,7 +113,7 @@ variable "workforce_identity_config" {
}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : contains(
for v in try(var.workforce_identity_pools.providers, {}) : contains(
["AZURE_AD_GROUPS_MAIL", "AZURE_AD_GROUPS_ID"],
coalesce(try(
v.oauth2_client_config.extended_attributes.attributes_type, null
@@ -125,7 +124,7 @@ variable "workforce_identity_config" {
}
validation {
condition = alltrue([
for v in try(var.workforce_identity_config.providers, {}) : contains(
for v in try(var.workforce_identity_pools.providers, {}) : contains(
["AZURE_AD_GROUPS_MAIL", "AZURE_AD_GROUPS_ID"],
coalesce(try(
v.oauth2_client_config.extra_attributes.attributes_type, null

View File

@@ -13,7 +13,7 @@
# limitations under the License.
values:
module.org.google_iam_workforce_pool.default[0]:
module.org.google_iam_workforce_pool.default["test-pool"]:
access_restrictions: []
description: Workforce pool for testing.
disabled: null
@@ -23,11 +23,12 @@ values:
session_duration: 3600s
timeouts: null
workforce_pool_id: test-pool
module.org.google_iam_workforce_pool_provider.default["oidc-full"]:
module.org.google_iam_workforce_pool_provider.default["test-pool/oidc-full"]:
attribute_condition: null
attribute_mapping:
google.subject: assertion.sub
description: null
detailed_audit_logging: null
disabled: false
display_name: null
extended_attributes_oauth2_client: []
@@ -53,9 +54,10 @@ values:
response_type: CODE
provider_id: oidc-full
saml: []
scim_usage: null
timeouts: null
workforce_pool_id: test-pool
module.org.google_iam_workforce_pool_provider.default["saml-basic"]:
module.org.google_iam_workforce_pool_provider.default["test-pool/saml-basic"]:
attribute_condition: null
attribute_mapping:
attribute.first_name: assertion.attributes.givenname[0]
@@ -65,6 +67,7 @@ values:
google.groups: assertion.attributes.groups
google.subject: assertion.subject
description: null
detailed_audit_logging: null
disabled: false
display_name: null
extended_attributes_oauth2_client: []
@@ -74,13 +77,15 @@ values:
provider_id: saml-basic
saml:
- idp_metadata_xml: <?xml version="1.0" encoding="utf-8"?>...
scim_usage: null
timeouts: null
workforce_pool_id: test-pool
module.org.google_iam_workforce_pool_provider.default["saml-full"]:
module.org.google_iam_workforce_pool_provider.default["test-pool/saml-full"]:
attribute_condition: null
attribute_mapping:
google.subject: assertion.sub
description: null
detailed_audit_logging: null
disabled: false
display_name: null
extended_attributes_oauth2_client: []
@@ -98,6 +103,7 @@ values:
provider_id: saml-full
saml:
- idp_metadata_xml: <?xml version="1.0" encoding="utf-8"?>...
scim_usage: null
timeouts: null
workforce_pool_id: test-pool