diff --git a/fast/stages/2-secops/.fast-stage.env b/fast/stages/2-secops/.fast-stage.env deleted file mode 100644 index 57d50b2e5..000000000 --- a/fast/stages/2-secops/.fast-stage.env +++ /dev/null @@ -1,4 +0,0 @@ -FAST_STAGE_DESCRIPTION="secops" -FAST_STAGE_LEVEL=2 -FAST_STAGE_NAME=secops -FAST_STAGE_DEPS="0-globals 0-org-setup 1-resman" diff --git a/fast/stages/2-secops/README.md b/fast/stages/2-secops/README.md deleted file mode 100644 index fa0d61de3..000000000 --- a/fast/stages/2-secops/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# SecOps Stage - -This stage sets up an area dedicated to hosting SecOps projects in the Google Cloud organization. - -The design of this stage is fairly simple, as it is only responsible for creating GCP projects that will be linked to SecOps instances as per the [following documentation](https://cloud.google.com/chronicle/docs/onboard/configure-cloud-project). - -After creating the projects please refer to your Google Cloud Security representative for instructions on how to bind your Google SecOps instance to the Google Cloud project/s created in this stage. - -The following diagram illustrates the high-level design of resources managed here: - -
-
-
google_iam_workforce_pool · google_iam_workforce_pool_provider |
-| [main.tf](./main.tf) | Module-level locals and resources. | folder · project | |
-| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file |
-| [variables-fast.tf](./variables-fast.tf) | None | | |
-| [variables.tf](./variables.tf) | Module variables. | | |
-
-## Variables
-
-| name | description | type | required | default | producer |
-|---|---|:---:|:---:|:---:|:---:|
-| [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-org-setup |
-| [billing_account](variables-fast.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-org-setup |
-| [environments](variables-fast.tf#L47) | Environment names. | map(object({…})) | ✓ | | 0-globals |
-| [folder_ids](variables-fast.tf#L65) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman |
-| [organization](variables-fast.tf#L75) | Organization details. | object({…}) | ✓ | | 0-org-setup |
-| [prefix](variables-fast.tf#L86) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-org-setup |
-| [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-org-setup |
-| [essential_contacts](variables.tf#L17) | Email used for essential contacts, unset if null. | string | | null | |
-| [outputs_location](variables.tf#L23) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | |
-| [stage_config](variables-fast.tf#L96) | FAST stage configuration. | object({…}) | | {} | 1-resman |
-| [tag_values](variables-fast.tf#L110) | Root-level tag values. | map(string) | | {} | 1-resman |
-| [workforce_identity_providers](variables.tf#L29) | Workforce Identity Federation pools. | map(object({…})) | | {} | |
-
-## Outputs
-
-| name | description | sensitive | consumers |
-|---|---|:---:|---|
-| [federated_identity_pool](outputs.tf#L48) | Workforce Identity Federation pool. | | |
-| [secops_project_ids](outputs.tf#L53) | SecOps project IDs. | | |
-| [tfvars](outputs.tf#L58) | Terraform variable files for the following stages. | ✓ | |
-
diff --git a/fast/stages/2-secops/diagram.png b/fast/stages/2-secops/diagram.png
deleted file mode 100644
index 8eb9fd7de..000000000
Binary files a/fast/stages/2-secops/diagram.png and /dev/null differ
diff --git a/fast/stages/2-secops/fast_version.txt b/fast/stages/2-secops/fast_version.txt
deleted file mode 100644
index c7701e1d1..000000000
--- a/fast/stages/2-secops/fast_version.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2025 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
-#
-# https://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.
-
-# FAST release: v48.0.0
\ No newline at end of file
diff --git a/fast/stages/2-secops/identity-providers-defs.tf b/fast/stages/2-secops/identity-providers-defs.tf
deleted file mode 100644
index f4d6b3029..000000000
--- a/fast/stages/2-secops/identity-providers-defs.tf
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright 2025 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 Workforce Identity provider definitions.
-
-locals {
- workforce_identity_providers_defs = {
- azuread = {
- attribute_mapping = {
- "google.subject" = "assertion.subject"
- "google.display_name" = "assertion.attributes.userprincipalname[0]"
- "google.groups" = "assertion.attributes.groups"
- "attribute.first_name" = "assertion.attributes.givenname[0]"
- "attribute.last_name" = "assertion.attributes.surname[0]"
- "attribute.user_email" = "assertion.attributes.mail[0]"
- }
- }
- okta = {
- attribute_mapping = {
- "google.subject" = "assertion.subject"
- "google.display_name" = "assertion.subject"
- "google.groups" = "assertion.attributes.groups"
- "attribute.first_name" = "assertion.attributes.firstName[0]"
- "attribute.last_name" = "assertion.attributes.lastName[0]"
- "attribute.user_email" = "assertion.attributes.email[0]"
- }
- }
- }
-}
diff --git a/fast/stages/2-secops/identity-providers.tf b/fast/stages/2-secops/identity-providers.tf
deleted file mode 100644
index 6c22d6349..000000000
--- a/fast/stages/2-secops/identity-providers.tf
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright 2025 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 Workforce Identity Federation provider definitions.
-
-locals {
- workforce_identity_providers = {
- for k, v in var.workforce_identity_providers : k => merge(
- v,
- lookup(local.workforce_identity_providers_defs, v.issuer, {})
- )
- }
-}
-
-resource "google_iam_workforce_pool" "default" {
- count = length(local.workforce_identity_providers) > 0 ? 1 : 0
- parent = "organizations/${var.organization.id}"
- location = "global"
- workforce_pool_id = "secops"
-}
-
-resource "google_iam_workforce_pool_provider" "default" {
- for_each = local.workforce_identity_providers
- attribute_condition = each.value.attribute_condition
- attribute_mapping = each.value.attribute_mapping
- description = each.value.description
- disabled = each.value.disabled
- display_name = each.value.display_name
- location = google_iam_workforce_pool.default[0].location
- provider_id = each.key
- workforce_pool_id = google_iam_workforce_pool.default[0].workforce_pool_id
- saml {
- idp_metadata_xml = each.value.saml.idp_metadata_xml
- }
-}
diff --git a/fast/stages/2-secops/main.tf b/fast/stages/2-secops/main.tf
deleted file mode 100644
index 7b643ee3d..000000000
--- a/fast/stages/2-secops/main.tf
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Copyright 2025 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 {
- has_env_folders = var.folder_ids.secops-dev != null
- iam_delegated = join(",", formatlist("'%s'", [
- "roles/cloudkms.cryptoKeyEncrypterDecrypter"
- ]))
- iam_admin_delegated = try(
- var.stage_config["secops"].iam_admin_delegated, {}
- )
- iam_viewer = try(
- var.stage_config["secops"].iam_viewer, {}
- )
- project_services = [
- "chronicle.googleapis.com",
- "stackdriver.googleapis.com"
- ]
-}
-
-module "folder" {
- source = "../../../modules/folder"
- folder_create = false
- id = var.folder_ids.secops
- contacts = (
- var.essential_contacts == null
- ? {}
- : { (var.essential_contacts) = ["ALL"] }
- )
-}
-
-module "project" {
- source = "../../../modules/project"
- for_each = var.environments
- name = "${each.value.short_name}-secops-0"
- parent = coalesce(
- var.folder_ids["secops-${each.key}"], var.folder_ids.secops
- )
- prefix = var.prefix
- billing_account = var.billing_account.id
- labels = { environment = each.key }
- services = local.project_services
- tag_bindings = local.has_env_folders ? {} : {
- environment = var.tag_values["environment/${each.value.tag_name}"]
- }
- # optionally delegate a fixed set of IAM roles to selected principals
- iam = {
- (var.custom_roles.project_iam_viewer) = try(
- local.iam_viewer[each.key], []
- )
- }
- iam_bindings = (
- lookup(local.iam_admin_delegated, each.key, null) == null ? {} : {
- sa_delegated_grants = {
- role = "roles/resourcemanager.projectIamAdmin"
- members = try(local.iam_admin_delegated[each.key], [])
- condition = {
- title = "${each.key}_stage3_sa_delegated_grants"
- description = "${var.environments[each.key].name} project delegated grants."
- expression = format(
- "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])",
- local.iam_delegated
- )
- }
- }
- }
- )
-}
diff --git a/fast/stages/2-secops/moved/.gitkeep b/fast/stages/2-secops/moved/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/fast/stages/2-secops/outputs.tf b/fast/stages/2-secops/outputs.tf
deleted file mode 100644
index 90805e726..000000000
--- a/fast/stages/2-secops/outputs.tf
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Copyright 2025 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 {
- tfvars = {
- federated_identity_pool = try(
- google_iam_workforce_pool.default[0].name, null
- )
- secops_project_ids = {
- for k, v in module.project : k => v.id
- }
- }
-}
-
-resource "local_file" "tfvars" {
- for_each = var.outputs_location == null ? {} : { 1 = 1 }
- file_permission = "0644"
- filename = "${pathexpand(var.outputs_location)}/tfvars/2-secops.auto.tfvars.json"
- content = jsonencode(local.tfvars)
-}
-
-resource "google_storage_bucket_object" "tfvars" {
- bucket = var.automation.outputs_bucket
- name = "tfvars/2-security.auto.tfvars.json"
- content = jsonencode(local.tfvars)
-}
-
-resource "google_storage_bucket_object" "version" {
- count = fileexists("fast_version.txt") ? 1 : 0
- bucket = var.automation.outputs_bucket
- name = "versions/2-secops-version.txt"
- source = "fast_version.txt"
-}
-
-output "federated_identity_pool" {
- description = "Workforce Identity Federation pool."
- value = local.tfvars.federated_identity_pool
-}
-
-output "secops_project_ids" {
- description = "SecOps project IDs."
- value = local.tfvars.secops_project_ids
-}
-
-output "tfvars" {
- description = "Terraform variable files for the following stages."
- sensitive = true
- value = local.tfvars
-}
diff --git a/fast/stages/2-secops/variables-fast.tf b/fast/stages/2-secops/variables-fast.tf
deleted file mode 100644
index c7ff6ca2d..000000000
--- a/fast/stages/2-secops/variables-fast.tf
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Copyright 2025 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 "automation" {
- # tfdoc:variable:source 0-org-setup
- description = "Automation resources created by the bootstrap stage."
- type = object({
- outputs_bucket = string
- })
-}
-
-variable "billing_account" {
- # tfdoc:variable:source 0-org-setup
- description = "Billing account id. If billing account is not part of the same org set `is_org_level` to false."
- type = object({
- id = string
- is_org_level = optional(bool, true)
- })
- validation {
- condition = var.billing_account.is_org_level != null
- error_message = "Invalid `null` value for `billing_account.is_org_level`."
- }
-}
-
-variable "custom_roles" {
- # tfdoc:variable:source 0-org-setup
- description = "Custom roles defined at the org level, in key => id format."
- type = object({
- project_iam_viewer = string
- })
- default = null
-}
-
-variable "environments" {
- # tfdoc:variable:source 0-globals
- description = "Environment names."
- type = map(object({
- name = string
- short_name = string
- tag_name = string
- is_default = optional(bool, false)
- }))
- nullable = false
- validation {
- condition = anytrue([
- for k, v in var.environments : v.is_default == true
- ])
- error_message = "At least one environment should be marked as default."
- }
-}
-
-variable "folder_ids" {
- # tfdoc:variable:source 1-resman
- description = "Folder name => id mappings, the 'security' folder name must exist."
- type = object({
- secops = string
- secops-dev = optional(string)
- secops-prod = optional(string)
- })
-}
-
-variable "organization" {
- # tfdoc:variable:source 0-org-setup
- description = "Organization details."
- type = object({
- domain = string
- id = number
- customer_id = string
- })
- nullable = false
-}
-
-variable "prefix" {
- # tfdoc:variable:source 0-org-setup
- description = "Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants."
- type = string
- validation {
- condition = try(length(var.prefix), 0) < 12
- error_message = "Use a maximum of 9 chars for organizations, and 11 chars for tenants."
- }
-}
-
-variable "stage_config" {
- # tfdoc:variable:source 1-resman
- description = "FAST stage configuration."
- type = object({
- security = optional(object({
- short_name = optional(string)
- iam_admin_delegated = optional(map(list(string)), {})
- iam_viewer = optional(map(list(string)), {})
- }), {})
- })
- default = {}
- nullable = false
-}
-
-variable "tag_values" {
- # tfdoc:variable:source 1-resman
- description = "Root-level tag values."
- type = map(string)
- default = {}
-}
diff --git a/fast/stages/2-secops/variables.tf b/fast/stages/2-secops/variables.tf
deleted file mode 100644
index da3468210..000000000
--- a/fast/stages/2-secops/variables.tf
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright 2025 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 "essential_contacts" {
- description = "Email used for essential contacts, unset if null."
- type = string
- default = null
-}
-
-variable "outputs_location" {
- description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable."
- type = string
- default = null
-}
-
-variable "workforce_identity_providers" {
- description = "Workforce Identity Federation pools."
- type = map(object({
- attribute_condition = optional(string)
- issuer = string
- display_name = string
- description = string
- disabled = optional(bool, false)
- saml = optional(object({
- idp_metadata_xml = string
- }), null)
- }))
- default = {}
- nullable = false
-}
diff --git a/fast/stages/3-secops-dev/README.md b/fast/stages/3-secops-dev/README.md
index 71d133eaa..8a54b0f29 100644
--- a/fast/stages/3-secops-dev/README.md
+++ b/fast/stages/3-secops-dev/README.md
@@ -1,8 +1,6 @@
# SecOps Stage
-This stage allows automated configuration of SecOps instance at both infrastructure and application level.
-
-The following diagram illustrates the high-level design of SecOps instance configuration in both GCP and SecOps instance, which can be adapted to specific requirements via variables.
+This stage allows automated configuration of a SecOps instance at both infrastructure and application level. The following diagram illustrates the high-level design.
@@ -23,20 +21,20 @@ The following diagram illustrates the high-level design of SecOps instance confi
## Design overview and choices
-The general idea behind this stage is to configure a single SecOps instance for a specific environment with configurations both on SecOps leveraging terraform resources (where available) and `restful_resource` for interacting with the new [SecOps APIs](https://cloud.google.com/chronicle/docs/reference/rest).
+The general idea behind this stage is to configure a single SecOps instance for a specific environment with configurations for SecOps leveraging native Terraform resources (where available) and the `restful_resource` for interacting with the new [SecOps APIs](https://cloud.google.com/chronicle/docs/reference/rest).
-Some high level features of the current version of the stage are:
+Some high level features of this stage are:
- API/Services enablement
- Data RBAC configuration with labels and scopes
-- IAM setup for the SecOps instance based on groups from Cloud Identity or WIF (with supports for Data RBAC)
-- Detection Rules and reference lists management via terraform (leveraging [secops-rules](../../../modules/secops-rules) module)
+- IAM setup for the SecOps instance based on Cloud Identity groups or WIF (with support for Data RBAC)
+- Detection Rules and reference lists management via Terraform (leveraging the [secops-rules](../../../modules/secops-rules) module)
- API Key setup for Webhook feeds
-- Integration with Workspace for alerts and logs ingestion via SecOps Feeds
+- Integration with Workspace for alert and log ingestion via SecOps Feeds
## How to run this stage
-If this stage is deployed within a FAST-based GCP organization, we recommend executing it after foundational FAST `stage-2` components like `networking` and `security`. This is the recommended flow as specific data platform features in this stage might depend on configurations from these earlier stages. Although this stage can be run independently, instructions for such a standalone setup are beyond the scope of this document.
+If this stage is deployed within a FAST-based GCP organization, we recommend executing it after foundational FAST `stage-2` components like `networking` and `security`. This is the recommended flow as specific features in this stage might depend on configurations from these earlier stages. Although this stage can be run independently, instructions for such a standalone setup are beyond the scope of this document.
### FAST prerequisites
@@ -46,8 +44,6 @@ Network permissions are needed to associate data domain or product projects to S
Security permissions are only needed when using CMEK encryption, to grant the relevant IAM roles to data platform service agents on the encryption keys used.
-The ["Classic FAST" dataset](../0-org-setup/README.md#classic-fast-dataset) in the bootstrap stage contains the configuration for a development Data Platform that can be easily adapted to serve for this stage.
-
## Customizations
This stage is designed with few basic integrations provided out of the box which can be customized as per the following sections.
@@ -136,19 +132,25 @@ Please be aware the Service Account Client ID needed during domain wide delegati
| name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:|
| [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-org-setup |
-| [tenant_config](variables.tf#L118) | SecOps Tenant configuration. | object({…}) | ✓ | | |
+| [prefix](variables-fast.tf#L67) | Prefix for organization projects. | string | ✓ | | 0-org-setup |
+| [tenant_config](variables.tf#L139) | SecOps Tenant configuration. | object({…}) | ✓ | | |
| [billing_account](variables-fast.tf#L26) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | | {} | 0-org-setup |
-| [data_rbac_config](variables.tf#L17) | SecOps Data RBAC scope and labels config. | object({…}) | | {} | |
-| [factories_config](variables.tf#L51) | Paths to YAML config expected in 'rules' and 'reference_lists'. Path to folders containing rules definitions (yaral files) and reference lists content (txt files) for the corresponding _defs keys. | object({…}) | | {…} | |
-| [folder_ids](variables-fast.tf#L35) | Folder name => id mappings. | map(string) | | {} | 1-resman |
-| [iam](variables.tf#L68) | SecOps IAM configuration in {PRINCIPAL => {roles => [ROLES], scopes => [SCOPES]}} format. | map(object({…})) | | {} | |
-| [iam_default](variables.tf#L78) | Groups ID in IdP assigned to SecOps admins, editors, viewers roles. | object({…}) | | {} | |
-| [project_id](variables.tf#L88) | Project id that references existing SecOps project. Use this variable when running this stage in isolation. | string | | null | |
-| [project_reuse](variables.tf#L94) | Whether to use an existing project, leave default for FAST deployment. | map(string) | | {} | |
-| [region](variables.tf#L100) | Google Cloud region definition for resources. | string | | "europe-west8" | |
-| [secops_project_ids](variables-fast.tf#L43) | SecOps Project IDs for each environment. | map(string) | | null | 2-secops |
-| [stage_config](variables.tf#L106) | FAST stage configuration used to find resource ids. Must match name defined for the stage in resource management. | object({…}) | | {…} | |
-| [workspace_integration_config](variables.tf#L126) | SecOps Feeds configuration for Workspace logs and entities ingestion. | object({…}) | | null | |
+| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} | |
+| [custom_roles](variables-fast.tf#L35) | Custom roles defined at the org level, in key => id format. | map(string) | | {} | 0-org-setup |
+| [data_rbac_config](variables.tf#L30) | SecOps Data RBAC scope and labels config. | object({…}) | | {} | |
+| [factories_config](variables.tf#L64) | Paths to YAML config expected in 'rules' and 'reference_lists'. Path to folders containing rules definitions (yaral files) and reference lists content (txt files) for the corresponding _defs keys. | object({…}) | | {…} | |
+| [folder_ids](variables-fast.tf#L43) | Folders created in the bootstrap stage. | map(string) | | {} | 0-org-setup |
+| [iam](variables.tf#L81) | SecOps IAM configuration in {PRINCIPAL => {roles => [ROLES], scopes => [SCOPES]}} format. | map(object({…})) | | {} | |
+| [iam_default](variables.tf#L91) | Groups ID in IdP assigned to SecOps admins, editors, viewers roles. | object({…}) | | {} | |
+| [iam_principals](variables-fast.tf#L51) | IAM-format principals. | map(string) | | {} | 0-org-setup |
+| [kms_keys](variables-fast.tf#L59) | KMS key ids. | map(string) | | {} | 2-security |
+| [parent_folder](variables.tf#L101) | Folder to use for created project. | string | | "$folder_ids:secops/dev" | |
+| [project_id](variables.tf#L108) | Project id for newly created project, or id of existing project if project_create is false. | string | | "dev-secops-core-0" | |
+| [project_ids](variables-fast.tf#L74) | Projects created in the bootstrap stage. | map(string) | | {} | 0-org-setup |
+| [project_reuse](variables.tf#L115) | Whether to use an existing project. | map(string) | | null | |
+| [region](variables.tf#L121) | Google Cloud region definition for resources. | string | | "europe-west8" | |
+| [stage_config](variables.tf#L127) | FAST stage configuration used to find resource ids. Must match name defined for the stage in resource management. | object({…}) | | {…} | |
+| [workspace_integration_config](variables.tf#L147) | SecOps Feeds configuration for Workspace logs and entities ingestion. | object({…}) | | null | |
## Outputs
diff --git a/fast/stages/3-secops-dev/main.tf b/fast/stages/3-secops-dev/main.tf
index 9bdf90017..483e3147f 100644
--- a/fast/stages/3-secops-dev/main.tf
+++ b/fast/stages/3-secops-dev/main.tf
@@ -17,36 +17,46 @@
locals {
secops_api_key_secret_key = "secops-feeds-api-key"
secops_workspace_int_sa_key = "secops-workspace-ing-sa-key"
- secops_project_id = coalesce(try(var.secops_project_ids[var.stage_config.environment], null), var.project_id)
- secops_feeds_api_path = "projects/${module.project.project_id}/locations/${var.tenant_config.region}/instances/${var.tenant_config.customer_id}/feeds"
- workspace_log_ingestion = var.workspace_integration_config != null
+ secops_feeds_api_path = (
+ "projects/${module.project.project_id}/locations/${var.tenant_config.region}/instances/${var.tenant_config.customer_id}/feeds"
+ )
+ workspace_log_ingestion = var.workspace_integration_config != null
}
module "project" {
source = "../../../modules/project"
billing_account = var.project_reuse == null ? var.billing_account.id : null
- name = local.secops_project_id
- parent = var.folder_ids[var.stage_config.name]
+ name = var.project_id
+ parent = var.parent_folder
+ prefix = var.prefix
project_reuse = var.project_reuse
org_policies = var.workspace_integration_config != null ? {
"iam.disableServiceAccountKeyCreation" = {
rules = [{ enforce = false }]
}
} : {}
- services = concat([
- "apikeys.googleapis.com",
- "compute.googleapis.com",
- "iap.googleapis.com",
- "secretmanager.googleapis.com",
- "stackdriver.googleapis.com",
- "pubsub.googleapis.com",
- "cloudfunctions.googleapis.com",
+ services = concat(
+ [
+ "apikeys.googleapis.com",
+ "compute.googleapis.com",
+ "iap.googleapis.com",
+ "secretmanager.googleapis.com",
+ "stackdriver.googleapis.com",
+ "pubsub.googleapis.com",
+ "cloudfunctions.googleapis.com",
],
var.workspace_integration_config != null ? [
"admin.googleapis.com",
"alertcenter.googleapis.com"
] : [],
)
+ context = {
+ custom_roles = merge(var.custom_roles, var.context.custom_roles)
+ folder_ids = merge(var.folder_ids, var.context.folder_ids)
+ iam_principals = merge(var.iam_principals, var.context.iam_principals)
+ kms_keys = merge(var.kms_keys, var.context.kms_keys)
+ project_ids = merge(var.project_ids, var.context.project_ids)
+ }
custom_roles = {
"secopsDashboardViewer" = [
"chronicle.dashboardCharts.get",
@@ -69,14 +79,26 @@ module "project" {
}
iam = {}
iam_bindings_additive = merge(
- { for group in var.iam_default.admins :
- "${group}-admins" => { member = "group:${group}", role = "roles/chronicle.admin" } },
- { for group in var.iam_default.editors :
- "${group}-editors" => { member = "group:${group}", role = "roles/chronicle.editor" } },
- { for group in var.iam_default.editors :
- "${group}-viewers" => { member = "group:${group}", role = "roles/chronicle.viewer" } },
- { for k, v in var.iam :
- k => {
+ {
+ for group in var.iam_default.admins : "${group}-admins" => {
+ member = "group:${group}"
+ role = "roles/chronicle.admin"
+ }
+ },
+ {
+ for group in var.iam_default.editors : "${group}-editors" => {
+ member = "group:${group}"
+ role = "roles/chronicle.editor"
+ }
+ },
+ {
+ for group in var.iam_default.editors : "${group}-viewers" => {
+ member = "group:${group}"
+ role = "roles/chronicle.viewer"
+ }
+ },
+ {
+ for k, v in var.iam : k => {
member = k
role = "roles/chronicle.restrictedDataAccess"
condition = {
@@ -85,7 +107,8 @@ module "project" {
description = "datarbac"
}
}
- })
+ }
+ )
iam_by_principals_additive = { for k, v in var.iam : k => v.roles }
}
@@ -93,7 +116,6 @@ resource "google_apikeys_key" "feed_api_key" {
project = module.project.project_id
name = "secops-feed-key"
display_name = "SecOps Feeds API Key"
-
restrictions {
api_targets {
service = "chronicle.googleapis.com"
@@ -103,7 +125,7 @@ resource "google_apikeys_key" "feed_api_key" {
module "secops-rules" {
source = "../../../modules/secops-rules"
- project_id = local.secops_project_id
+ project_id = var.project_id
tenant_config = {
region = var.tenant_config.region
customer_id = var.tenant_config.customer_id
diff --git a/fast/stages/3-secops-dev/variables-fast.tf b/fast/stages/3-secops-dev/variables-fast.tf
index ccbf95a5d..cb9261050 100644
--- a/fast/stages/3-secops-dev/variables-fast.tf
+++ b/fast/stages/3-secops-dev/variables-fast.tf
@@ -32,17 +32,49 @@ variable "billing_account" {
default = {}
}
-variable "folder_ids" {
- # tfdoc:variable:source 1-resman
- description = "Folder name => id mappings."
+variable "custom_roles" {
+ # tfdoc:variable:source 0-org-setup
+ description = "Custom roles defined at the org level, in key => id format."
type = map(string)
nullable = false
default = {}
}
-variable "secops_project_ids" {
- # tfdoc:variable:source 2-secops
- description = "SecOps Project IDs for each environment."
+variable "folder_ids" {
+ # tfdoc:variable:source 0-org-setup
+ description = "Folders created in the bootstrap stage."
type = map(string)
- default = null
+ nullable = false
+ default = {}
+}
+
+variable "iam_principals" {
+ # tfdoc:variable:source 0-org-setup
+ description = "IAM-format principals."
+ type = map(string)
+ nullable = false
+ default = {}
+}
+
+variable "kms_keys" {
+ # tfdoc:variable:source 2-security
+ description = "KMS key ids."
+ type = map(string)
+ nullable = false
+ default = {}
+}
+
+variable "prefix" {
+ # tfdoc:variable:source 0-org-setup
+ description = "Prefix for organization projects."
+ type = string
+ nullable = false
+}
+
+variable "project_ids" {
+ # tfdoc:variable:source 0-org-setup
+ description = "Projects created in the bootstrap stage."
+ type = map(string)
+ nullable = false
+ default = {}
}
diff --git a/fast/stages/3-secops-dev/variables.tf b/fast/stages/3-secops-dev/variables.tf
index 2c09caf80..1a5c978a8 100644
--- a/fast/stages/3-secops-dev/variables.tf
+++ b/fast/stages/3-secops-dev/variables.tf
@@ -14,6 +14,19 @@
* limitations under the License.
*/
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ custom_roles = optional(map(string), {})
+ folder_ids = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ kms_keys = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "data_rbac_config" {
description = "SecOps Data RBAC scope and labels config."
type = object({
@@ -85,16 +98,24 @@ variable "iam_default" {
default = {}
}
-variable "project_id" {
- description = "Project id that references existing SecOps project. Use this variable when running this stage in isolation."
+variable "parent_folder" {
+ description = "Folder to use for created project."
type = string
- default = null
+ nullable = false
+ default = "$folder_ids:secops/dev"
+}
+
+variable "project_id" {
+ description = "Project id for newly created project, or id of existing project if project_create is false."
+ type = string
+ nullable = false
+ default = "dev-secops-core-0"
}
variable "project_reuse" {
- description = "Whether to use an existing project, leave default for FAST deployment."
+ description = "Whether to use an existing project."
type = map(string)
- default = {}
+ default = null
}
variable "region" {
diff --git a/fast/stages/3-secops-dev/workspace.tf b/fast/stages/3-secops-dev/workspace.tf
index 6904575a5..d711234b5 100644
--- a/fast/stages/3-secops-dev/workspace.tf
+++ b/fast/stages/3-secops-dev/workspace.tf
@@ -68,25 +68,36 @@ resource "restful_resource" "workspace_feeds" {
"display_name" : each.key,
"details" : {
"feed_source_type" : "API",
- "log_type" : "projects/${module.project.project_id}/locations/${var.tenant_config.region}/instances/${var.tenant_config.customer_id}/logTypes/${each.value.log_type}",
+ "log_type" : (
+ "projects/${module.project.project_id}/locations/${var.tenant_config.region}/instances/${var.tenant_config.customer_id}/logTypes/${each.value.log_type}"
+ ),
"asset_namespace" : "",
"labels" : {},
- (each.value.feed_type) : merge({
- "authentication" : {
- "token_endpoint" : "https://oauth2.googleapis.com/token",
- "claims" : {
- "issuer" : module.workspace-integration-sa[0].email,
- "subject" : var.workspace_integration_config.delegated_user,
- "audience" : "https://oauth2.googleapis.com/token"
+ (each.value.feed_type) : merge(
+ {
+ "authentication" : {
+ "token_endpoint" : "https://oauth2.googleapis.com/token",
+ "claims" : {
+ "issuer" : module.workspace-integration-sa[0].email,
+ "subject" : var.workspace_integration_config.delegated_user,
+ "audience" : "https://oauth2.googleapis.com/token"
+ },
+ rs_credentials : {
+ private_key : jsondecode(base64decode(
+ google_service_account_key.workspace_integration_key[0].private_key
+ )).private_key
+ }
},
- rs_credentials : {
- private_key : jsondecode(base64decode(google_service_account_key.workspace_integration_key[0].private_key)).private_key
- }
+ workspace_customer_id : (
+ each.key == "ws-alerts"
+ ? trimprefix(var.workspace_integration_config.workspace_customer_id, "C")
+ : var.workspace_integration_config.workspace_customer_id
+ )
},
- workspace_customer_id : each.key == "ws-alerts" ? trimprefix(var.workspace_integration_config.workspace_customer_id, "C") : var.workspace_integration_config.workspace_customer_id
- }, each.key == "ws-activity" ? {
- applications : var.workspace_integration_config.applications
- } : {})
+ each.key != "ws-activity" ? {} : {
+ applications : var.workspace_integration_config.applications
+ }
+ )
}
}
write_only_attrs = ["details"]
diff --git a/tests/fast/stages/s2_secops/simple.tfvars b/tests/fast/stages/s2_secops/simple.tfvars
deleted file mode 100644
index 3d116dd40..000000000
--- a/tests/fast/stages/s2_secops/simple.tfvars
+++ /dev/null
@@ -1,32 +0,0 @@
-automation = {
- outputs_bucket = "test"
-}
-billing_account = {
- id = "000000-111111-222222"
-}
-custom_roles = {
- project_iam_viewer = "organizations/123456789012/roles/bar"
-}
-environments = {
- "dev" : {
- "is_default" : true,
- "key" : "dev",
- "name" : "Development",
- "short_name" : "dev",
- "tag_name" : "development"
- }
-}
-essential_contacts = "gcp-secops-admins@fast.example.com"
-folder_ids = {
- secops = "folders/12345678"
-}
-organization = {
- domain = "fast.example.com"
- id = 123456789012
- customer_id = "C00000000"
-}
-prefix = "fast"
-tag_values = {
- "environment/development" = "tagValues/12345"
- "environment/production" = "tagValues/12346"
-}
diff --git a/tests/fast/stages/s2_secops/simple.yaml b/tests/fast/stages/s2_secops/simple.yaml
deleted file mode 100644
index e39fb4679..000000000
--- a/tests/fast/stages/s2_secops/simple.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2025 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.
-
-counts:
- google_essential_contacts_contact: 1
- google_project: 1
- google_project_iam_binding: 1
- google_project_iam_member: 1
- google_project_service: 2
- google_project_service_identity: 1
- google_storage_bucket_object: 2
- google_tags_tag_binding: 1
- modules: 2
- resources: 10
-
-outputs:
- federated_identity_pool: null
- secops_project_ids:
- dev: fast-dev-secops-0
- tfvars:
- federated_identity_pool: null
- secops_project_ids:
- dev: fast-dev-secops-0
diff --git a/tests/fast/stages/s2_secops/tftest.yaml b/tests/fast/stages/s2_secops/tftest.yaml
deleted file mode 100644
index 805383079..000000000
--- a/tests/fast/stages/s2_secops/tftest.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2025 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.
-
-module: fast/stages/2-secops
-
-tests:
- simple:
diff --git a/tests/fast/stages/s3_secops_dev/simple.tfvars b/tests/fast/stages/s3_secops_dev/simple.tfvars
index bcd987eef..9c033f441 100644
--- a/tests/fast/stages/s3_secops_dev/simple.tfvars
+++ b/tests/fast/stages/s3_secops_dev/simple.tfvars
@@ -4,17 +4,13 @@ automation = {
billing_account = {
id = "012345-67890A-BCDEF0",
}
-project_reuse = null
folder_ids = {
- "secops-dev" = "folders/123456789"
+ "secops/dev" = "folders/123456789"
}
tenant_config = {
customer_id = "xxxxxx-xxxxxx-xxxxxx"
region = "europe"
}
-secops_project_ids = {
- dev = "fast-dev-secops-0"
-}
iam_default = {
viewers = ["gcp-secops-admins@fast.example.com"]
}
@@ -24,6 +20,7 @@ iam = {
scopes = ["gscope"]
}
}
+prefix = "fast"
workspace_integration_config = {
delegated_user = "secops-feed@fast.example.com"
workspace_customer_id = "C121212"
diff --git a/tests/fast/stages/s3_secops_dev/simple.yaml b/tests/fast/stages/s3_secops_dev/simple.yaml
index 066ba34eb..a41f6b470 100644
--- a/tests/fast/stages/s3_secops_dev/simple.yaml
+++ b/tests/fast/stages/s3_secops_dev/simple.yaml
@@ -35,4 +35,4 @@ counts:
restful_resource: 6
outputs:
- project_id: fast-dev-secops-0
+ project_id: fast-dev-secops-core-0