diff --git a/modules/fabric-project/README.md b/modules/fabric-project/README.md new file mode 100644 index 00000000..9425a59b --- /dev/null +++ b/modules/fabric-project/README.md @@ -0,0 +1,69 @@ +# Google Cloud Simple Project Creation + +This module allows simple Google Cloud Platform project creation, with minimal service and project-level IAM binding management. It's designed to be used for architectural design and rapid prototyping, as part of the [Cloud Foundation Fabric](https://github.com/terraform-google-modules/cloud-foundation-fabric) environments. + +The resources/services/activations/deletions that this module will create/trigger are: + +- one project +- zero or one project metadata items for OSLogin activation +- zero or more project service activations +- zero or more project-level IAM bindings +- zero or more project-level custom roles +- zero or one project liens + +## Usage + +Basic usage of this module is as follows: + +```hcl +module "project_myproject" { + source = "terraform-google-modules/project-factory/google//modules/fabric-project" + parent_id = "1234567890" + parent_type = "folder" + billing_account = "ABCD-1234-ABCD-1234" + prefix = "staging" + name = "myproject" + oslogin = true + owners = ["group:admins@example.com"] + oslogin_admins = ["group:admins@example.com"] + gce_service_account_roles = ["foo-project:roles/compute.networkUser"] +} +``` + +[^]: (autogen_docs_start) + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| name | Project name and id suffix. | string | n/a | yes | +| parent\_id | Id of the resource under which the folder will be placed. | string | n/a | yes | +| prefix | Prefix used to generate project id and name | string | n/a | yes | +| activate\_apis | Service APIs to enable. | list | `` | no | +| auto\_create\_network | Whether to create the default network for the project | string | `"false"` | no | +| billing\_account | Billing account id. | string | `""` | no | +| custom\_roles | Map of role name => comma-delimited list of permissions to create in this project. | map | `` | no | +| editors | Optional list of IAM-format members to set as project editor. | list | `` | no | +| extra\_bindings\_members | List of comma-delimited IAM-format members for additional IAM bindings, one item per role. | list | `` | no | +| extra\_bindings\_roles | List of roles for additional IAM bindings, pair with members list below. | list | `` | no | +| gce\_service\_account\_roles | List of project id=>role to assign to the default GCE service account. | list | `` | no | +| labels | Resource labels. | map | `` | no | +| lien\_reason | If non-empty, creates a project lien with this description. | string | `""` | no | +| oslogin | Enable oslogin. | string | `"false"` | no | +| oslogin\_admins | List of IAM-format members that will get OS Login admin role. | list | `` | no | +| oslogin\_users | List of IAM-format members that will get OS Login user role. | list | `` | no | +| owners | Optional list of IAM-format members to set as project owners. | list | `` | no | +| parent\_type | Type of the parent resource, defaults to organization. | string | `"organization"` | no | +| viewers | Optional list of IAM-format members to set as project viewers. | list | `` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| custom\_roles | Ids of the created custom roles. | +| gce\_service\_account | Default GCE service account (depends on services). | +| gke\_service\_account | Default GKE service account (depends on services). | +| number | Project number (depends on services). | +| project\_id | Project id (depends on services). | + +[^]: (autogen_docs_end) diff --git a/modules/fabric-project/main.tf b/modules/fabric-project/main.tf new file mode 100644 index 00000000..b836fb7c --- /dev/null +++ b/modules/fabric-project/main.tf @@ -0,0 +1,145 @@ +/** + * Copyright 2018 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. + */ + +# lifecycle can't be set dynamically, see +# https://github.com/hashicorp/terraform/issues/3116 + +locals { + all_oslogin_users = "${concat(var.oslogin_users, var.oslogin_admins)}" + num_oslogin_users = "${length(var.oslogin_users) + length(var.oslogin_admins)}" + gce_service_account = "${google_project.project.number}-compute@developer.gserviceaccount.com" + gke_service_account = "service-${google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" +} + +resource "google_project" "project" { + org_id = "${var.parent_type == "organization" ? var.parent_id : ""}" + folder_id = "${var.parent_type == "folder" ? var.parent_id : ""}" + project_id = "${var.prefix}-${var.name}" + name = "${var.prefix}-${var.name}" + billing_account = "${var.billing_account}" + auto_create_network = "${var.auto_create_network}" + labels = "${var.labels}" +} + +resource "google_project_services" "services" { + count = "${length(var.activate_apis) > 0 ? 1 : 0}" + project = "${google_project.project.project_id}" + + services = [ + # "compute.googleapis.com", + # "iam.googleapis.com", + # "oslogin.googleapis.com", + "${var.activate_apis}", + ] +} + +# this will fail for external users, who need to be manually added so they +# can accept the email invitation to join the project +resource "google_project_iam_member" "owners" { + count = "${length(var.owners)}" + project = "${google_project.project.project_id}" + role = "roles/owner" + member = "${element(var.owners, count.index)}" +} + +resource "google_project_iam_member" "editors" { + count = "${length(var.editors)}" + project = "${google_project.project.project_id}" + role = "roles/editor" + member = "${element(var.editors, count.index)}" +} + +resource "google_project_iam_member" "viewers" { + count = "${length(var.viewers)}" + project = "${google_project.project.project_id}" + role = "roles/viewer" + member = "${element(var.viewers, count.index)}" +} + +resource "google_compute_project_metadata_item" "oslogin_meta" { + count = "${var.oslogin}" + project = "${google_project.project.project_id}" + key = "enable-oslogin" + value = "TRUE" + + # depend on services or it will fail on destroy + depends_on = ["google_project_services.services"] +} + +resource "google_project_iam_member" "oslogin_admins" { + count = "${var.oslogin ? length(var.oslogin_admins) : 0}" + project = "${google_project.project.project_id}" + role = "roles/compute.osAdminLogin" + member = "${element(var.oslogin_admins, count.index)}" +} + +resource "google_project_iam_member" "oslogin_users" { + count = "${var.oslogin ? length(var.oslogin_users) : 0}" + project = "${google_project.project.project_id}" + role = "roles/compute.osLogin" + member = "${element(var.oslogin_users, count.index)}" +} + +resource "google_project_iam_member" "oslogin_sa_users" { + count = "${var.oslogin ? local.num_oslogin_users : 0}" + project = "${google_project.project.project_id}" + role = "roles/iam.serviceAccountUser" + member = "${element(local.all_oslogin_users, count.index)}" +} + +# this grants the project viewer role to OS Login users, so that they can +# use the CLI to get the list of instances +resource "google_project_iam_member" "oslogin_viewers" { + count = "${var.oslogin ? local.num_oslogin_users : 0}" + project = "${google_project.project.project_id}" + role = "roles/viewer" + member = "${element(local.all_oslogin_users, count.index)}" +} + +resource "google_project_iam_custom_role" "roles" { + count = "${length(var.custom_roles)}" + project = "${google_project.project.project_id}" + role_id = "${element(keys(var.custom_roles), count.index)}" + title = "Custom role ${element(keys(var.custom_roles), count.index)}" + description = "Terraform-managed" + + permissions = [ + "${split(",", element(values(var.custom_roles), count.index))}", + ] +} + +resource "google_project_iam_binding" "extra" { + count = "${length(var.extra_bindings_roles)}" + project = "${google_project.project.project_id}" + role = "${element(var.extra_bindings_roles, count.index)}" + members = ["${split(",", element(var.extra_bindings_members, count.index))}"] + depends_on = ["google_project_iam_custom_role.roles"] +} + +resource "google_resource_manager_lien" "lien" { + count = "${var.lien_reason != "" ? 1 : 0}" + parent = "projects/${google_project.project.number}" + restrictions = ["resourcemanager.projects.delete"] + origin = "created-by-terraform" + reason = "${var.lien_reason}" +} + +resource "google_project_iam_member" "gce_service_account" { + count = "${length(var.gce_service_account_roles)}" + project = "${element(split("=>", element(var.gce_service_account_roles, count.index)), 0)}" + role = "${element(split("=>", element(var.gce_service_account_roles, count.index)), 1)}" + member = "serviceAccount:${local.gce_service_account}" +} diff --git a/modules/fabric-project/outputs.tf b/modules/fabric-project/outputs.tf new file mode 100644 index 00000000..0d252c12 --- /dev/null +++ b/modules/fabric-project/outputs.tf @@ -0,0 +1,44 @@ +/** + * Copyright 2018 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. + */ + +output "project_id" { + description = "Project id (depends on services)." + value = "${google_project.project.project_id}" + depends_on = ["google_project_services.services"] +} + +output "number" { + description = "Project number (depends on services)." + value = "${google_project.project.number}" + depends_on = ["google_project_services.services"] +} + +output "gce_service_account" { + description = "Default GCE service account (depends on services)." + value = "${local.gce_service_account}" + depends_on = ["google_project_services.services"] +} + +output "gke_service_account" { + description = "Default GKE service account (depends on services)." + value = "${local.gke_service_account}" + depends_on = ["google_project_services.services"] +} + +output "custom_roles" { + description = "Ids of the created custom roles." + value = ["${google_project_iam_custom_role.roles.*.role_id}"] +} diff --git a/modules/fabric-project/variables.tf b/modules/fabric-project/variables.tf new file mode 100644 index 00000000..f385be42 --- /dev/null +++ b/modules/fabric-project/variables.tf @@ -0,0 +1,110 @@ +/** + * Copyright 2018 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. + */ + +variable "parent_id" { + description = "Id of the resource under which the folder will be placed." +} + +variable "parent_type" { + description = "Type of the parent resource, defaults to organization." + default = "organization" +} + +variable "prefix" { + description = "Prefix used to generate project id and name" +} + +variable "name" { + description = "Project name and id suffix." +} + +variable "billing_account" { + description = "Billing account id." + default = "" +} + +variable "activate_apis" { + description = "Service APIs to enable." + default = [] +} + +variable "owners" { + description = "Optional list of IAM-format members to set as project owners." + default = [] +} + +variable "editors" { + description = "Optional list of IAM-format members to set as project editor." + default = [] +} + +variable "viewers" { + description = "Optional list of IAM-format members to set as project viewers." + default = [] +} + +variable "lien_reason" { + description = "If non-empty, creates a project lien with this description." + default = "" +} + +variable "oslogin" { + description = "Enable oslogin." + default = false +} + +variable "oslogin_admins" { + description = "List of IAM-format members that will get OS Login admin role." + default = [] +} + +variable "oslogin_users" { + description = "List of IAM-format members that will get OS Login user role." + default = [] +} + +# TODO: revert to a single map once the following issue is fixed +# https://github.com/hashicorp/terraform/issues/12570 + +variable "extra_bindings_roles" { + description = "List of roles for additional IAM bindings, pair with members list below." + default = [] +} + +variable "extra_bindings_members" { + description = "List of comma-delimited IAM-format members for additional IAM bindings, one item per role." + default = [] +} + +variable "auto_create_network" { + description = "Whether to create the default network for the project" + default = false +} + +variable "custom_roles" { + description = "Map of role name => comma-delimited list of permissions to create in this project." + default = {} +} + +variable "gce_service_account_roles" { + description = "List of project id=>role to assign to the default GCE service account." + default = [] +} + +variable "labels" { + description = "Resource labels." + default = {} +}