Skip to content

Commit

Permalink
feat: add optional groups creation (#757)
Browse files Browse the repository at this point in the history
* 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 <bharathkrishnakb@gmail.com>
  • Loading branch information
mauro-cit and bharathkkb committed Aug 9, 2022
1 parent 37ba8ba commit 5d9f867
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 5 deletions.
19 changes: 19 additions & 0 deletions 0-bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ For more information about the permissions that are required, and the resources
that are created, see the organization bootstrap module
[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap)

### Optional - Automatic creation of Google Cloud Identity groups

Google Cloud Identity groups are used for [authentication and access management](https://cloud.google.com/architecture/security-foundations/authentication-authorization) in the foundation.

To enable automatic creation of the [required groups](https://cloud.google.com/architecture/security-foundations/authentication-authorization#users_and_groups) you need to:

- Have an existing project for Cloud Identity API billing.
- Enable the Cloud Identity API(`cloudidentity.googleapis.com`) on the billing project.
- Grant role `roles/serviceusage.serviceUsageConsumer` to the user running Terraform on the billing project.
- Provide values for the groups and billing project in the variable `groups`.

All groups in the `groups.required_groups` are required.

All groups in the `groups.optional_groups` are optional.

### Troubleshooting

Please refer to [troubleshooting](../docs/TROUBLESHOOTING.md) if you run into issues during this step.
Expand Down Expand Up @@ -177,6 +192,8 @@ the following steps:
| folder\_prefix | Name prefix to use for folders created. Should be the same in all steps. | `string` | `"fldr"` | no |
| group\_billing\_admins | Google Group for GCP Billing Administrators | `string` | n/a | yes |
| group\_org\_admins | Google Group for GCP Organization Administrators | `string` | n/a | yes |
| 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 |
| 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 |
| org\_id | GCP Organization ID | `string` | n/a | yes |
| org\_policy\_admin\_role | Additional Org Policy Admin role for admin group. You can use this for testing purposes. | `bool` | `false` | no |
| 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 |
Expand All @@ -193,8 +210,10 @@ the following steps:
| gcs\_bucket\_cloudbuild\_artifacts | Bucket used to store Cloud/Build artifacts in CloudBuild project. |
| gcs\_bucket\_tfstate | Bucket used for storing terraform state for foundations pipelines in seed project. |
| networks\_step\_terraform\_service\_account\_email | Networks Step Terraform Account |
| optional\_groups | List of Google Groups created that are optional to the Example Foundation steps. |
| organization\_step\_terraform\_service\_account\_email | Organization Step Terraform Account |
| projects\_step\_terraform\_service\_account\_email | Projects Step Terraform Account |
| required\_groups | List of Google Groups created that are required by the Example Foundation steps. |
| seed\_project\_id | Project where service accounts and core APIs will be enabled. |
| terraform\_sa\_name | Fully qualified name for privileged service account for Terraform. |
| terraform\_service\_account | Email for privileged service account for Terraform. |
Expand Down
57 changes: 57 additions & 0 deletions 0-bootstrap/groups.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright 2022 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.
*/

#Groups creation resources

locals {
optional_groups_to_create = {
for key, value in var.groups.optional_groups : key => value
if value != "" && var.groups.create_groups == true
}
required_groups_to_create = {
for key, value in var.groups.required_groups : key => value
if var.groups.create_groups == true
}
}

data "google_organization" "org" {
count = var.groups.create_groups ? 1 : 0
organization = var.org_id
}

module "required_group" {
for_each = local.required_groups_to_create
source = "terraform-google-modules/group/google"
version = "~> 0.1"

id = each.value
display_name = each.key
description = each.key
initial_group_config = var.initial_group_config
customer_id = data.google_organization.org[0].directory_customer_id
}

module "optional_group" {
for_each = local.optional_groups_to_create
source = "terraform-google-modules/group/google"
version = "~> 0.1"

id = each.value
display_name = each.key
description = each.key
initial_group_config = var.initial_group_config
customer_id = data.google_organization.org[0].directory_customer_id
}
14 changes: 9 additions & 5 deletions 0-bootstrap/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ locals {
org_admins_org_iam_permissions = var.org_policy_admin_role == true ? [
"roles/orgpolicy.policyAdmin", "roles/resourcemanager.organizationAdmin", "roles/billing.user"
] : ["roles/resourcemanager.organizationAdmin", "roles/billing.user"]
group_org_admins = var.groups.create_groups ? var.groups.required_groups.group_org_admins : var.group_org_admins
group_billing_admins = var.groups.create_groups ? var.groups.required_groups.group_billing_admins : var.group_billing_admins
}

resource "google_folder" "bootstrap" {
Expand All @@ -47,8 +49,8 @@ module "seed_bootstrap" {
project_id = "${var.project_prefix}-b-seed"
state_bucket_name = "${var.bucket_prefix}-b-tfstate"
billing_account = var.billing_account
group_org_admins = var.group_org_admins
group_billing_admins = var.group_billing_admins
group_org_admins = local.group_org_admins
group_billing_admins = local.group_billing_admins
default_region = var.default_region
org_project_creators = local.org_project_creators
sa_enable_impersonation = true
Expand All @@ -57,7 +59,7 @@ module "seed_bootstrap" {
project_prefix = var.project_prefix

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

project_labels = {
environment = "bootstrap"
Expand Down Expand Up @@ -107,7 +109,7 @@ module "cloudbuild_bootstrap" {
folder_id = google_folder.bootstrap.id
project_id = "${var.project_prefix}-b-cicd"
billing_account = var.billing_account
group_org_admins = var.group_org_admins
group_org_admins = local.group_org_admins
default_region = var.default_region
terraform_sa_email = module.seed_bootstrap.terraform_sa_email
terraform_sa_name = module.seed_bootstrap.terraform_sa_name
Expand All @@ -121,7 +123,7 @@ module "cloudbuild_bootstrap" {
terraform_version_sha256sum = "4a52886e019b4fdad2439da5ff43388bbcc6cce9784fde32c53dcd0e28ca9957"

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

activate_apis = [
"serviceusage.googleapis.com",
Expand Down Expand Up @@ -153,8 +155,10 @@ module "cloudbuild_bootstrap" {
"non\\-production", //non-production needs a \ to ensure regex matches correct branches.
"production"
]

}


// Standalone repo for Terraform-validator policies.
// This repo does not need to trigger builds in Cloud Build.
resource "google_sourcerepo_repository" "gcp_policies" {
Expand Down
15 changes: 15 additions & 0 deletions 0-bootstrap/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,18 @@ output "terraform_validator_policies_repo" {
// description = "Bucket used to store Jenkins artifacts in Jenkins project."
// value = module.jenkins_bootstrap.gcs_bucket_jenkins_artifacts
//}


/* ----------------------------------------
Specific to Google Groups creation module
---------------------------------------- */

output "required_groups" {
description = "List of Google Groups created that are required by the Example Foundation steps."
value = var.groups.create_groups == true ? module.required_group : {}
}

output "optional_groups" {
description = "List of Google Groups created that are optional to the Example Foundation steps."
value = var.groups.create_groups == true ? module.optional_group : {}
}
20 changes: 20 additions & 0 deletions 0-bootstrap/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright 2022 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.
*/

provider "google-beta" {
user_project_override = true
billing_project = var.groups.billing_project
}
24 changes: 24 additions & 0 deletions 0-bootstrap/terraform.example.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,30 @@ default_region = "us-central1"
// The folder must already exist.
//parent_folder = "01234567890"

// Optional - for enabling the automatic groups creation, uncoment the groups
// variable and update the values with the desired group names
//groups = {
// create_groups = true,
// billing_project = "billing-project",
// required_groups = {
// group_org_admins = "group_org_admins_local_test@example.com"
// group_billing_admins = "group_billing_admins_local_test@example.com"
// billing_data_users = "billing_data_users_local_test@example.com"
// audit_data_users = "audit_data_users_local_test@example.com"
// monitoring_workspace_users = "monitoring_workspace_users_local_test@example.com"
// },
// optional_groups = {
// gcp_platform_viewer = "gcp_platform_viewer_local_test@example.com"
// gcp_security_reviewer = "gcp_security_reviewer_local_test@example.com"
// gcp_network_viewer = "gcp_network_viewer_local_test@example.com"
// gcp_scc_admin = "gcp_scc_admin_local_test@example.com"
// gcp_global_secrets_admin = "gcp_global_secrets_admin_local_test@example.com"
// gcp_audit_viewer = "gcp_audit_viewer_local_test@example.com"
// }
//}
//


/* ----------------------------------------
Specific to jenkins_bootstrap module
---------------------------------------- */
Expand Down
82 changes: 82 additions & 0 deletions 0-bootstrap/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,85 @@ variable "cloud_source_repos" {
# type = string
# description = "BGP session range for tunnel 1"
# }

/* ----------------------------------------
Specific to Groups creation
---------------------------------------- */
variable "groups" {
description = "Contain the details of the Groups to be created."
type = object({
create_groups = bool
billing_project = string
required_groups = object({
group_org_admins = string
group_billing_admins = string
billing_data_users = string
audit_data_users = string
monitoring_workspace_users = string
})
optional_groups = object({
gcp_platform_viewer = string
gcp_security_reviewer = string
gcp_network_viewer = string
gcp_scc_admin = string
gcp_global_secrets_admin = string
gcp_audit_viewer = string
})
})
default = {
create_groups = false
billing_project = ""
required_groups = {
group_org_admins = ""
group_billing_admins = ""
billing_data_users = ""
audit_data_users = ""
monitoring_workspace_users = ""
}
optional_groups = {
gcp_platform_viewer = ""
gcp_security_reviewer = ""
gcp_network_viewer = ""
gcp_scc_admin = ""
gcp_global_secrets_admin = ""
gcp_audit_viewer = ""
}
}

validation {
condition = var.groups.create_groups == true ? (var.groups.billing_project != "" ? true : false) : true
error_message = "A billing_project must be passed to use the automatic group creation."
}

validation {
condition = var.groups.create_groups == true ? (var.groups.required_groups.group_org_admins != "" ? true : false) : true
error_message = "The group group_org_admins is invalid, it must be a valid email."
}

validation {
condition = var.groups.create_groups == true ? (var.groups.required_groups.group_billing_admins != "" ? true : false) : true
error_message = "The group group_billing_admins is invalid, it must be a valid email."
}

validation {
condition = var.groups.create_groups == true ? (var.groups.required_groups.billing_data_users != "" ? true : false) : true
error_message = "The group billing_data_users is invalid, it must be a valid email."
}

validation {
condition = var.groups.create_groups == true ? (var.groups.required_groups.audit_data_users != "" ? true : false) : true
error_message = "The group audit_data_users is invalid, it must be a valid email."
}

validation {
condition = var.groups.create_groups == true ? (var.groups.required_groups.monitoring_workspace_users != "" ? true : false) : true
error_message = "The group monitoring_workspace_users is invalid, it must be a valid email."
}

}

variable "initial_group_config" {
description = "Define the group configuration when it are initialized. Valid values are: WITH_INITIAL_OWNER, EMPTY and INITIAL_GROUP_CONFIG_UNSPECIFIED."
type = string
default = "WITH_INITIAL_OWNER"
}

0 comments on commit 5d9f867

Please sign in to comment.