docs(organization): document external IAM management for logging sinks at scale (#3746)
* docs(organization): document external IAM management for logging sinks at scale * Update TOC --------- Co-authored-by: Julio Castillo <jccb@google.com>
This commit is contained in:
@@ -26,6 +26,7 @@ To manage organization policies, the `orgpolicy.googleapis.com` service should b
|
||||
- [Privileged Access Manager (PAM) Entitlements Factory](#privileged-access-manager-pam-entitlements-factory)
|
||||
- [Hierarchical Firewall Policy Attachments](#hierarchical-firewall-policy-attachments)
|
||||
- [Log Sinks](#log-sinks)
|
||||
- [Externally Managing IAM for Log Sinks](#externally-managing-iam-for-log-sinks)
|
||||
- [Data Access Logs](#data-access-logs)
|
||||
- [Custom Roles](#custom-roles)
|
||||
- [Custom Roles Factory](#custom-roles-factory)
|
||||
@@ -447,6 +448,68 @@ module "org" {
|
||||
# tftest inventory=logging.yaml
|
||||
```
|
||||
|
||||
### Externally Managing IAM for Log Sinks
|
||||
|
||||
By default the module creates one conditional IAM binding per sink for `roles/logging.bucketWriter` on the destination project. GCP enforces a hard limit of [20 conditional bindings per role and principal](https://cloud.google.com/iam/docs/conditions-overview#limitations) on a single resource. If you route many sinks to the same destination project, you will hit this limit.
|
||||
|
||||
Set `iam = false` on the affected sinks and manage the IAM binding externally, consolidating multiple destinations into fewer bindings using an OR'd CEL condition expression (max 12 logical operators per condition).
|
||||
|
||||
```hcl
|
||||
module "log-bucket-0" {
|
||||
source = "./fabric/modules/logging-bucket"
|
||||
parent = var.project_id
|
||||
name = "audit-0"
|
||||
}
|
||||
|
||||
module "log-bucket-1" {
|
||||
source = "./fabric/modules/logging-bucket"
|
||||
parent = var.project_id
|
||||
name = "audit-1"
|
||||
}
|
||||
|
||||
module "org" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.organization_id
|
||||
logging_sinks = {
|
||||
audit-0 = {
|
||||
destination = module.log-bucket-0.id
|
||||
filter = "severity=NOTICE"
|
||||
type = "logging"
|
||||
iam = false
|
||||
}
|
||||
audit-1 = {
|
||||
destination = module.log-bucket-1.id
|
||||
filter = "severity=WARNING"
|
||||
type = "logging"
|
||||
iam = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "log-bucket-writer" {
|
||||
project = var.project_id
|
||||
role = "roles/logging.bucketWriter"
|
||||
member = module.org.sink_writer_identities["audit-0"]
|
||||
condition {
|
||||
title = "log_bucket_writer"
|
||||
description = "Grants bucketWriter for audit-0, audit-1."
|
||||
expression = join(" || ", [
|
||||
"resource.name.endsWith('${module.log-bucket-0.id}')",
|
||||
"resource.name.endsWith('${module.log-bucket-1.id}')",
|
||||
# add up to 11 more
|
||||
])
|
||||
}
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
# tftest inventory=logging-iam-external.yaml
|
||||
```
|
||||
|
||||
When you exceed 13 sinks per binding, use Terraform's `chunklist()` with `for_each` to generate multiple `google_project_iam_member` resources automatically.
|
||||
|
||||
For production-scale deployments or strict per-sink isolation, consider using [user-managed service accounts for log routing](https://cloud.google.com/logging/docs/routing/user-managed-service-accounts) instead of the default shared writer identity. This removes the conditional binding limit entirely and provides per-sink auditability.
|
||||
|
||||
## Data Access Logs
|
||||
|
||||
Activation of data access logs can be controlled via the `logging_data_access` variable.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright 2025 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.
|
||||
|
||||
values:
|
||||
module.log-bucket-0.google_logging_project_bucket_config.bucket[0]:
|
||||
bucket_id: audit-0
|
||||
location: global
|
||||
project: project-id
|
||||
retention_days: 30
|
||||
module.log-bucket-1.google_logging_project_bucket_config.bucket[0]:
|
||||
bucket_id: audit-1
|
||||
location: global
|
||||
project: project-id
|
||||
retention_days: 30
|
||||
module.org.google_logging_organization_sink.sink["audit-0"]:
|
||||
filter: severity=NOTICE
|
||||
include_children: true
|
||||
name: audit-0
|
||||
org_id: '1122334455'
|
||||
module.org.google_logging_organization_sink.sink["audit-1"]:
|
||||
filter: severity=WARNING
|
||||
include_children: true
|
||||
name: audit-1
|
||||
org_id: '1122334455'
|
||||
google_project_iam_member.log-bucket-writer:
|
||||
project: project-id
|
||||
role: roles/logging.bucketWriter
|
||||
condition:
|
||||
- title: log_bucket_writer
|
||||
|
||||
counts:
|
||||
google_logging_organization_sink: 2
|
||||
google_logging_project_bucket_config: 2
|
||||
google_project_iam_member: 1
|
||||
modules: 3
|
||||
resources: 5
|
||||
Reference in New Issue
Block a user