Grant IAM rights to service identities in host project (#1542)

* [module/project] Grant IAM rights to service identities based on used services in host project
* [blueprints/factories/project-factory] enable granting IAM permissions in host VPC for service identities directly or by specifying services in use
This commit is contained in:
Wiktor Niesiobędzki
2023-07-29 20:07:21 +02:00
committed by GitHub
parent fdd53624f1
commit 4998f1d376
11 changed files with 283 additions and 55 deletions

View File

@@ -259,6 +259,30 @@ module "service-project" {
# tftest modules=2 resources=8 inventory=shared-vpc.yaml
```
The module allows also granting necessary permissions in host project to service identities by specifying which services will be used in service project in `grant_iam_for_services`.
```hcl
module "host-project" {
source = "./fabric/modules/project"
name = "my-host-project"
shared_vpc_host_config = {
enabled = true
}
}
module "service-project" {
source = "./fabric/modules/project"
name = "my-service-project"
services = [
"container.googleapis.com",
]
shared_vpc_service_config = {
host_project = module.host-project.project_id
service_iam_grants = module.service-project.services
}
}
# tftest modules=2 resources=9 inventory=shared-vpc-auto-grants.yaml
```
## Organization Policies
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
@@ -577,7 +601,6 @@ output "compute_robot" {
```
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
## Files
@@ -631,9 +654,9 @@ output "compute_robot" {
| [service_perimeter_standard](variables.tf#L272) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L278) | Service APIs to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [shared_vpc_host_config](variables.tf#L284) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; service_projects &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L293) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [skip_delete](variables.tf#L303) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [tag_bindings](variables.tf#L309) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [shared_vpc_service_config](variables.tf#L293) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; host_project &#61; null&#10;&#125;">&#123;&#8230;&#125;</code> |
| [skip_delete](variables.tf#L315) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
| [tag_bindings](variables.tf#L321) | Tag bindings for this project, in key => tag value id format. | <code>map&#40;string&#41;</code> | | <code>null</code> |
## Outputs
@@ -645,6 +668,6 @@ output "compute_robot" {
| [number](outputs.tf#L56) | Project number. | |
| [project_id](outputs.tf#L75) | Project id. | |
| [service_accounts](outputs.tf#L94) | Product robot service accounts in project. | |
| [sink_writer_identities](outputs.tf#L110) | Writer identities created for each sink. | |
| [services](outputs.tf#L110) | Service APIs to enabled in the project. | |
| [sink_writer_identities](outputs.tf#L119) | Writer identities created for each sink. | |
<!-- END TFDOC -->

View File

@@ -107,6 +107,15 @@ output "service_accounts" {
]
}
output "services" {
description = "Service APIs to enabled in the project."
value = var.services
depends_on = [
google_project_service.project_services,
google_project_service_identity.jit_si,
]
}
output "sink_writer_identities" {
description = "Writer identities created for each sink."
value = {

View File

@@ -17,15 +17,25 @@
# tfdoc:file:description Shared VPC project-level configuration.
locals {
_shared_vpc_agent_config = yamldecode(file("${path.module}/sharedvpc-agent-iam.yaml"))
_shared_vpc_agent_config_filtered = [
for config in local._shared_vpc_agent_config : config
if contains(var.shared_vpc_service_config.service_iam_grants, config.service)
]
_shared_vpc_agent_grants = flatten(flatten([
for api in local._shared_vpc_agent_config_filtered : [
for service, roles in api.agents : [
for role in roles : { role = role, service = service }
]
]
]))
# compute the host project IAM bindings for this project's service identities
_svpc_service_iam = flatten([
for role, services in local._svpc_service_identity_iam : [
for role, services in var.shared_vpc_service_config.service_identity_iam : [
for service in services : { role = role, service = service }
]
])
_svpc_service_identity_iam = coalesce(
local.svpc_service_config.service_identity_iam, {}
)
svpc_host_config = {
enabled = coalesce(
try(var.shared_vpc_host_config.enabled, null), false
@@ -34,11 +44,9 @@ locals {
try(var.shared_vpc_host_config.service_projects, null), []
)
}
svpc_service_config = coalesce(var.shared_vpc_service_config, {
host_project = null, service_identity_iam = {}
})
svpc_service_iam = {
for b in local._svpc_service_iam : "${b.role}:${b.service}" => b
for b in setunion(local._svpc_service_iam, local._shared_vpc_agent_grants) : "${b.role}:${b.service}" => b
}
}
@@ -59,7 +67,7 @@ resource "google_compute_shared_vpc_service_project" "service_projects" {
resource "google_compute_shared_vpc_service_project" "shared_vpc_service" {
provider = google-beta
count = local.svpc_service_config.host_project != null ? 1 : 0
count = var.shared_vpc_service_config.host_project != null ? 1 : 0
host_project = var.shared_vpc_service_config.host_project
service_project = local.project.project_id
}

View File

@@ -0,0 +1,97 @@
# Copyright 2023 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.
# Cloud Composer
# https://cloud.google.com/composer/docs/how-to/managing/configuring-shared-vpc#edit_permissions_for_the_composer_agent_service_account
- service: composer.googleapis.com
agents:
composer:
- roles/compute.networkUser
- roles/composer.sharedVpcAgent
# Compute Engine
# TODO: identify docs
- service: compute.googleapis.com
agents:
cloudservices:
- roles/compute.networkUser
# Google Kubernetes Engine
# https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-shared-vpc#enabling_and_granting_roles
- service: container.googleapis.com
agents:
container:
- roles/compute.networkUser
- roles/container.hostServiceAgentUser
- roles/compute.securityAdmin # to manage firewall rules
cloudservices:
- roles/compute.networkUser
# Dataflow
# https://cloud.google.com/dataflow/docs/guides/specifying-networks#shared
- service: dataflow.googleapis.com
agents:
dataflow:
- roles/compute.networkUser
# Cloud Data Fusion
# https://cloud.google.com/data-fusion/docs/how-to/create-private-ip#shared-vpc-network_1
- service: datafusion.googleapis.com
agents:
datafusion:
- roles/compute.networkUser
dataproc:
- roles/compute.networkUser
# Dataproc
# https://cloud.google.com/dataproc/docs/concepts/configuring-clusters/network#create_a_cluster_that_uses_a_network_in_another_project
- service: dataproc.googleapis.com
agents:
dataproc:
- roles/compute.networkUser
cloudservices:
- roles/compute.networkUser
# Change Data Capture | Datastream
# https://cloud.google.com/datastream/docs/create-a-private-connectivity-configuration
- service: datastream.googleapis.com
agents:
datastream:
- roles/compute.networkAdmin
# Cloud Functions
# For shared connectors in host project
# https://cloud.google.com/functions/docs/networking/shared-vpc-host-project
- service: cloudfunctions.googleapis.com
agents:
cloudfunctions:
- roles/vpcaccess.user
# Cloud Run
# For shared connectors in host project
# https://cloud.google.com/run/docs/configuring/shared-vpc-host-project
- service: run.googleapis.com
agents:
run:
- roles/vpcaccess.user
# Cloud Run / Cloud Functions
# For connectors in service project
# https://cloud.google.com/functions/docs/networking/shared-vpc-service-projects#grant-permissions
- service: vpcaccess.googleapis.com
agents:
vpcaccess:
- roles/compute.networkUser
cloudservices:
- roles/compute.networkUser

View File

@@ -292,12 +292,24 @@ variable "shared_vpc_host_config" {
variable "shared_vpc_service_config" {
description = "Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config)."
# the list of valid service identities is in service-accounts.tf
# the list of valid service identities is in service-agents.yaml
type = object({
host_project = string
service_identity_iam = optional(map(list(string)))
service_identity_iam = optional(map(list(string)), {})
service_iam_grants = optional(list(string), [])
})
default = null
default = {
host_project = null
}
nullable = false
validation {
condition = var.shared_vpc_service_config.host_project != null || (
var.shared_vpc_service_config.host_project == null &&
length(var.shared_vpc_service_config.service_iam_grants) == 0 &&
length(var.shared_vpc_service_config.service_iam_grants) == 0
)
error_message = "You need to provide host_project when providing service_identity_iam or service_iam_grants"
}
}
variable "skip_delete" {