Add support for SCIM to workforce identity (#3951)
* Add support for SCIM to workforce identity * Update schemas and tests
This commit is contained in:
@@ -424,6 +424,10 @@
|
||||
"disabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"detailed_audit_logging": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"identity_provider": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
@@ -527,6 +531,44 @@
|
||||
"$ref": "#/$defs/wfif_oauth2_client_attrs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scim_tenant": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"claim_mapping"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"claim_mapping": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"default": "global"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"hard_delete": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scim_usage": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ENABLED_FOR_GROUPS"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +124,24 @@
|
||||
- **attribute_mapping_template**: *string*
|
||||
<br>*enum: ['azuread', 'okta']*
|
||||
- **disabled**: *boolean*
|
||||
- **detailed_audit_logging**: *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))*
|
||||
- **scim_tenant**: *object*
|
||||
<br>*additional properties: false*
|
||||
- ⁺**id**: *string*
|
||||
- ⁺**claim_mapping**: *object*
|
||||
<br>*additional properties: string*
|
||||
- **location**: *string*
|
||||
<br>*default: global*
|
||||
- **display_name**: *string*
|
||||
- **description**: *string*
|
||||
- **hard_delete**: *boolean*
|
||||
- **scim_usage**: *string*
|
||||
<br>*enum: ['ENABLED_FOR_GROUPS']*
|
||||
|
||||
## Definitions
|
||||
|
||||
|
||||
@@ -971,6 +971,7 @@ module "org" {
|
||||
}
|
||||
}
|
||||
oidc-full = {
|
||||
scim_usage = "ENABLED_FOR_GROUPS"
|
||||
attribute_mapping = {
|
||||
"google.subject" = "assertion.sub"
|
||||
}
|
||||
@@ -993,12 +994,20 @@ module "org" {
|
||||
attributes_type = "AZURE_AD_GROUPS_MAIL"
|
||||
}
|
||||
}
|
||||
scim_tenant = {
|
||||
id = "my-scim-tenant"
|
||||
display_name = "My SCIM Tenant"
|
||||
claim_mapping = {
|
||||
"google.subject" = "user.externalId"
|
||||
"google.group" = "group.externalId"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=wfif.yaml
|
||||
# tftest inventory=wfif.yaml
|
||||
```
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
@@ -1009,7 +1018,7 @@ module "org" {
|
||||
|---|---|---|
|
||||
| [assets.tf](./assets.tf) | None | <code>google_cloud_asset_organization_feed</code> |
|
||||
| [iam.tf](./iam.tf) | IAM bindings. | <code>google_organization_iam_binding</code> · <code>google_organization_iam_custom_role</code> · <code>google_organization_iam_member</code> |
|
||||
| [identity-providers.tf](./identity-providers.tf) | Workforce Identity Federation provider definitions. | <code>google_iam_workforce_pool</code> · <code>google_iam_workforce_pool_provider</code> |
|
||||
| [identity-providers.tf](./identity-providers.tf) | Workforce Identity Federation provider definitions. | <code>google_iam_workforce_pool</code> · <code>google_iam_workforce_pool_provider</code> · <code>google_iam_workforce_pool_provider_scim_tenant</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and data access logs. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_organization_exclusion</code> · <code>google_logging_organization_settings</code> · <code>google_logging_organization_sink</code> · <code>google_organization_iam_audit_config</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_firewall_policy_association</code> · <code>google_essential_contacts_contact</code> |
|
||||
| [org-policy-custom-constraints.tf](./org-policy-custom-constraints.tf) | None | <code>google_org_policy_custom_constraint</code> |
|
||||
@@ -1080,11 +1089,12 @@ module "org" {
|
||||
| [organization_policies_ids](outputs.tf#L113) | Map of ORGANIZATION_POLICIES => ID in the organization. | |
|
||||
| [scc_custom_sha_modules_ids](outputs.tf#L118) | Map of SCC CUSTOM SHA MODULES => ID in the organization. | |
|
||||
| [scc_mute_configs](outputs.tf#L123) | SCC mute configurations. | |
|
||||
| [service_agents](outputs.tf#L128) | Identities of all organization-level service agents. | |
|
||||
| [sink_writer_identities](outputs.tf#L136) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L144) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L153) | Tag value resources. | |
|
||||
| [workforce_identity_pool_ids](outputs.tf#L161) | Workforce identity pool ids. | |
|
||||
| [workforce_identity_provider_names](outputs.tf#L168) | Workforce Identity provider names. | |
|
||||
| [workforce_identity_providers](outputs.tf#L175) | Workforce Identity provider attributes. | |
|
||||
| [scim_tenants](outputs.tf#L128) | Workforce Identity provider SCIM tenants. | |
|
||||
| [service_agents](outputs.tf#L142) | Identities of all organization-level service agents. | |
|
||||
| [sink_writer_identities](outputs.tf#L150) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L158) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L167) | Tag value resources. | |
|
||||
| [workforce_identity_pool_ids](outputs.tf#L175) | Workforce identity pool ids. | |
|
||||
| [workforce_identity_provider_names](outputs.tf#L182) | Workforce Identity provider names. | |
|
||||
| [workforce_identity_providers](outputs.tf#L189) | Workforce Identity provider attributes. | |
|
||||
<!-- END TFDOC -->
|
||||
|
||||
@@ -44,6 +44,14 @@ locals {
|
||||
})
|
||||
}
|
||||
]...)
|
||||
|
||||
wfif_scim_tenants = {
|
||||
for k, v in local.wfif_providers : k => merge(v.scim_tenant, {
|
||||
provider_id = v.provider_id
|
||||
pool = v.pool
|
||||
})
|
||||
if try(v.scim_tenant, null) != null
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_iam_workforce_pool" "default" {
|
||||
@@ -70,12 +78,14 @@ resource "google_iam_workforce_pool" "default" {
|
||||
}
|
||||
|
||||
resource "google_iam_workforce_pool_provider" "default" {
|
||||
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
|
||||
display_name = each.value.display_name
|
||||
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
|
||||
detailed_audit_logging = each.value.detailed_audit_logging
|
||||
display_name = each.value.display_name
|
||||
scim_usage = each.value.scim_usage
|
||||
attribute_mapping = merge(
|
||||
try(local.wfif_attribute_mappings[each.value.attribute_mapping_template], {}),
|
||||
each.value.attribute_mapping
|
||||
@@ -193,3 +203,15 @@ resource "google_iam_workforce_pool_provider" "default" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_iam_workforce_pool_provider_scim_tenant" "default" {
|
||||
for_each = local.wfif_scim_tenants
|
||||
location = each.value.location
|
||||
workforce_pool_id = google_iam_workforce_pool.default[each.value.pool].workforce_pool_id
|
||||
provider_id = google_iam_workforce_pool_provider.default[each.key].provider_id
|
||||
scim_tenant_id = each.value.id
|
||||
display_name = each.value.display_name
|
||||
description = each.value.description
|
||||
claim_mapping = each.value.claim_mapping
|
||||
hard_delete = each.value.hard_delete
|
||||
}
|
||||
|
||||
@@ -125,6 +125,20 @@ output "scc_mute_configs" {
|
||||
value = google_scc_v2_organization_mute_config.scc_mute_configs
|
||||
}
|
||||
|
||||
output "scim_tenants" {
|
||||
description = "Workforce Identity provider SCIM tenants."
|
||||
value = {
|
||||
for k, v in google_iam_workforce_pool_provider_scim_tenant.default : k => {
|
||||
id = v.id
|
||||
pool = v.workforce_pool_id
|
||||
provider = v.provider_id
|
||||
state = v.state
|
||||
base_uri = v.base_uri
|
||||
service_agent = v.service_agent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "service_agents" {
|
||||
description = "Identities of all organization-level service agents."
|
||||
value = local.service_agents
|
||||
|
||||
@@ -34,6 +34,8 @@ variable "workforce_identity_pools" {
|
||||
attribute_mapping = optional(map(string), {})
|
||||
attribute_mapping_template = optional(string)
|
||||
disabled = optional(bool, false)
|
||||
detailed_audit_logging = optional(bool, false)
|
||||
scim_usage = optional(string)
|
||||
identity_provider = object({
|
||||
oidc = optional(object({
|
||||
issuer_uri = string
|
||||
@@ -67,70 +69,101 @@ variable "workforce_identity_pools" {
|
||||
query_filter = optional(string)
|
||||
}))
|
||||
}), {})
|
||||
scim_tenant = optional(object({
|
||||
id = string
|
||||
claim_mapping = map(string)
|
||||
location = optional(string, "global")
|
||||
display_name = optional(string)
|
||||
description = optional(string)
|
||||
hard_delete = optional(bool)
|
||||
}))
|
||||
})), {})
|
||||
}))
|
||||
nullable = false
|
||||
default = {}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for v in try(var.workforce_identity_pools.providers, {}) : contains(
|
||||
["azuread", "okta"],
|
||||
coalesce(v.attribute_mapping_template, "azuread")
|
||||
)
|
||||
])
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
prov.attribute_mapping_template == null || contains(
|
||||
["azuread", "okta"],
|
||||
prov.attribute_mapping_template
|
||||
)
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Supported mapping templates are: azuread, okta."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
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
|
||||
])
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
(try(prov.identity_provider.oidc, null) == null ? 0 : 1) +
|
||||
(try(prov.identity_provider.saml, null) == null ? 0 : 1)
|
||||
) == 1
|
||||
]
|
||||
]))
|
||||
error_message = "Only one of identity_provider.oidc or identity_provider.saml can be defined."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
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
|
||||
), "CODE")
|
||||
)
|
||||
])
|
||||
error_message = "Invalid OIDC web SSO config response type."
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
try(prov.identity_provider.oidc.web_sso_config.response_type, null) == null || contains(
|
||||
["CODE", "ID_TOKEN"],
|
||||
try(prov.identity_provider.oidc.web_sso_config.response_type, null)
|
||||
)
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Supported OIDC web SSO config response types are: CODE, ID_TOKEN."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
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
|
||||
), "MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS")
|
||||
)
|
||||
])
|
||||
error_message = "Invalid OIDC web SSO config assertion claims behavior."
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
try(prov.identity_provider.oidc.web_sso_config.assertion_claims_behavior, null) == null || contains(
|
||||
["MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS", "ONLY_ID_TOKEN_CLAIMS"],
|
||||
try(prov.identity_provider.oidc.web_sso_config.assertion_claims_behavior, null)
|
||||
)
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Supported OIDC web SSO config assertion claims behaviors are: MERGE_USER_INFO_OVER_ID_TOKEN_CLAIMS, ONLY_ID_TOKEN_CLAIMS."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
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
|
||||
), "AZURE_AD_GROUPS_MAIL")
|
||||
)
|
||||
])
|
||||
error_message = "Invalid AzureAD attribute type in OAuth 2.0 client extended attributes.."
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
try(prov.oauth2_client_config.extended_attributes.attributes_type, null) == null || contains(
|
||||
["AZURE_AD_GROUPS_MAIL", "AZURE_AD_GROUPS_ID"],
|
||||
try(prov.oauth2_client_config.extended_attributes.attributes_type, null)
|
||||
)
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Supported AzureAD attribute types in OAuth 2.0 client extended attributes are: AZURE_AD_GROUPS_MAIL, AZURE_AD_GROUPS_ID."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
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
|
||||
), "AZURE_AD_GROUPS_MAIL")
|
||||
)
|
||||
])
|
||||
error_message = "Invalid AzureAD attribute type in OAuth 2.0 client extra attributes.."
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) : (
|
||||
try(prov.oauth2_client_config.extra_attributes.attributes_type, null) == null || contains(
|
||||
["AZURE_AD_GROUPS_MAIL", "AZURE_AD_GROUPS_ID"],
|
||||
try(prov.oauth2_client_config.extra_attributes.attributes_type, null)
|
||||
)
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Supported AzureAD attribute types in OAuth 2.0 client extra attributes are: AZURE_AD_GROUPS_MAIL, AZURE_AD_GROUPS_ID."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue(flatten([
|
||||
for pool in values(var.workforce_identity_pools) : [
|
||||
for prov in values(pool.providers) :
|
||||
prov.scim_usage == null || prov.scim_usage == "ENABLED_FOR_GROUPS"
|
||||
]
|
||||
]))
|
||||
error_message = "Supported scim_usage values are: ENABLED_FOR_GROUPS."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright 2025 Google LLC
|
||||
# Copyright 2026 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
|
||||
# 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,
|
||||
@@ -28,7 +28,7 @@ values:
|
||||
attribute_mapping:
|
||||
google.subject: assertion.sub
|
||||
description: null
|
||||
detailed_audit_logging: null
|
||||
detailed_audit_logging: false
|
||||
disabled: false
|
||||
display_name: null
|
||||
extended_attributes_oauth2_client: []
|
||||
@@ -54,7 +54,7 @@ values:
|
||||
response_type: CODE
|
||||
provider_id: oidc-full
|
||||
saml: []
|
||||
scim_usage: null
|
||||
scim_usage: ENABLED_FOR_GROUPS
|
||||
timeouts: null
|
||||
workforce_pool_id: test-pool
|
||||
module.org.google_iam_workforce_pool_provider.default["test-pool/saml-basic"]:
|
||||
@@ -67,7 +67,7 @@ values:
|
||||
google.groups: assertion.attributes.groups
|
||||
google.subject: assertion.subject
|
||||
description: null
|
||||
detailed_audit_logging: null
|
||||
detailed_audit_logging: false
|
||||
disabled: false
|
||||
display_name: null
|
||||
extended_attributes_oauth2_client: []
|
||||
@@ -85,7 +85,7 @@ values:
|
||||
attribute_mapping:
|
||||
google.subject: assertion.sub
|
||||
description: null
|
||||
detailed_audit_logging: null
|
||||
detailed_audit_logging: false
|
||||
disabled: false
|
||||
display_name: null
|
||||
extended_attributes_oauth2_client: []
|
||||
@@ -106,9 +106,24 @@ values:
|
||||
scim_usage: null
|
||||
timeouts: null
|
||||
workforce_pool_id: test-pool
|
||||
module.org.google_iam_workforce_pool_provider_scim_tenant.default["test-pool/oidc-full"]:
|
||||
claim_mapping:
|
||||
google.group: group.externalId
|
||||
google.subject: user.externalId
|
||||
description: null
|
||||
display_name: My SCIM Tenant
|
||||
hard_delete: false
|
||||
location: global
|
||||
provider_id: oidc-full
|
||||
scim_tenant_id: my-scim-tenant
|
||||
timeouts: null
|
||||
workforce_pool_id: test-pool
|
||||
|
||||
counts:
|
||||
google_iam_workforce_pool: 1
|
||||
google_iam_workforce_pool_provider: 3
|
||||
google_iam_workforce_pool_provider_scim_tenant: 1
|
||||
modules: 1
|
||||
resources: 4
|
||||
resources: 5
|
||||
|
||||
outputs: {}
|
||||
|
||||
Reference in New Issue
Block a user