Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add groups creation #757

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
}