Add IAM deny policies support (#3970)
* Added IAM denial policies * Moved default to empty, removed trys, added condition vars to expression * remove redundant null checks * reduce line length * boilerplate and principal context expansion * update readmes * add explicit validation against null values * add context tests * Add missing license headers to examples --------- Co-authored-by: Julio Castillo <jccb@google.com>
This commit is contained in:
@@ -137,3 +137,21 @@ tag_bindings = {
|
||||
baz = "$tag_values:test/one"
|
||||
foo = "$${projects[\"test-00\"].test}/cc-123"
|
||||
}
|
||||
|
||||
iam_deny_policies = {
|
||||
test-policy = {
|
||||
display_name = "Test Deny Policy"
|
||||
rules = [
|
||||
{
|
||||
description = "Test Rule"
|
||||
denied_principals = ["$iam_principals:myuser"]
|
||||
denied_permissions = ["compute.googleapis.com/instances.create"]
|
||||
exception_principals = ["$iam_principals:mygroup"]
|
||||
denial_condition = {
|
||||
title = "Test Condition"
|
||||
expression = "resource.matchTag('$${organization.id}/environment', 'development')"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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,
|
||||
@@ -96,6 +96,25 @@ values:
|
||||
condition: []
|
||||
member: user:test-user@example.com
|
||||
role: organizations/366118655033/roles/myRoleTwo
|
||||
google_iam_deny_policy.default["test-policy"]:
|
||||
display_name: Test Deny Policy
|
||||
name: test-policy
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: null
|
||||
expression: resource.matchTag('1234567890/environment', 'development')
|
||||
location: null
|
||||
title: Test Condition
|
||||
denied_permissions:
|
||||
- compute.googleapis.com/instances.create
|
||||
denied_principals:
|
||||
- user:test-user@example.com
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- group:test-group@example.com
|
||||
description: Test Rule
|
||||
timeouts: null
|
||||
google_logging_folder_settings.default[0]:
|
||||
kms_key_name: projects/test-kms-0/locations/europe-west8/keyRings/test/cryptoKeys/test
|
||||
timeouts: null
|
||||
@@ -162,10 +181,22 @@ counts:
|
||||
google_folder_iam_audit_config: 1
|
||||
google_folder_iam_binding: 7
|
||||
google_folder_iam_member: 1
|
||||
google_iam_deny_policy: 1
|
||||
google_logging_folder_settings: 1
|
||||
google_logging_folder_sink: 1
|
||||
google_privileged_access_manager_entitlement: 1
|
||||
google_pubsub_topic_iam_member: 1
|
||||
google_tags_tag_binding: 3
|
||||
modules: 0
|
||||
resources: 19
|
||||
resources: 20
|
||||
|
||||
outputs:
|
||||
asset_search_results: {}
|
||||
assured_workload: null
|
||||
folder: __missing__
|
||||
id: __missing__
|
||||
name: Test Context
|
||||
organization_policies_ids: {}
|
||||
scc_custom_sha_modules_ids: {}
|
||||
service_agents: {}
|
||||
sink_writer_identities: __missing__
|
||||
|
||||
63
tests/modules/folder/examples/iam-deny-policies.yaml
Normal file
63
tests/modules/folder/examples/iam-deny-policies.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
#
|
||||
# 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.folder.google_folder.folder[0]:
|
||||
deletion_protection: false
|
||||
display_name: Folder name
|
||||
parent: folders/1122334455
|
||||
tags: null
|
||||
timeouts: null
|
||||
module.folder.google_iam_deny_policy.default["conditional-delete-deny"]:
|
||||
display_name: Conditional instance deletion deny
|
||||
name: conditional-delete-deny
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: Prevent deletion of instances tagged as production.
|
||||
expression: resource.matchTag('123456789012/environment', 'prod')
|
||||
location: null
|
||||
title: prevent_prod_deletion
|
||||
denied_permissions:
|
||||
- compute.googleapis.com/instances.delete
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals: []
|
||||
description: Deny deletion of compute instances based on resource tags.
|
||||
timeouts: null
|
||||
module.folder.google_iam_deny_policy.default["prevent-key-creation"]:
|
||||
display_name: Prevent SA key creation
|
||||
name: prevent-key-creation
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition: []
|
||||
denied_permissions:
|
||||
- iam.googleapis.com/serviceAccountKeys.create
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- principalSet://goog/group/gcp-folder-admins@example.com
|
||||
description: Deny service account key creation to all except the folder admin
|
||||
group.
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_folder: 1
|
||||
google_iam_deny_policy: 2
|
||||
modules: 1
|
||||
resources: 3
|
||||
|
||||
outputs: {}
|
||||
@@ -208,3 +208,21 @@ tags = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iam_deny_policies = {
|
||||
test-policy = {
|
||||
display_name = "Test Deny Policy"
|
||||
rules = [
|
||||
{
|
||||
description = "Test Rule"
|
||||
denied_principals = ["$iam_principals:myuser"]
|
||||
denied_permissions = ["compute.googleapis.com/instances.create"]
|
||||
exception_principals = ["$iam_principals:mygroup"]
|
||||
denial_condition = {
|
||||
title = "Test Condition"
|
||||
expression = "resource.matchTag('$${organization.id}/environment', 'development')"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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:
|
||||
feed_output_config:
|
||||
- pubsub_destination:
|
||||
- topic: projects/test-prod-audit-logs-0/topics/audit-logs
|
||||
org_id: "1234567890"
|
||||
org_id: '1234567890'
|
||||
timeouts: null
|
||||
google_essential_contacts_contact.contact["$email_addresses:default"]:
|
||||
email: foo@example.com
|
||||
@@ -37,6 +37,26 @@ values:
|
||||
- ALL
|
||||
parent: organizations/1234567890
|
||||
timeouts: null
|
||||
google_iam_deny_policy.default["test-policy"]:
|
||||
display_name: Test Deny Policy
|
||||
name: test-policy
|
||||
parent: cloudresourcemanager.googleapis.com%2Forganizations%2F1234567890
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: null
|
||||
expression: resource.matchTag('1234567890/environment', 'development')
|
||||
location: null
|
||||
title: Test Condition
|
||||
denied_permissions:
|
||||
- compute.googleapis.com/instances.create
|
||||
denied_principals:
|
||||
- user:test-user@example.com
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- group:test-group@example.com
|
||||
description: Test Rule
|
||||
timeouts: null
|
||||
google_logging_organization_settings.default[0]:
|
||||
kms_key_name: projects/test-kms-0/locations/europe-west8/keyRings/test/cryptoKeys/test
|
||||
organization: '1234567890'
|
||||
@@ -266,6 +286,7 @@ counts:
|
||||
google_bigquery_dataset_iam_member: 1
|
||||
google_cloud_asset_organization_feed: 1
|
||||
google_essential_contacts_contact: 1
|
||||
google_iam_deny_policy: 1
|
||||
google_logging_organization_settings: 1
|
||||
google_logging_organization_sink: 5
|
||||
google_organization_iam_audit_config: 1
|
||||
@@ -281,4 +302,27 @@ counts:
|
||||
google_tags_tag_value_iam_binding: 2
|
||||
google_tags_tag_value_iam_member: 1
|
||||
modules: 0
|
||||
resources: 30
|
||||
resources: 31
|
||||
|
||||
outputs:
|
||||
asset_search_results: {}
|
||||
custom_constraint_ids: {}
|
||||
custom_role_id: {}
|
||||
custom_roles: {}
|
||||
id: organizations/1234567890
|
||||
logging_identities: __missing__
|
||||
logging_sinks: __missing__
|
||||
network_tag_keys: {}
|
||||
network_tag_values: {}
|
||||
organization_id: organizations/1234567890
|
||||
organization_policies_ids: {}
|
||||
scc_custom_sha_modules_ids: {}
|
||||
scc_mute_configs: {}
|
||||
scim_tenants: {}
|
||||
service_agents: {}
|
||||
sink_writer_identities: __missing__
|
||||
tag_keys: {}
|
||||
tag_values: {}
|
||||
workforce_identity_pool_ids: {}
|
||||
workforce_identity_provider_names: {}
|
||||
workforce_identity_providers: {}
|
||||
|
||||
58
tests/modules/organization/examples/iam-deny-policies.yaml
Normal file
58
tests/modules/organization/examples/iam-deny-policies.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
# 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
|
||||
#
|
||||
# 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.organization.google_iam_deny_policy.default["conditional-key-deny"]:
|
||||
display_name: Conditional SA Key Deny
|
||||
name: conditional-key-deny
|
||||
parent: cloudresourcemanager.googleapis.com%2Forganizations%2F1122334455
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: Restrict access to specific IP ranges
|
||||
expression: '!inIpRange(request.auth.access_levels, ''accessPolicies/123456789/accessLevels/trusted_ips'')'
|
||||
location: null
|
||||
title: ip-restriction
|
||||
denied_permissions:
|
||||
- iam.serviceAccountKeys.create
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals: []
|
||||
description: Deny key creation outside of authorized IPs using a condition.
|
||||
timeouts: null
|
||||
module.organization.google_iam_deny_policy.default["prevent-sa-token-creation"]:
|
||||
display_name: Prevent SA token creation
|
||||
name: prevent-sa-token-creation
|
||||
parent: cloudresourcemanager.googleapis.com%2Forganizations%2F1122334455
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition: []
|
||||
denied_permissions:
|
||||
- iam.serviceAccounts.getAccessToken
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- principalSet://goog/group/gcp-admins@example.com
|
||||
description: Deny service account token creation to all except the central admin
|
||||
group.
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_iam_deny_policy: 2
|
||||
modules: 1
|
||||
resources: 2
|
||||
|
||||
outputs: {}
|
||||
@@ -254,3 +254,21 @@ tags = {
|
||||
vpc_sc = {
|
||||
perimeter_name = "$vpc_sc_perimeters:default"
|
||||
}
|
||||
|
||||
iam_deny_policies = {
|
||||
test-policy = {
|
||||
display_name = "Test Deny Policy"
|
||||
rules = [
|
||||
{
|
||||
description = "Test Rule"
|
||||
denied_principals = ["$iam_principals:myuser"]
|
||||
denied_permissions = ["compute.googleapis.com/instances.create"]
|
||||
exception_principals = ["$iam_principals:mygroup"]
|
||||
denial_condition = {
|
||||
title = "Test Condition"
|
||||
expression = "resource.matchTag('$${organization.id}/environment', 'development')"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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,
|
||||
@@ -29,7 +29,6 @@ values:
|
||||
project: my-project
|
||||
timeouts: null
|
||||
google_compute_shared_vpc_service_project.shared_vpc_service[0]:
|
||||
deletion_policy: null
|
||||
host_project: test-vpc-host
|
||||
service_project: my-project
|
||||
timeouts: null
|
||||
@@ -40,6 +39,26 @@ values:
|
||||
- ALL
|
||||
parent: projects/my-project
|
||||
timeouts: null
|
||||
google_iam_deny_policy.default["test-policy"]:
|
||||
display_name: Test Deny Policy
|
||||
name: test-policy
|
||||
parent: cloudresourcemanager.googleapis.com%2Fprojects%2Fmy-project
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: null
|
||||
expression: resource.matchTag('1234567890/environment', 'development')
|
||||
location: null
|
||||
title: Test Condition
|
||||
denied_permissions:
|
||||
- compute.googleapis.com/instances.create
|
||||
denied_principals:
|
||||
- user:test-user@example.com
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- group:test-group@example.com
|
||||
description: Test Rule
|
||||
timeouts: null
|
||||
google_kms_crypto_key_iam_member.service_agent_cmek["key-0.compute-system"]:
|
||||
condition: []
|
||||
crypto_key_id: projects/kms-central-prj/locations/europe-west1/keyRings/my-keyring/cryptoKeys/ew1-compute
|
||||
@@ -156,7 +175,6 @@ values:
|
||||
google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: null
|
||||
deletion_policy: DELETE
|
||||
effective_labels:
|
||||
goog-terraform-provisioned: 'true'
|
||||
folder_id: '6789012345'
|
||||
@@ -323,6 +341,7 @@ counts:
|
||||
google_cloud_asset_project_feed: 1
|
||||
google_compute_shared_vpc_service_project: 1
|
||||
google_essential_contacts_contact: 1
|
||||
google_iam_deny_policy: 1
|
||||
google_kms_crypto_key_iam_member: 1
|
||||
google_logging_metric: 1
|
||||
google_logging_project_sink: 1
|
||||
@@ -341,4 +360,36 @@ counts:
|
||||
google_tags_tag_value_iam_binding: 2
|
||||
google_tags_tag_value_iam_member: 1
|
||||
modules: 0
|
||||
resources: 38
|
||||
resources: 39
|
||||
|
||||
outputs:
|
||||
alert_ids: __missing__
|
||||
asset_search_results: {}
|
||||
bigquery_reservations:
|
||||
assignments: {}
|
||||
reservations: {}
|
||||
custom_role_id: {}
|
||||
custom_roles: {}
|
||||
default_service_accounts: __missing__
|
||||
id: my-project
|
||||
kms_autokeys: {}
|
||||
name: my-project
|
||||
network_tag_keys: {}
|
||||
network_tag_values: {}
|
||||
notification_channel_names: __missing__
|
||||
notification_channels: __missing__
|
||||
number: __missing__
|
||||
organization_policies_ids: {}
|
||||
project_id: my-project
|
||||
quota_configs: {}
|
||||
quotas: {}
|
||||
scc_custom_sha_modules_ids: {}
|
||||
service_agents: __missing__
|
||||
services:
|
||||
- compute.googleapis.com
|
||||
sink_writer_identities: __missing__
|
||||
tag_keys: {}
|
||||
tag_values: {}
|
||||
workload_identity_pool_ids: {}
|
||||
workload_identity_provider_ids: {}
|
||||
workload_identity_providers: {}
|
||||
|
||||
73
tests/modules/project/examples/iam-deny-policies.yaml
Normal file
73
tests/modules/project/examples/iam-deny-policies.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
# 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
|
||||
#
|
||||
# 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.project.google_iam_deny_policy.default["prevent-core-bucket-deletion"]:
|
||||
display_name: Prevent core bucket deletion
|
||||
name: prevent-core-bucket-deletion
|
||||
parent: cloudresourcemanager.googleapis.com%2Fprojects%2Fmy-project
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition:
|
||||
- description: Applies only to buckets starting with 'core-'.
|
||||
expression: resource.name.startsWith("projects/-/buckets/core-")
|
||||
location: null
|
||||
title: core_buckets_only
|
||||
denied_permissions:
|
||||
- storage.googleapis.com/buckets.delete
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals: []
|
||||
description: Deny deletion of any Cloud Storage bucket with the 'core-' prefix.
|
||||
timeouts: null
|
||||
module.project.google_iam_deny_policy.default["prevent-kms-destruction"]:
|
||||
display_name: Prevent KMS Key destruction
|
||||
name: prevent-kms-destruction
|
||||
parent: cloudresourcemanager.googleapis.com%2Fprojects%2Fmy-project
|
||||
rules:
|
||||
- deny_rule:
|
||||
- denial_condition: []
|
||||
denied_permissions:
|
||||
- cloudkms.googleapis.com/cryptoKeyVersions.destroy
|
||||
denied_principals:
|
||||
- principalSet://goog/public:all
|
||||
exception_permissions: []
|
||||
exception_principals:
|
||||
- principalSet://goog/group/gcp-kms-admins@example.com
|
||||
description: Deny destroying KMS key versions to all except the key admins group.
|
||||
timeouts: null
|
||||
module.project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 123456-123456-123456
|
||||
deletion_policy: DELETE
|
||||
effective_labels:
|
||||
goog-terraform-provisioned: 'true'
|
||||
folder_id: '1122334455'
|
||||
labels: null
|
||||
name: my-project
|
||||
org_id: null
|
||||
project_id: my-project
|
||||
tags: null
|
||||
terraform_labels:
|
||||
goog-terraform-provisioned: 'true'
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_iam_deny_policy: 2
|
||||
google_project: 1
|
||||
modules: 1
|
||||
resources: 3
|
||||
|
||||
outputs: {}
|
||||
Reference in New Issue
Block a user