Skip to content

Commit 5d9f867

Browse files
feat: add optional groups creation (#757)
* new groups module + changing 0-bootstrap step to use this module * Moving the billing_project var + changing error messages + changing strategy to output gorups * adjusting and commenting depends_on * Small change to for_each on optional groups * creating a local to define the optional groups to be created * renaming groups variable, removing local module and uncommenting provider * changing outputs strategy * missing var name update at example tfvars * removing unnecessary step documented * removing email validation on groups variable Co-authored-by: Bharath KKB <[email protected]>
1 parent 37ba8ba commit 5d9f867

File tree

7 files changed

+226
-5
lines changed

7 files changed

+226
-5
lines changed

0-bootstrap/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ For more information about the permissions that are required, and the resources
9595
that are created, see the organization bootstrap module
9696
[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap)
9797

98+
### Optional - Automatic creation of Google Cloud Identity groups
99+
100+
Google Cloud Identity groups are used for [authentication and access management](https://cloud.google.com/architecture/security-foundations/authentication-authorization) in the foundation.
101+
102+
To enable automatic creation of the [required groups](https://cloud.google.com/architecture/security-foundations/authentication-authorization#users_and_groups) you need to:
103+
104+
- Have an existing project for Cloud Identity API billing.
105+
- Enable the Cloud Identity API(`cloudidentity.googleapis.com`) on the billing project.
106+
- Grant role `roles/serviceusage.serviceUsageConsumer` to the user running Terraform on the billing project.
107+
- Provide values for the groups and billing project in the variable `groups`.
108+
109+
All groups in the `groups.required_groups` are required.
110+
111+
All groups in the `groups.optional_groups` are optional.
112+
98113
### Troubleshooting
99114

100115
Please refer to [troubleshooting](../docs/TROUBLESHOOTING.md) if you run into issues during this step.
@@ -177,6 +192,8 @@ the following steps:
177192
| folder\_prefix | Name prefix to use for folders created. Should be the same in all steps. | `string` | `"fldr"` | no |
178193
| group\_billing\_admins | Google Group for GCP Billing Administrators | `string` | n/a | yes |
179194
| group\_org\_admins | Google Group for GCP Organization Administrators | `string` | n/a | yes |
195+
| groups | Contain the details of the Groups to be created. | <pre>object({<br> create_groups = bool<br> billing_project = string<br> required_groups = object({<br> group_org_admins = string<br> group_billing_admins = string<br> billing_data_users = string<br> audit_data_users = string<br> monitoring_workspace_users = string<br> })<br> optional_groups = object({<br> gcp_platform_viewer = string<br> gcp_security_reviewer = string<br> gcp_network_viewer = string<br> gcp_scc_admin = string<br> gcp_global_secrets_admin = string<br> gcp_audit_viewer = string<br> })<br> })</pre> | <pre>{<br> "billing_project": "",<br> "create_groups": false,<br> "optional_groups": {<br> "gcp_audit_viewer": "",<br> "gcp_global_secrets_admin": "",<br> "gcp_network_viewer": "",<br> "gcp_platform_viewer": "",<br> "gcp_scc_admin": "",<br> "gcp_security_reviewer": ""<br> },<br> "required_groups": {<br> "audit_data_users": "",<br> "billing_data_users": "",<br> "group_billing_admins": "",<br> "group_org_admins": "",<br> "monitoring_workspace_users": ""<br> }<br>}</pre> | no |
196+
| initial\_group\_config | Define the group configuration when it are initialized. Valid values are: WITH\_INITIAL\_OWNER, EMPTY and INITIAL\_GROUP\_CONFIG\_UNSPECIFIED. | `string` | `"WITH_INITIAL_OWNER"` | no |
180197
| org\_id | GCP Organization ID | `string` | n/a | yes |
181198
| org\_policy\_admin\_role | Additional Org Policy Admin role for admin group. You can use this for testing purposes. | `bool` | `false` | no |
182199
| org\_project\_creators | Additional list of members to have project creator role across the organization. Prefix of group: user: or serviceAccount: is required. | `list(string)` | `[]` | no |
@@ -193,8 +210,10 @@ the following steps:
193210
| gcs\_bucket\_cloudbuild\_artifacts | Bucket used to store Cloud/Build artifacts in CloudBuild project. |
194211
| gcs\_bucket\_tfstate | Bucket used for storing terraform state for foundations pipelines in seed project. |
195212
| networks\_step\_terraform\_service\_account\_email | Networks Step Terraform Account |
213+
| optional\_groups | List of Google Groups created that are optional to the Example Foundation steps. |
196214
| organization\_step\_terraform\_service\_account\_email | Organization Step Terraform Account |
197215
| projects\_step\_terraform\_service\_account\_email | Projects Step Terraform Account |
216+
| required\_groups | List of Google Groups created that are required by the Example Foundation steps. |
198217
| seed\_project\_id | Project where service accounts and core APIs will be enabled. |
199218
| terraform\_sa\_name | Fully qualified name for privileged service account for Terraform. |
200219
| terraform\_service\_account | Email for privileged service account for Terraform. |

0-bootstrap/groups.tf

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright 2022 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+
#Groups creation resources
18+
19+
locals {
20+
optional_groups_to_create = {
21+
for key, value in var.groups.optional_groups : key => value
22+
if value != "" && var.groups.create_groups == true
23+
}
24+
required_groups_to_create = {
25+
for key, value in var.groups.required_groups : key => value
26+
if var.groups.create_groups == true
27+
}
28+
}
29+
30+
data "google_organization" "org" {
31+
count = var.groups.create_groups ? 1 : 0
32+
organization = var.org_id
33+
}
34+
35+
module "required_group" {
36+
for_each = local.required_groups_to_create
37+
source = "terraform-google-modules/group/google"
38+
version = "~> 0.1"
39+
40+
id = each.value
41+
display_name = each.key
42+
description = each.key
43+
initial_group_config = var.initial_group_config
44+
customer_id = data.google_organization.org[0].directory_customer_id
45+
}
46+
47+
module "optional_group" {
48+
for_each = local.optional_groups_to_create
49+
source = "terraform-google-modules/group/google"
50+
version = "~> 0.1"
51+
52+
id = each.value
53+
display_name = each.key
54+
description = each.key
55+
initial_group_config = var.initial_group_config
56+
customer_id = data.google_organization.org[0].directory_customer_id
57+
}

0-bootstrap/main.tf

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ locals {
3232
org_admins_org_iam_permissions = var.org_policy_admin_role == true ? [
3333
"roles/orgpolicy.policyAdmin", "roles/resourcemanager.organizationAdmin", "roles/billing.user"
3434
] : ["roles/resourcemanager.organizationAdmin", "roles/billing.user"]
35+
group_org_admins = var.groups.create_groups ? var.groups.required_groups.group_org_admins : var.group_org_admins
36+
group_billing_admins = var.groups.create_groups ? var.groups.required_groups.group_billing_admins : var.group_billing_admins
3537
}
3638

3739
resource "google_folder" "bootstrap" {
@@ -47,8 +49,8 @@ module "seed_bootstrap" {
4749
project_id = "${var.project_prefix}-b-seed"
4850
state_bucket_name = "${var.bucket_prefix}-b-tfstate"
4951
billing_account = var.billing_account
50-
group_org_admins = var.group_org_admins
51-
group_billing_admins = var.group_billing_admins
52+
group_org_admins = local.group_org_admins
53+
group_billing_admins = local.group_billing_admins
5254
default_region = var.default_region
5355
org_project_creators = local.org_project_creators
5456
sa_enable_impersonation = true
@@ -57,7 +59,7 @@ module "seed_bootstrap" {
5759
project_prefix = var.project_prefix
5860

5961
# Remove after github.com/terraform-google-modules/terraform-google-bootstrap/issues/160
60-
depends_on = [google_folder.bootstrap]
62+
depends_on = [google_folder.bootstrap, module.required_group, module.optional_group]
6163

6264
project_labels = {
6365
environment = "bootstrap"
@@ -107,7 +109,7 @@ module "cloudbuild_bootstrap" {
107109
folder_id = google_folder.bootstrap.id
108110
project_id = "${var.project_prefix}-b-cicd"
109111
billing_account = var.billing_account
110-
group_org_admins = var.group_org_admins
112+
group_org_admins = local.group_org_admins
111113
default_region = var.default_region
112114
terraform_sa_email = module.seed_bootstrap.terraform_sa_email
113115
terraform_sa_name = module.seed_bootstrap.terraform_sa_name
@@ -121,7 +123,7 @@ module "cloudbuild_bootstrap" {
121123
terraform_version_sha256sum = "4a52886e019b4fdad2439da5ff43388bbcc6cce9784fde32c53dcd0e28ca9957"
122124

123125
# Remove after github.com/terraform-google-modules/terraform-google-bootstrap/issues/160
124-
depends_on = [module.seed_bootstrap]
126+
depends_on = [google_folder.bootstrap, module.required_group, module.optional_group]
125127

126128
activate_apis = [
127129
"serviceusage.googleapis.com",
@@ -153,8 +155,10 @@ module "cloudbuild_bootstrap" {
153155
"non\\-production", //non-production needs a \ to ensure regex matches correct branches.
154156
"production"
155157
]
158+
156159
}
157160

161+
158162
// Standalone repo for Terraform-validator policies.
159163
// This repo does not need to trigger builds in Cloud Build.
160164
resource "google_sourcerepo_repository" "gcp_policies" {

0-bootstrap/outputs.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,18 @@ output "terraform_validator_policies_repo" {
111111
// description = "Bucket used to store Jenkins artifacts in Jenkins project."
112112
// value = module.jenkins_bootstrap.gcs_bucket_jenkins_artifacts
113113
//}
114+
115+
116+
/* ----------------------------------------
117+
Specific to Google Groups creation module
118+
---------------------------------------- */
119+
120+
output "required_groups" {
121+
description = "List of Google Groups created that are required by the Example Foundation steps."
122+
value = var.groups.create_groups == true ? module.required_group : {}
123+
}
124+
125+
output "optional_groups" {
126+
description = "List of Google Groups created that are optional to the Example Foundation steps."
127+
value = var.groups.create_groups == true ? module.optional_group : {}
128+
}

0-bootstrap/provider.tf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright 2022 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+
provider "google-beta" {
18+
user_project_override = true
19+
billing_project = var.groups.billing_project
20+
}

0-bootstrap/terraform.example.tfvars

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ default_region = "us-central1"
3131
// The folder must already exist.
3232
//parent_folder = "01234567890"
3333

34+
// Optional - for enabling the automatic groups creation, uncoment the groups
35+
// variable and update the values with the desired group names
36+
//groups = {
37+
// create_groups = true,
38+
// billing_project = "billing-project",
39+
// required_groups = {
40+
// group_org_admins = "[email protected]"
41+
// group_billing_admins = "[email protected]"
42+
// billing_data_users = "[email protected]"
43+
// audit_data_users = "[email protected]"
44+
// monitoring_workspace_users = "[email protected]"
45+
// },
46+
// optional_groups = {
47+
// gcp_platform_viewer = "[email protected]"
48+
// gcp_security_reviewer = "[email protected]"
49+
// gcp_network_viewer = "[email protected]"
50+
// gcp_scc_admin = "[email protected]"
51+
// gcp_global_secrets_admin = "[email protected]"
52+
// gcp_audit_viewer = "[email protected]"
53+
// }
54+
//}
55+
//
56+
57+
3458
/* ----------------------------------------
3559
Specific to jenkins_bootstrap module
3660
---------------------------------------- */

0-bootstrap/variables.tf

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,85 @@ variable "cloud_source_repos" {
163163
# type = string
164164
# description = "BGP session range for tunnel 1"
165165
# }
166+
167+
/* ----------------------------------------
168+
Specific to Groups creation
169+
---------------------------------------- */
170+
variable "groups" {
171+
description = "Contain the details of the Groups to be created."
172+
type = object({
173+
create_groups = bool
174+
billing_project = string
175+
required_groups = object({
176+
group_org_admins = string
177+
group_billing_admins = string
178+
billing_data_users = string
179+
audit_data_users = string
180+
monitoring_workspace_users = string
181+
})
182+
optional_groups = object({
183+
gcp_platform_viewer = string
184+
gcp_security_reviewer = string
185+
gcp_network_viewer = string
186+
gcp_scc_admin = string
187+
gcp_global_secrets_admin = string
188+
gcp_audit_viewer = string
189+
})
190+
})
191+
default = {
192+
create_groups = false
193+
billing_project = ""
194+
required_groups = {
195+
group_org_admins = ""
196+
group_billing_admins = ""
197+
billing_data_users = ""
198+
audit_data_users = ""
199+
monitoring_workspace_users = ""
200+
}
201+
optional_groups = {
202+
gcp_platform_viewer = ""
203+
gcp_security_reviewer = ""
204+
gcp_network_viewer = ""
205+
gcp_scc_admin = ""
206+
gcp_global_secrets_admin = ""
207+
gcp_audit_viewer = ""
208+
}
209+
}
210+
211+
validation {
212+
condition = var.groups.create_groups == true ? (var.groups.billing_project != "" ? true : false) : true
213+
error_message = "A billing_project must be passed to use the automatic group creation."
214+
}
215+
216+
validation {
217+
condition = var.groups.create_groups == true ? (var.groups.required_groups.group_org_admins != "" ? true : false) : true
218+
error_message = "The group group_org_admins is invalid, it must be a valid email."
219+
}
220+
221+
validation {
222+
condition = var.groups.create_groups == true ? (var.groups.required_groups.group_billing_admins != "" ? true : false) : true
223+
error_message = "The group group_billing_admins is invalid, it must be a valid email."
224+
}
225+
226+
validation {
227+
condition = var.groups.create_groups == true ? (var.groups.required_groups.billing_data_users != "" ? true : false) : true
228+
error_message = "The group billing_data_users is invalid, it must be a valid email."
229+
}
230+
231+
validation {
232+
condition = var.groups.create_groups == true ? (var.groups.required_groups.audit_data_users != "" ? true : false) : true
233+
error_message = "The group audit_data_users is invalid, it must be a valid email."
234+
}
235+
236+
validation {
237+
condition = var.groups.create_groups == true ? (var.groups.required_groups.monitoring_workspace_users != "" ? true : false) : true
238+
error_message = "The group monitoring_workspace_users is invalid, it must be a valid email."
239+
}
240+
241+
}
242+
243+
variable "initial_group_config" {
244+
description = "Define the group configuration when it are initialized. Valid values are: WITH_INITIAL_OWNER, EMPTY and INITIAL_GROUP_CONFIG_UNSPECIFIED."
245+
type = string
246+
default = "WITH_INITIAL_OWNER"
247+
}

0 commit comments

Comments
 (0)