diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 12a34c5589..0000000000 --- a/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -venv/* -*/venv/* -**/.python-version -**/.terraform -**/terraform.tfstate* -**/terraform.tfvars -**/*.auto.tfvars -**/.terraform.tfstate.lock.info -**/.terraform.lock.hcl -!tests/**/terraform.tfvars -**/__pycache__ -**/.pytest_cache -**/.test.lock -.idea -.vscode -.idx/dev.nix -backend.tf -backend-config.hcl -credentials.json -key.json -terraform-ls.tf -bundle.zip -.DS_Store -**/packer_cache -**/*.pkrvars.hcl -fixture_* -fast/configs -fast/**/[0-9]*providers.tf -fast/**/terraform.tfvars -fast/**/terraform.tfvars.json -fast/**/terraform-*.auto.tfvars.json -fast/**/[0-9]*.auto.tfvars* -**/node_modules -fast/**/globals.auto.tfvars.json -cloud_sql_proxy -examples/cloud-operations/binauthz/tenant-setup.yaml -examples/cloud-operations/binauthz/app/app.yaml -env/ -examples/cloud-operations/adfs/ansible/vars/vars.yaml -examples/cloud-operations/adfs/ansible/gssh.sh -examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/vars.yaml -examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/gssh.sh -blueprints/cloud-operations/network-quota-monitoring/cloud-function.zip -blueprints/apigee/bigquery-analytics/bundle-export.zip -blueprints/apigee/bigquery-analytics/bundle-gcs2bq.zip -blueprints/apigee/bigquery-analytics/apiproxy.zip -blueprints/apigee/bigquery-analytics/create-datastore.sh -blueprints/apigee/bigquery-analytics/deploy-apiproxy.sh -blueprints/apigee/network-patterns/nb-glb-psc-neg-sb-psc-ilbl7-hybrid-neg/bundle/apiproxy/targets/default.xml -blueprints/apigee/network-patterns/nb-glb-psc-neg-sb-psc-ilbl7-hybrid-neg/bundle.zip -blueprints/apigee/network-patterns/nb-glb-psc-neg-sb-psc-ilbl7-hybrid-neg/deploy-apiproxy.sh -blueprints/apigee/hybrid-gke/apiproxy.zip -blueprints/apigee/hybrid-gke/deploy-apiproxy.sh -blueprints/apigee/hybrid-gke/ansible/gssh.sh -blueprints/apigee/hybrid-gke/ansible/vars/vars.yaml -blueprints/gke/autopilot/ansible/gssh.sh -blueprints/gke/autopilot/ansible/vars/vars.yaml -blueprints/gke/autopilot/bundle/monitoring/kustomization.yaml -blueprints/gke/autopilot/bundle/locust/kustomization.yaml -blueprints/gke/autopilot/bundle.tar.gz -blueprints/gke/patterns/batch/job-*.yaml diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index 8463ff16b6..9bc79f0e09 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -1,85 +1,36 @@ -/** - * Copyright 2024 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. - */ - -# tfdoc:file:description Automation project and resources. - locals { cicd_resman_sa = try(module.automation-tf-cicd-sa["resman"].iam_email, "") cicd_resman_r_sa = try(module.automation-tf-cicd-r-sa["resman"].iam_email, "") + + # Use the prefix directly + descriptive_name = var.prefix } module "automation-project" { - source = "../../../modules/project" - billing_account = var.billing_account.id - name = "iac-core-0" - parent = coalesce( - var.project_parent_ids.automation, "organizations/${var.organization.id}" - ) - prefix = local.prefix - contacts = ( - var.bootstrap_user != null || var.essential_contacts == null - ? {} - : { (var.essential_contacts) = ["ALL"] } - ) - # human (groups) IAM bindings - iam_by_principals = { - (local.principals.gcp-devops) = [ - "roles/iam.serviceAccountAdmin", - "roles/iam.serviceAccountTokenCreator", - ] - (local.principals.gcp-organization-admins) = [ - "roles/iam.serviceAccountTokenCreator", - "roles/iam.workloadIdentityPoolAdmin" - ] + source = "../../../modules/project" + billing_account = var.billing_account.id + name = "iac-core-0" + project_id = "iac-core-0" + parent = coalesce(var.project_parent_ids.automation, var.organization_id) + prefix = local.prefix + organization_id = var.organization_id + contacts = (var.bootstrap_user != null || var.essential_contacts == null ? {} : { (var.essential_contacts) = ["ALL"] }) + iam_by_principals = { + (local.principals.gcp-devops) = ["roles/iam.serviceAccountAdmin", "roles/iam.serviceAccountTokenCreator"] + (local.principals.gcp-organization-admins) = ["roles/iam.serviceAccountTokenCreator", "roles/iam.workloadIdentityPoolAdmin"] } - # machine (service accounts) IAM bindings iam = { - "roles/browser" = [ - module.automation-tf-resman-r-sa.iam_email - ] - "roles/owner" = [ - module.automation-tf-bootstrap-sa.iam_email - ] - "roles/cloudbuild.builds.editor" = [ - module.automation-tf-resman-sa.iam_email - ] - "roles/cloudbuild.builds.viewer" = [ - module.automation-tf-resman-r-sa.iam_email - ] - "roles/iam.serviceAccountAdmin" = [ - module.automation-tf-resman-sa.iam_email - ] - "roles/iam.serviceAccountViewer" = [ - module.automation-tf-resman-r-sa.iam_email - ] - "roles/iam.workloadIdentityPoolAdmin" = [ - module.automation-tf-resman-sa.iam_email - ] - "roles/iam.workloadIdentityPoolViewer" = [ - module.automation-tf-resman-r-sa.iam_email - ] - "roles/source.admin" = [ - module.automation-tf-resman-sa.iam_email - ] - "roles/source.reader" = [ - module.automation-tf-resman-r-sa.iam_email - ] - "roles/storage.admin" = [ - module.automation-tf-resman-sa.iam_email - ] + "roles/browser" = [module.automation-tf-resman-r-sa.iam_email] + "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] + "roles/cloudbuild.builds.editor" = [module.automation-tf-resman-sa.iam_email] + "roles/cloudbuild.builds.viewer" = [module.automation-tf-resman-r-sa.iam_email] + "roles/iam.serviceAccountAdmin" = [module.automation-tf-resman-sa.iam_email] + "roles/iam.serviceAccountViewer" = [module.automation-tf-resman-r-sa.iam_email] + "roles/iam.workloadIdentityPoolAdmin" = [module.automation-tf-resman-sa.iam_email] + "roles/iam.workloadIdentityPoolViewer" = [module.automation-tf-resman-r-sa.iam_email] + "roles/source.admin" = [module.automation-tf-resman-sa.iam_email] + "roles/source.reader" = [module.automation-tf-resman-r-sa.iam_email] + "roles/storage.admin" = [module.automation-tf-resman-sa.iam_email] (module.organization.custom_role_id["storage_viewer"]) = [ module.automation-tf-bootstrap-r-sa.iam_email, module.automation-tf-resman-r-sa.iam_email @@ -91,15 +42,12 @@ module "automation-project" { } iam_bindings = { delegated_grants_resman = { - members = [module.automation-tf-resman-sa.iam_email] - role = "roles/resourcemanager.projectIamAdmin" + members = [module.automation-tf-resman-sa.iam_email] + role = "roles/resourcemanager.projectIamAdmin" condition = { title = "resman_delegated_grant" description = "Resource manager service account delegated grant." - expression = format( - "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['%s'])", - "roles/serviceusage.serviceUsageConsumer" - ) + expression = format("api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly(['%s'])", "roles/serviceusage.serviceUsageConsumer") } } } @@ -114,15 +62,9 @@ module "automation-project" { } } org_policies = var.bootstrap_user != null ? {} : { - "compute.skipDefaultNetworkCreation" = { - rules = [{ enforce = true }] - } - "iam.automaticIamGrantsForDefaultServiceAccounts" = { - rules = [{ enforce = true }] - } - "iam.disableServiceAccountKeyCreation" = { - rules = [{ enforce = true }] - } + "compute.skipDefaultNetworkCreation" = { rules = [{ enforce = true }] } + "iam.automaticIamGrantsForDefaultServiceAccounts" = { rules = [{ enforce = true }] } + "iam.disableServiceAccountKeyCreation" = { rules = [{ enforce = true }] } } services = concat( [ @@ -149,31 +91,22 @@ module "automation-project" { "storage.googleapis.com", "sts.googleapis.com" ], - # enable specific service only after org policies have been applied var.bootstrap_user != null ? [] : [ "cloudbuild.googleapis.com", "compute.googleapis.com", "container.googleapis.com", ] ) - - # Enable IAM data access logs to capture impersonation and service - # account token generation events. This is implemented within the - # automation project to limit log volume. For heightened security, - # consider enabling it at the organization level. A log sink within - # the organization will collect and store these logs in a logging - # bucket. See - # https://cloud.google.com/iam/docs/audit-logging#audited_operations logging_data_access = { "iam.googleapis.com" = { - # ADMIN_READ captures impersonation and token generation/exchanges ADMIN_READ = [] - # enable DATA_WRITE if you want to capture configuration changes - # to IAM-related resources (roles, deny policies, service - # accounts, identity pools, etc) - # DATA_WRITE = [] } } + log_bucket_name = "iac-core-0-bkt-logs" + bucket_name = "iac-core-0-state" + service_account_email = module.automation-tf-resman-sa.iam_email + internal_service_account_email = module.automation-tf-bootstrap-sa.iam_email + kms_key_name = "iac-core-0-kms-key" } # output files bucket @@ -206,7 +139,7 @@ module "automation-tf-bootstrap-sa" { source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "bootstrap-0" - display_name = "Terraform organization bootstrap service account." + display_name = "Service account for bootstrap-0, bootstrap service account." prefix = local.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { @@ -223,7 +156,7 @@ module "automation-tf-bootstrap-r-sa" { source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "bootstrap-0r" - display_name = "Terraform organization bootstrap service account (read-only)." + display_name = "Service account for bootstrap-0r use for workflow (read-only)." prefix = local.prefix # allow SA used by CI/CD workflow to impersonate this SA iam = { @@ -265,7 +198,7 @@ module "automation-tf-resman-sa" { source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "resman-0" - display_name = "Terraform stage 1 resman service account." + display_name = "Service account stage 1 resman service account." prefix = local.prefix # allow SA used by CI/CD workflow to impersonate this SA # we use additive IAM to allow tenant CI/CD SAs to impersonate it @@ -286,7 +219,7 @@ module "automation-tf-resman-r-sa" { source = "../../../modules/iam-service-account" project_id = module.automation-project.project_id name = "resman-0r" - display_name = "Terraform stage 1 resman service account (read-only)." + display_name = "Service account stage 1 resman service account (read-only)." prefix = local.prefix # allow SA used by CI/CD workflow to impersonate this SA # we use additive IAM to allow tenant CI/CD SAs to impersonate it @@ -310,3 +243,130 @@ module "automation-tf-resman-r-sa" { (module.automation-tf-output-gcs.name) = [module.organization.custom_role_id["storage_viewer"]] } } + +# Log bucket for storing access logs +resource "google_storage_bucket" "log_bucket" { + name = "${var.log_bucket_name}-${local.descriptive_name}" + location = var.region + project = module.automation-project.project_id + storage_class = "STANDARD" + + versioning { + enabled = true + } + + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = 365 + } + } + + labels = { + environment = "production" + purpose = "logging" + } + + uniform_bucket_level_access = true +} + +resource "google_storage_bucket" "terraform_state" { + name = "${var.bucket_name}" # -${local.descriptive_name} + location = var.region + project = module.automation-project.project_id + storage_class = "STANDARD" + + versioning { + enabled = true + } + + lifecycle_rule { + action { + type = "SetStorageClass" + storage_class = "NEARLINE" + } + condition { + age = 365 + matches_storage_class = ["STANDARD", "DURABLE_REDUCED_AVAILABILITY"] + } + } + + lifecycle_rule { + action { + type = "SetStorageClass" + storage_class = "COLDLINE" + } + condition { + age = 1095 + matches_storage_class = ["NEARLINE"] + } + } + + lifecycle_rule { + action { + type = "SetStorageClass" + storage_class = "ARCHIVE" + } + condition { + age = 1825 + matches_storage_class = ["COLDLINE"] + } + } + + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = 2555 + } + } + + uniform_bucket_level_access = true + + logging { + log_bucket = google_storage_bucket.log_bucket.name + log_object_prefix = "logs/" + } + + # encryption { + # default_kms_key_name = var.kms_key_name + # } + + # retention_policy { + # retention_period = 365 * 3 // 3 years + # } + + labels = { + environment = "production" + purpose = "terraform-state" + } + + depends_on = [ + google_storage_bucket.log_bucket + ] +} + +resource "google_storage_bucket_iam_binding" "no_public_access" { + bucket = google_storage_bucket.terraform_state.name + role = "roles/storage.objectViewer" + + members = [ + "serviceAccount:${var.service_account_email}" + ] + + condition { + title = "No public access" + description = "Prevent public access" + expression = "request.auth != null" + } +} + +resource "google_storage_bucket_iam_member" "internal_only" { + bucket = google_storage_bucket.terraform_state.name + role = "roles/storage.objectViewer" + member = "serviceAccount:${var.service_account_email}" +} + diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index 2c1ce0e78f..59bcf89a1a 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -38,19 +38,20 @@ locals { # billing account in same org (IAM is in the organization.tf file) module "billing-export-project" { - source = "../../../modules/project" - count = local.billing_mode == "org" ? 1 : 0 - billing_account = var.billing_account.id - name = "billing-exp-0" - parent = coalesce( - var.project_parent_ids.billing, "organizations/${var.organization.id}" - ) - prefix = local.prefix - contacts = ( - var.bootstrap_user != null || var.essential_contacts == null - ? {} - : { (var.essential_contacts) = ["ALL"] } - ) + source = "../../../modules/project" + count = local.billing_mode == "org" ? 1 : 0 + billing_account = var.billing_account.id + name = "billing-exp-0" + project_id = "billing-exp-0" + organization_id = var.organization_id + service_account_email = module.automation-tf-bootstrap-sa.iam_email + internal_service_account_email = module.automation-tf-bootstrap-r-sa.iam_email + kms_key_name = "billing-exp-0-key" + log_bucket_name = "billing-exp-0-logs" + bucket_name = "billing-exp-0-data" + parent = coalesce(var.project_parent_ids.billing, var.organization_id) + prefix = local.prefix + contacts = (var.bootstrap_user != null || var.essential_contacts == null ? {} : { (var.essential_contacts) = ["ALL"] }) iam = { "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] "roles/viewer" = [module.automation-tf-bootstrap-r-sa.iam_email] diff --git a/fast/stages/0-bootstrap/bootstrap.tfplan b/fast/stages/0-bootstrap/bootstrap.tfplan deleted file mode 100644 index b45e3c511e..0000000000 Binary files a/fast/stages/0-bootstrap/bootstrap.tfplan and /dev/null differ diff --git a/fast/stages/0-bootstrap/log-export.tf b/fast/stages/0-bootstrap/log-export.tf index 86c8740756..0ee3587901 100644 --- a/fast/stages/0-bootstrap/log-export.tf +++ b/fast/stages/0-bootstrap/log-export.tf @@ -39,11 +39,10 @@ locals { module "log-export-project" { source = "../../../modules/project" name = "audit-logs-0" - parent = coalesce( - var.project_parent_ids.logging, "organizations/${var.organization.id}" - ) + parent = coalesce(var.project_parent_ids.logging, var.organization_id) prefix = local.prefix billing_account = var.billing_account.id + organization_id = var.organization_id contacts = ( var.bootstrap_user != null || var.essential_contacts == null ? {} @@ -61,6 +60,12 @@ module "log-export-project" { "storage.googleapis.com", "stackdriver.googleapis.com" ] + bucket_name = "audit-exp-0-data" + service_account_email = "log-export-sa@iha-prod-audit-logs-0.iam.gserviceaccount.com" + internal_service_account_email = "log-export-sa@iha-prod-audit-logs-0.iam.gserviceaccount.com" + kms_key_name = "audit-log-0-key" + log_bucket_name = "audit-exp-0-log" + project_id = "audit-exp-0" } # one log export per type, with conditionals to skip those not needed diff --git a/fast/stages/0-bootstrap/main.tf b/fast/stages/0-bootstrap/main.tf index b9153bddaf..6d92ed711a 100644 --- a/fast/stages/0-bootstrap/main.tf +++ b/fast/stages/0-bootstrap/main.tf @@ -35,4 +35,16 @@ locals { } # naming: environment used in most resource names prefix = join("-", compact([var.prefix, "prod"])) + + providers_template = templatefile("${abspath(path.module)}/templates/providers.tf.tpl", { + bucket = var.bucket + sa = var.sa + name = var.name + backend_extra = try(var.backend_extra, null) + }) +} + +resource "local_file" "bootstrap-providers" { + content = local.providers_template + filename = "${var.outputs_location}/0-bootstrap-providers.tf" } diff --git a/fast/stages/0-bootstrap/organization.tf b/fast/stages/0-bootstrap/organization.tf index fdd08937a7..438eca1cc5 100644 --- a/fast/stages/0-bootstrap/organization.tf +++ b/fast/stages/0-bootstrap/organization.tf @@ -99,26 +99,26 @@ locals { # TODO: add a check block to ensure our custom roles exist in the factory files # import org policy constraints enabled by default in new orgs since February 2024 -import { - for_each = ( - !var.org_policies_config.import_defaults || var.bootstrap_user != null - ? toset([]) - : toset([ - "compute.requireOsLogin", - "compute.skipDefaultNetworkCreation", - "compute.vmExternalIpAccess", - "iam.allowedPolicyMemberDomains", - "iam.automaticIamGrantsForDefaultServiceAccounts", - "iam.disableServiceAccountKeyCreation", - "iam.disableServiceAccountKeyUpload", - "sql.restrictAuthorizedNetworks", - "sql.restrictPublicIp", - "storage.uniformBucketLevelAccess", - ]) - ) - id = "organizations/${var.organization.id}/policies/${each.key}" - to = module.organization.google_org_policy_policy.default[each.key] -} +# import { +# for_each = ( +# !var.org_policies_config.import_defaults || var.bootstrap_user != null +# ? toset([]) +# : toset([ +# "compute.requireOsLogin", +# "compute.skipDefaultNetworkCreation", +# "compute.vmExternalIpAccess", +# "iam.allowedPolicyMemberDomains", +# "iam.automaticIamGrantsForDefaultServiceAccounts", +# "iam.disableServiceAccountKeyCreation", +# "iam.disableServiceAccountKeyUpload", +# "sql.restrictAuthorizedNetworks", +# "sql.restrictPublicIp", +# "storage.uniformBucketLevelAccess", +# ]) +# ) +# id = "organizations/${var.organization.id}/policies/${each.key}" +# to = module.organization.google_org_policy_policy.default[each.key] +# } module "organization-logging" { # Preconfigure organization-wide logging settings to ensure project diff --git a/fast/stages/0-bootstrap/templates/providers.tf.tpl b/fast/stages/0-bootstrap/templates/providers.tf.tpl index d1c224c5c1..deb0d3713c 100644 --- a/fast/stages/0-bootstrap/templates/providers.tf.tpl +++ b/fast/stages/0-bootstrap/templates/providers.tf.tpl @@ -25,9 +25,17 @@ terraform { } provider "google" { impersonate_service_account = "${sa}" + scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email" + ] } provider "google-beta" { impersonate_service_account = "${sa}" + scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email" + ] } # end provider.tf for ${name} diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index c460841d7c..a2e7e9efb6 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -119,13 +119,13 @@ variable "groups" { # https://cloud.google.com/docs/enterprise/setup-checklist description = "Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated." type = object({ - gcp-billing-admins = optional(string, "gcp-billing-admins") - gcp-devops = optional(string, "gcp-devops") - gcp-network-admins = optional(string, "gcp-vpc-network-admins") - gcp-organization-admins = optional(string, "gcp-organization-admins") - gcp-security-admins = optional(string, "gcp-security-admins") + gcp-billing-admins = optional(string, "gcp_billing_admins") + gcp-devops = optional(string, "gcp_devops") + gcp-network-admins = optional(string, "gcp_vpc_network_admins") + gcp-organization-admins = optional(string, "gcp_organization_admins") + gcp-security-admins = optional(string, "gcp_security_admins") # aliased to gcp-devops as the checklist does not create it - gcp-support = optional(string, "gcp-devops") + gcp-support = optional(string, "gcp_devops") }) nullable = false default = {} @@ -245,7 +245,7 @@ variable "organization" { variable "outputs_location" { description = "Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable." type = string - default = null + default = null # default = "~/fast-config" } variable "prefix" { @@ -255,6 +255,7 @@ variable "prefix" { condition = try(length(var.prefix), 0) < 10 error_message = "Use a maximum of 9 characters for prefix." } + default = "iha" } variable "project_parent_ids" { @@ -298,3 +299,69 @@ variable "workload_identity_providers" { default = {} nullable = false } + +variable "bucket" { + type = string + default = "iac-core-0-state-iha" +} + +variable "sa" { + type = string + default = "" +} + +variable "name" { + type = string + default = "cloud-foundation" +} + +variable "backend_extra" { + type = string + description = "Additional configuration for the backend." + default = null +} + +variable "project_id" { + description = "The ID of the GCP project" + type = string +} + +variable "region" { + description = "The region to deploy resources" + type = string + default = "us-central1" +} + +variable "bucket_name" { + description = "The name of the GCS bucket" + type = string +} + +variable "log_bucket_name" { + description = "The name of the GCS log bucket" + type = string +} + +variable "kms_key_name" { + description = "The name of the KMS key for encryption" + type = string +} + +variable "service_account_email" { + description = "The email of the service account for storage admin" + type = string +} + +variable "internal_service_account_email" { + description = "The email of the internal service account for bucket access" + type = string +} + +variable "organization_id" { + description = "The ID of the GCP organization in the form organizations/nnnnnn format." + type = string + validation { + condition = can(regex("^organizations/\\d+$", var.organization_id)) + error_message = "The organization_id must in the form organizations/nnn." + } +} \ No newline at end of file diff --git a/modules/organization/organization-policies.tf b/modules/organization/organization-policies.tf index 2faf2e97e1..74c939049e 100644 --- a/modules/organization/organization-policies.tf +++ b/modules/organization/organization-policies.tf @@ -74,50 +74,50 @@ locals { } } -resource "google_org_policy_policy" "default" { - for_each = local.org_policies - name = each.value.name - parent = each.value.parent - spec { - inherit_from_parent = each.value.inherit_from_parent - reset = each.value.reset - dynamic "rules" { - for_each = each.value.rules - iterator = rule - content { - allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null - deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null - enforce = ( - each.value.is_boolean_policy && rule.value.enforce != null - ? upper(tostring(rule.value.enforce)) - : null - ) - dynamic "condition" { - for_each = rule.value.condition.expression != null ? [1] : [] - content { - description = rule.value.condition.description - expression = rule.value.condition.expression - location = rule.value.condition.location - title = rule.value.condition.title - } - } - dynamic "values" { - for_each = rule.value.has_values ? [1] : [] - content { - allowed_values = try(rule.value.allow.values, null) - denied_values = try(rule.value.deny.values, null) - } - } - } - } - } - depends_on = [ - google_organization_iam_binding.authoritative, - google_organization_iam_binding.bindings, - google_organization_iam_member.bindings, - google_organization_iam_custom_role.roles, - google_org_policy_custom_constraint.constraint, - google_tags_tag_key.default, - google_tags_tag_value.default, - ] -} +# resource "google_org_policy_policy" "default" { +# for_each = local.org_policies +# name = each.value.name +# parent = each.value.parent +# spec { +# inherit_from_parent = each.value.inherit_from_parent +# reset = each.value.reset +# dynamic "rules" { +# for_each = each.value.rules +# iterator = rule +# content { +# allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null +# deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null +# enforce = ( +# each.value.is_boolean_policy && rule.value.enforce != null +# ? upper(tostring(rule.value.enforce)) +# : null +# ) +# dynamic "condition" { +# for_each = rule.value.condition.expression != null ? [1] : [] +# content { +# description = rule.value.condition.description +# expression = rule.value.condition.expression +# location = rule.value.condition.location +# title = rule.value.condition.title +# } +# } +# dynamic "values" { +# for_each = rule.value.has_values ? [1] : [] +# content { +# allowed_values = try(rule.value.allow.values, null) +# denied_values = try(rule.value.deny.values, null) +# } +# } +# } +# } +# } +# depends_on = [ +# google_organization_iam_binding.authoritative, +# google_organization_iam_binding.bindings, +# google_organization_iam_member.bindings, +# google_organization_iam_custom_role.roles, +# google_org_policy_custom_constraint.constraint, +# google_tags_tag_key.default, +# google_tags_tag_value.default, +# ] +# } diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index 9422134a73..0309a3cfb2 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -40,7 +40,7 @@ output "id" { depends_on = [ google_logging_organization_settings.default, google_org_policy_custom_constraint.constraint, - google_org_policy_policy.default, + # google_org_policy_policy.default, google_organization_iam_binding.authoritative, google_organization_iam_binding.bindings, google_organization_iam_custom_role.roles, @@ -76,7 +76,7 @@ output "organization_id" { value = var.organization_id depends_on = [ google_org_policy_custom_constraint.constraint, - google_org_policy_policy.default, + # google_org_policy_policy.default, google_organization_iam_binding.authoritative, google_organization_iam_binding.bindings, google_organization_iam_member.bindings, diff --git a/modules/project/main.tf b/modules/project/main.tf index 90eee38b8e..89b2f23383 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -1,23 +1,6 @@ -/** - * 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. - */ - locals { - descriptive_name = ( - var.descriptive_name != null ? var.descriptive_name : "${local.prefix}${var.name}" - ) + descriptive_name = "${var.prefix}-${formatdate("YYYYMMDD-HHmmss", timestamp())}" + parent_type = var.parent == null ? null : split("/", var.parent)[0] parent_id = var.parent == null ? null : split("/", var.parent)[1] prefix = var.prefix == null ? "" : "${var.prefix}-" @@ -100,3 +83,139 @@ resource "google_monitoring_monitored_project" "primary" { metrics_scope = each.value name = local.project.project_id } + +# resource "google_storage_bucket" "log_bucket" { +# name = "${var.log_bucket_name}ibb" +# location = var.region +# project = local.project.project_id +# storage_class = "STANDARD" + +# versioning { +# enabled = true +# } + +# lifecycle_rule { +# action { +# type = "Delete" +# } +# condition { +# age = 365 +# } +# } + +# labels = { +# environment = "production" +# purpose = "logging" +# } + +# uniform_bucket_level_access = true +# } + +# resource "google_storage_bucket" "terraform_state" { +# name = "${var.bucket_name}ibb" +# location = var.region +# project = local.project.project_id +# storage_class = "STANDARD" +# force_destroy = true + +# # versioning { +# # enabled = true +# # } + +# lifecycle_rule { +# action { +# type = "SetStorageClass" +# storage_class = "NEARLINE" +# } +# condition { +# age = 365 +# matches_storage_class = ["STANDARD", "DURABLE_REDUCED_AVAILABILITY"] +# } +# } + +# lifecycle_rule { +# action { +# type = "SetStorageClass" +# storage_class = "COLDLINE" +# } +# condition { +# age = 1095 +# matches_storage_class = ["NEARLINE"] +# } +# } + +# lifecycle_rule { +# action { +# type = "SetStorageClass" +# storage_class = "ARCHIVE" +# } +# condition { +# age = 1825 +# matches_storage_class = ["COLDLINE"] +# } +# } + +# lifecycle_rule { +# action { +# type = "Delete" +# } +# condition { +# age = 2555 +# } +# } + +# uniform_bucket_level_access = true + +# logging { +# log_bucket = google_storage_bucket.log_bucket.name +# log_object_prefix = "logs/" +# } + +# # encryption { +# # default_kms_key_name = var.kms_key_name +# # } + +# retention_policy { +# retention_period = 365 * 3 // 3 years +# } + +# labels = { +# environment = "production" +# purpose = "terraform-state" +# } + +# depends_on = [ +# google_project.project, +# google_storage_bucket.log_bucket +# ] +# } + +# resource "google_project_iam_binding" "storage_admin" { +# project = local.project.project_id +# role = "roles/storage.admin" + +# members = [ +# "serviceAccount:${var.service_account_email}" +# ] +# } + +# resource "google_storage_bucket_iam_binding" "no_public_access" { +# bucket = google_storage_bucket.terraform_state.name +# role = "roles/storage.objectViewer" + +# members = [ +# "serviceAccount:${var.service_account_email}" +# ] + +# condition { +# title = "No public access" +# description = "Prevent public access" +# expression = "request.auth != null" +# } +# } + +# resource "google_storage_bucket_iam_member" "internal_only" { +# bucket = google_storage_bucket.terraform_state.name +# role = "roles/storage.objectViewer" +# member = "serviceAccount:${var.internal_service_account_email}" +# } diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 4ff0cf93be..02c308ac16 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -284,3 +284,48 @@ variable "vpc_sc" { }) default = null } + +variable "project_id" { + description = "The ID of the GCP project" + type = string +} + +variable "region" { + description = "The region to deploy resources" + type = string + default = "us-central1" +} + +variable "bucket_name" { + description = "The name of the GCS bucket" + type = string +} + +variable "log_bucket_name" { + description = "The name of the GCS log bucket" + type = string +} + +variable "kms_key_name" { + description = "The name of the KMS key for encryption" + type = string +} + +variable "service_account_email" { + description = "The email of the service account for storage admin" + type = string +} + +variable "internal_service_account_email" { + description = "The email of the internal service account for bucket access" + type = string +} + +variable "organization_id" { + description = "Organization id in organizations/nnnnnn format." + type = string + validation { + condition = can(regex("^organizations/[0-9]+", var.organization_id)) + error_message = "The organization_id must in the form organizations/nnn." + } +} \ No newline at end of file