Skip to content

Commit e0fd03a

Browse files
feat: Fleet app operator permissions (#1986)
Co-authored-by: Andrew Peabody <andrewpeabody@google.com>
1 parent 2834461 commit e0fd03a

File tree

18 files changed

+580
-1
lines changed

18 files changed

+580
-1
lines changed

build/int.cloudbuild.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,26 @@ steps:
535535
- verify simple-autopilot-private-non-default-sa
536536
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
537537
args: ['/bin/bash', '-c', 'cft test run TestSimpleAutopilotPrivateNonDefaultSA --stage teardown --verbose']
538+
- id: init simple-fleet-app-operator-permissions
539+
waitFor:
540+
- create-all
541+
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
542+
args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage init --verbose']
543+
- id: apply simple-fleet-app-operator-permissions
544+
waitFor:
545+
- init simple-fleet-app-operator-permissions
546+
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
547+
args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage apply --verbose']
548+
- id: verify simple-fleet-app-operator-permissions
549+
waitFor:
550+
- apply simple-fleet-app-operator-permissions
551+
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
552+
args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage verify --verbose']
553+
- id: teardown simple-fleet-app-operator-permissions
554+
waitFor:
555+
- verify simple-fleet-app-operator-permissions
556+
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
557+
args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage teardown --verbose']
538558
tags:
539559
- 'ci'
540560
- 'integration'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Simple App Operator Permissions Setup for a Fleet Scope
2+
3+
This example illustrates how to create a Fleet Scope for a [team](https://cloud.google.com/kubernetes-engine/fleet-management/docs/team-management) and set up permissions for an app operator in the team.
4+
5+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
6+
## Inputs
7+
8+
| Name | Description | Type | Default | Required |
9+
|------|-------------|------|---------|:--------:|
10+
| fleet\_project\_id | The project to which the Fleet belongs. | `string` | n/a | yes |
11+
12+
## Outputs
13+
14+
| Name | Description |
15+
|------|-------------|
16+
| fleet\_project\_id | The project to which the Fleet belongs. |
17+
| wait | An output (Fleet Scope RBAC Role Binding IDs) to use when you want to depend on granting permissions finishing. |
18+
19+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
20+
21+
To provision this example, run the following from within this directory:
22+
- `terraform init` to get the plugins
23+
- `terraform plan` to see the infrastructure plan
24+
- `terraform apply` to apply the infrastructure build
25+
- `terraform destroy` to destroy the built infrastructure
26+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
locals {
18+
app_operator_id = "app-operator-id"
19+
app_operator_team = "app-operator-team"
20+
app_operator_role = "VIEW"
21+
}
22+
23+
# Create a Service Account, which can be used as an app operator.
24+
resource "google_service_account" "service_account" {
25+
project = var.fleet_project_id
26+
account_id = local.app_operator_id
27+
display_name = "Test App Operator Service Account"
28+
}
29+
30+
# Create a Fleet Scope for the app operator's team.
31+
resource "google_gke_hub_scope" "scope" {
32+
project = var.fleet_project_id
33+
scope_id = local.app_operator_team
34+
}
35+
36+
# Grant permissions to the app operator to work with the Fleet Scope.
37+
module "permissions" {
38+
source = "../../modules/fleet-app-operator-permissions"
39+
40+
fleet_project_id = var.fleet_project_id
41+
scope_id = google_gke_hub_scope.scope.scope_id
42+
users = ["${local.app_operator_id}@${var.fleet_project_id}.iam.gserviceaccount.com"]
43+
groups = []
44+
role = local.app_operator_role
45+
46+
depends_on = [
47+
google_service_account.service_account
48+
]
49+
}
50+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
output "fleet_project_id" {
18+
description = "The project to which the Fleet belongs."
19+
value = var.fleet_project_id
20+
}
21+
22+
output "wait" {
23+
description = "An output (Fleet Scope RBAC Role Binding IDs) to use when you want to depend on granting permissions finishing."
24+
value = module.permissions.wait
25+
}
26+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
variable "fleet_project_id" {
18+
description = "The project to which the Fleet belongs."
19+
type = string
20+
}
21+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
terraform {
18+
required_version = ">= 1.2.0"
19+
20+
required_providers {
21+
google = {
22+
source = "hashicorp/google"
23+
version = ">= 4.81.0"
24+
}
25+
google-beta = {
26+
source = "hashicorp/google-beta"
27+
version = ">= 4.81.0"
28+
}
29+
}
30+
}
31+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Terrafrom Module for Fleet App Operator Permissions
2+
3+
This module bundles different permissions (IAM and RBAC Role Bindings) required for [Fleet team management](https://cloud.google.com/kubernetes-engine/fleet-management/docs/team-management). A platform admin can use this module to set up permissions for an app operator (user or group) in a team--including usage of Fleet Scopes, Connect Gateway, logging, and metrics--based on predefined roles (VIEW, EDIT, ADMIN).
4+
5+
## Usage
6+
```tf
7+
Example:
8+
module "fleet_app_operator_permissions" {
9+
source = "terraform-google-modules/kubernetes-engine/google//modules/fleet-app-operator-permissions"
10+
11+
fleet_project_id = "my-project-id"
12+
scope_id = "frontend-team"
13+
users = ["person1@company.com", "person2@company.com"]
14+
groups = ["people@company.com"]
15+
role = "EDIT"
16+
}
17+
```
18+
19+
To deploy this config, run:
20+
- `terraform init` to get the plugins
21+
- `terraform plan` to see the infrastructure plan
22+
- `terraform apply` to apply the infrastructure build
23+
- `terraform destroy` to destroy the built infrastructure
24+
25+
26+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
27+
## Inputs
28+
29+
| Name | Description | Type | Default | Required |
30+
|------|-------------|------|---------|:--------:|
31+
| fleet\_project\_id | The project to which the Fleet belongs. | `string` | n/a | yes |
32+
| groups | The list of app operator group principals, e.g., `people@google.com`, `principalSet://iam.googleapis.com/locations/global/workforcePools/my-pool/group/people`. | `list(string)` | n/a | yes |
33+
| role | The principals role for the Fleet Scope (`VIEW`/`EDIT`/`ADMIN`). | `string` | n/a | yes |
34+
| scope\_id | The scope for which IAM and RBAC role bindings are created. | `string` | n/a | yes |
35+
| users | The list of app operator user principals, e.g., `person@google.com`, `principal://iam.googleapis.com/locations/global/workforcePools/my-pool/subject/person`, `serviceAccount:my-service-account@my-project.iam.gserviceaccount.com`. | `list(string)` | n/a | yes |
36+
37+
## Outputs
38+
39+
| Name | Description |
40+
|------|-------------|
41+
| fleet\_project\_id | The project to which the Fleet belongs. |
42+
| wait | An output to use when you want to depend on Scope RBAC Role Binding creation finishing. |
43+
44+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
locals {
18+
user_principals = [for name in var.users : (
19+
startswith(name, "principal://") ? name : (
20+
endswith(name, "gserviceaccount.com") ? "serviceAccount:${name}" : (
21+
"user:${name}"
22+
)))]
23+
24+
group_principals = [for name in var.groups : (
25+
startswith(name, "principalSet://") ? name : (
26+
"group:${name}"
27+
))]
28+
29+
project_level_scope_role = {
30+
"VIEW" = "roles/gkehub.scopeViewerProjectLevel"
31+
"EDIT" = "roles/gkehub.scopeEditorProjectLevel"
32+
"ADMIN" = "roles/gkehub.scopeEditorProjectLevel" # Same as EDIT
33+
}
34+
35+
resource_level_scope_role = {
36+
"VIEW" = "roles/gkehub.scopeViewer"
37+
"EDIT" = "roles/gkehub.scopeEditor"
38+
"ADMIN" = "roles/gkehub.scopeAdmin"
39+
}
40+
}
41+
42+
resource "google_project_iam_binding" "log_view_permissions" {
43+
project = var.fleet_project_id
44+
role = "roles/logging.viewAccessor"
45+
members = concat(local.user_principals, local.group_principals)
46+
condition {
47+
title = "conditional log view access"
48+
description = "log view access for scope ${var.scope_id}"
49+
expression = "resource.name == \"projects/${var.fleet_project_id}/locations/global/buckets/fleet-o11y-scope-${var.scope_id}/views/fleet-o11y-scope-${var.scope_id}-k8s_container\" || resource.name == \"projects/${var.fleet_project_id}/locations/global/buckets/fleet-o11y-scope-${var.scope_id}/views/fleet-o11y-scope-${var.scope_id}-k8s_pod\""
50+
}
51+
}
52+
53+
resource "google_project_iam_binding" "project_level_scope_permissions" {
54+
project = var.fleet_project_id
55+
role = local.project_level_scope_role[var.role]
56+
members = concat(local.user_principals, local.group_principals)
57+
}
58+
59+
resource "google_gke_hub_scope_iam_binding" "resource_level_scope_permissions" {
60+
project = var.fleet_project_id
61+
scope_id = var.scope_id
62+
role = local.resource_level_scope_role[var.role]
63+
members = concat(local.user_principals, local.group_principals)
64+
}
65+
66+
resource "random_id" "user_rand_suffix" {
67+
for_each = toset(var.users)
68+
byte_length = 4
69+
}
70+
71+
resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_user_role_bindings" {
72+
for_each = toset(var.users)
73+
project = var.fleet_project_id
74+
scope_rbac_role_binding_id = "tf-${substr(join("", regexall("[a-z0-9]+", each.key)), 0, 16)}-${random_id.user_rand_suffix[each.key].hex}"
75+
scope_id = var.scope_id
76+
user = each.key
77+
role {
78+
predefined_role = var.role
79+
}
80+
}
81+
82+
resource "random_id" "group_rand_suffix" {
83+
for_each = toset(var.groups)
84+
byte_length = 4
85+
}
86+
87+
resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_group_role_bindings" {
88+
for_each = toset(var.groups)
89+
project = var.fleet_project_id
90+
scope_rbac_role_binding_id = "tf-${substr(join("", regexall("[a-z0-9]+", each.key)), 0, 16)}-${random_id.group_rand_suffix[each.key].hex}"
91+
scope_id = var.scope_id
92+
group = each.key
93+
role {
94+
predefined_role = var.role
95+
}
96+
}
97+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
output "fleet_project_id" {
18+
description = "The project to which the Fleet belongs."
19+
value = var.fleet_project_id
20+
}
21+
22+
output "wait" {
23+
description = "An output to use when you want to depend on Scope RBAC Role Binding creation finishing."
24+
value = {
25+
for k, v in merge(google_gke_hub_scope_rbac_role_binding.scope_rbac_user_role_bindings, google_gke_hub_scope_rbac_role_binding.scope_rbac_group_role_bindings) : k => v.scope_rbac_role_binding_id
26+
}
27+
}
28+

0 commit comments

Comments
 (0)