Drop the 2-secops stage and minimally refactor 3-secops-dev (#3537)

* drop 2-secops and minimally refactor 3-secops

* remove stage 2 tests

* tfdoc
This commit is contained in:
Ludovico Magnocavallo
2025-11-18 14:32:06 +01:00
committed by GitHub
parent 3d61542367
commit 932fd82fe2
21 changed files with 165 additions and 779 deletions

View File

@@ -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"

View File

@@ -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:
<p align="center">
<img src="diagram.png" alt="Security diagram">
</p>
<!-- BEGIN TOC -->
- [Design overview and choices](#design-overview-and-choices)
- [Workforce Identity Federation](#workforce-identity-federation)
- [How to run this stage](#how-to-run-this-stage)
- [Provider and Terraform variables](#provider-and-terraform-variables)
- [Impersonating the automation service account](#impersonating-the-automation-service-account)
- [Variable configuration](#variable-configuration)
- [Using delayed billing association for projects](#using-delayed-billing-association-for-projects)
- [Running the stage](#running-the-stage)
- [Customizations](#customizations)
- [Workforce Identity Federation](#workforce-identity-federation)
- [Files](#files)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Design overview and choices
This stage will deploy 1 SecOps project for each environment available from the 0-globals input variables, of course such a behaviour might be updated to either deploy a single production instance or different number of environments with respect to the foundations ones.
IAM for day to day operations is already assigned at the folder level to the secops team by the previous stage, but more granularity can be added here at the project level, to grant control of separate services across environments to different actors as well as in the later 3-secops-dev/prod stages.
### Workforce Identity Federation
This stage supports configuration of [Workforce Identity Federation](https://cloud.google.com/iam/docs/workforce-identity-federation) which lets an external identity provider (IdP) to authenticate and authorize a group of users (usually employees) using IAM, so that the users can access Google Cloud services.
The following example shows an example on how to define a Workforce Identity pool for the organization.
```hcl
# stage 2 secops wif tfvars
workforce_identity_providers = {
test = {
issuer = "azuread"
display_name = "wif-provider"
description = "Workforce Identity pool"
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
}
# tftest skip
```
## How to run this stage
This stage is meant to be executed after the [bootstrap](../0-org-setup) stage has run, as it leverages the automation service account and bucket created there, and additional resources configured there.
It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the previous stages for the environmental requirements.
Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration.
### Provider and Terraform variables
As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-org-setup/README.md#output-files-and-cross-stage-variables) is also leveraged here.
The commands to link or copy the provider and terraform variable files can be easily derived from the `fast-links.sh` script in the FAST stages folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run.
```bash
../fast-links.sh ~/fast-config
# File linking commands for security stage
# provider file
ln -s ~/fast-config/fast-test-00/providers/2-secops-providers.tf ./
# input files from other stages
ln -s ~/fast-config/fast-test-00/tfvars/0-globals.auto.tfvars.json ./
ln -s ~/fast-config/fast-test-00/tfvars/0-org-setup.auto.tfvars.json ./
ln -s ~/fast-config/fast-test-00/tfvars/1-resman.auto.tfvars.json ./
# conventional place for stage tfvars (manually created)
ln -s ~/fast-config/fast-test-00/2-secops.auto.tfvars ./
```
```bash
../fast-links.sh gs://xxx-prod-iac-core-outputs-0
# File linking commands for security stage
# provider file
gcloud storage cp gs://xxx-prod-iac-core-outputs-0/providers/2-secops-providers.tf ./
# input files from other stages
gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./
gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-org-setup.auto.tfvars.json ./
gcloud storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./
# conventional place for stage tfvars (manually created)
gcloud storage cp gs://xxx-prod-iac-core-outputs-0/2-secops.auto.tfvars ./
```
### Impersonating the automation service account
The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups.
### Variable configuration
Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets:
- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above
- variables which refer to resources managed by previous stages, which are prepopulated here via the `0-org-setup.auto.tfvars.json` and `1-resman.auto.tfvars.json` files linked or copied above
- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file
The latter set is explained in the [Customization](#customizations) sections below, and the full list can be found in the [Variables](#variables) table at the bottom of this document.
Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../0-org-setup/README.md#output-files-and-cross-stage-variables) for more details:
```tfvars
outputs_location = "~/fast-config"
```
### Using delayed billing association for projects
This configuration is possible but unsupported and only exists for development purposes, use at your own risk:
- temporarily switch `billing_account.id` to `null` in `0-globals.auto.tfvars.json`
- for each project resources in the project modules used in this stage (`dev`, `prod`)
- apply using `-target`, for example
`terraform apply -target 'module.project["dev"].google_project.project[0]'`
- untaint the project resource after applying, for example
`terraform untaint 'module.project["dev"].google_project.project[0]'`
- go through the process to associate the billing account with the two projects
- switch `billing_account.id` back to the real billing account id
- resume applying normally
### Running the stage
Once provider and variable values are in place and the correct user is configured, the stage can be run:
```bash
terraform init
terraform apply
```
## Customizations
### Workforce Identity Federation
This is a minimal configuration that creates a Workforce Identity pool at organization level.
```tfvars
workforce_identity_providers = {
test = {
issuer = "azuread"
display_name = "wif-provider"
description = "Workforce Identity pool"
saml = {
idp_metadata_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>..."
}
}
}
```
<!-- TFDOC OPTS files:1 show_extra:1 exclude:2-secops-providers.tf -->
<!-- BEGIN TFDOC -->
## Files
| name | description | modules | resources |
|---|---|---|---|
| [identity-providers-defs.tf](./identity-providers-defs.tf) | Workforce Identity provider definitions. | | |
| [identity-providers.tf](./identity-providers.tf) | Workforce Identity Federation provider definitions. | | <code>google_iam_workforce_pool</code> · <code>google_iam_workforce_pool_provider</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>folder</code> · <code>project</code> | |
| [outputs.tf](./outputs.tf) | Module outputs. | | <code>google_storage_bucket_object</code> · <code>local_file</code> |
| [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. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [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. | <code title="object&#40;&#123;&#10; id &#61; string&#10; is_org_level &#61; optional&#40;bool, true&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [environments](variables-fast.tf#L47) | Environment names. | <code title="map&#40;object&#40;&#123;&#10; name &#61; string&#10; short_name &#61; string&#10; tag_name &#61; string&#10; is_default &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | | <code>0-globals</code> |
| [folder_ids](variables-fast.tf#L65) | Folder name => id mappings, the 'security' folder name must exist. | <code title="object&#40;&#123;&#10; secops &#61; string&#10; secops-dev &#61; optional&#40;string&#41;&#10; secops-prod &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>1-resman</code> |
| [organization](variables-fast.tf#L75) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [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. | <code>string</code> | ✓ | | <code>0-org-setup</code> |
| [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; project_iam_viewer &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>0-org-setup</code> |
| [essential_contacts](variables.tf#L17) | Email used for essential contacts, unset if null. | <code>string</code> | | <code>null</code> | |
| [outputs_location](variables.tf#L23) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [stage_config](variables-fast.tf#L96) | FAST stage configuration. | <code title="object&#40;&#123;&#10; security &#61; optional&#40;object&#40;&#123;&#10; short_name &#61; optional&#40;string&#41;&#10; iam_admin_delegated &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_viewer &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>1-resman</code> |
| [tag_values](variables-fast.tf#L110) | Root-level tag values. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>1-resman</code> |
| [workforce_identity_providers](variables.tf#L29) | Workforce Identity Federation pools. | <code title="map&#40;object&#40;&#123;&#10; attribute_condition &#61; optional&#40;string&#41;&#10; issuer &#61; string&#10; display_name &#61; string&#10; description &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; saml &#61; optional&#40;object&#40;&#123;&#10; idp_metadata_xml &#61; string&#10; &#125;&#41;, null&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
## 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. | ✓ | |
<!-- END TFDOC -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -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

View File

@@ -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]"
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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
)
}
}
}
)
}

View File

@@ -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
}

View File

@@ -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 = {}
}

View File

@@ -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
}

View File

@@ -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.
<p align="center">
<img src="diagram.png" alt="SecOPs stage">
@@ -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. | <code title="object&#40;&#123;&#10; outputs_bucket &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>0-org-setup</code> |
| [tenant_config](variables.tf#L118) | SecOps Tenant configuration. | <code title="object&#40;&#123;&#10; customer_id &#61; string&#10; region &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [prefix](variables-fast.tf#L67) | Prefix for organization projects. | <code>string</code> | ✓ | | <code>0-org-setup</code> |
| [tenant_config](variables.tf#L139) | SecOps Tenant configuration. | <code title="object&#40;&#123;&#10; customer_id &#61; string&#10; region &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | |
| [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. | <code title="object&#40;&#123;&#10; id &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [data_rbac_config](variables.tf#L17) | SecOps Data RBAC scope and labels config. | <code title="object&#40;&#123;&#10; labels &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; string&#10; label_id &#61; string&#10; udm_query &#61; string&#10; &#125;&#41;&#41;&#41;&#10; scopes &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; string&#10; scope_id &#61; string&#10; allowed_data_access_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; data_access_label &#61; optional&#40;string&#41;&#10; log_type &#61; optional&#40;string&#41;&#10; asset_namespace &#61; optional&#40;string&#41;&#10; ingestion_label &#61; optional&#40;object&#40;&#123;&#10; ingestion_label_key &#61; string&#10; ingestion_label_value &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; denied_data_access_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; data_access_label &#61; optional&#40;string&#41;&#10; log_type &#61; optional&#40;string&#41;&#10; asset_namespace &#61; optional&#40;string&#41;&#10; ingestion_label &#61; optional&#40;object&#40;&#123;&#10; ingestion_label_key &#61; string&#10; ingestion_label_value &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [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. | <code title="object&#40;&#123;&#10; rules &#61; optional&#40;string&#41;&#10; rules_defs &#61; optional&#40;string, &#34;data&#47;rules&#34;&#41;&#10; reference_lists &#61; optional&#40;string&#41;&#10; reference_lists_defs &#61; optional&#40;string, &#34;data&#47;reference_lists&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; rules &#61; &#34;.&#47;data&#47;secops_rules.yaml&#34;&#10; rules_defs &#61; &#34;.&#47;data&#47;rules&#34;&#10; reference_lists &#61; &#34;.&#47;data&#47;secops_reference_lists.yaml&#34;&#10; reference_lists_defs &#61; &#34;.&#47;data&#47;reference_lists&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [folder_ids](variables-fast.tf#L35) | Folder name => id mappings. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>1-resman</code> |
| [iam](variables.tf#L68) | SecOps IAM configuration in {PRINCIPAL => {roles => [ROLES], scopes => [SCOPES]}} format. | <code title="map&#40;object&#40;&#123;&#10; roles &#61; list&#40;string&#41;&#10; scopes &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_default](variables.tf#L78) | Groups ID in IdP assigned to SecOps admins, editors, viewers roles. | <code title="object&#40;&#123;&#10; admins &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; editors &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; viewers &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [project_id](variables.tf#L88) | Project id that references existing SecOps project. Use this variable when running this stage in isolation. | <code>string</code> | | <code>null</code> | |
| [project_reuse](variables.tf#L94) | Whether to use an existing project, leave default for FAST deployment. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | |
| [region](variables.tf#L100) | Google Cloud region definition for resources. | <code>string</code> | | <code>&#34;europe-west8&#34;</code> | |
| [secops_project_ids](variables-fast.tf#L43) | SecOps Project IDs for each environment. | <code>map&#40;string&#41;</code> | | <code>null</code> | <code>2-secops</code> |
| [stage_config](variables.tf#L106) | FAST stage configuration used to find resource ids. Must match name defined for the stage in resource management. | <code title="object&#40;&#123;&#10; environment &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; environment &#61; &#34;dev&#34;&#10; name &#61; &#34;secops-dev&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [workspace_integration_config](variables.tf#L126) | SecOps Feeds configuration for Workspace logs and entities ingestion. | <code title="object&#40;&#123;&#10; workspace_customer_id &#61; string&#10; delegated_user &#61; string&#10; applications &#61; optional&#40;list&#40;string&#41;, &#91;&#34;access_transparency&#34;, &#34;admin&#34;, &#34;calendar&#34;, &#34;chat&#34;, &#34;drive&#34;, &#34;gcp&#34;,&#10; &#34;gplus&#34;, &#34;groups&#34;, &#34;groups_enterprise&#34;, &#34;jamboard&#34;, &#34;login&#34;, &#34;meet&#34;, &#34;mobile&#34;, &#34;rules&#34;, &#34;saml&#34;, &#34;token&#34;,&#10; &#34;user_accounts&#34;, &#34;context_aware_access&#34;, &#34;chrome&#34;, &#34;data_studio&#34;, &#34;keep&#34;,&#10; &#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; custom_roles &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; folder_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; kms_keys &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [custom_roles](variables-fast.tf#L35) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [data_rbac_config](variables.tf#L30) | SecOps Data RBAC scope and labels config. | <code title="object&#40;&#123;&#10; labels &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; string&#10; label_id &#61; string&#10; udm_query &#61; string&#10; &#125;&#41;&#41;&#41;&#10; scopes &#61; optional&#40;map&#40;object&#40;&#123;&#10; description &#61; string&#10; scope_id &#61; string&#10; allowed_data_access_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; data_access_label &#61; optional&#40;string&#41;&#10; log_type &#61; optional&#40;string&#41;&#10; asset_namespace &#61; optional&#40;string&#41;&#10; ingestion_label &#61; optional&#40;object&#40;&#123;&#10; ingestion_label_key &#61; string&#10; ingestion_label_value &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; denied_data_access_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; data_access_label &#61; optional&#40;string&#41;&#10; log_type &#61; optional&#40;string&#41;&#10; asset_namespace &#61; optional&#40;string&#41;&#10; ingestion_label &#61; optional&#40;object&#40;&#123;&#10; ingestion_label_key &#61; string&#10; ingestion_label_value &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [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. | <code title="object&#40;&#123;&#10; rules &#61; optional&#40;string&#41;&#10; rules_defs &#61; optional&#40;string, &#34;data&#47;rules&#34;&#41;&#10; reference_lists &#61; optional&#40;string&#41;&#10; reference_lists_defs &#61; optional&#40;string, &#34;data&#47;reference_lists&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; rules &#61; &#34;.&#47;data&#47;secops_rules.yaml&#34;&#10; rules_defs &#61; &#34;.&#47;data&#47;rules&#34;&#10; reference_lists &#61; &#34;.&#47;data&#47;secops_reference_lists.yaml&#34;&#10; reference_lists_defs &#61; &#34;.&#47;data&#47;reference_lists&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [folder_ids](variables-fast.tf#L43) | Folders created in the bootstrap stage. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [iam](variables.tf#L81) | SecOps IAM configuration in {PRINCIPAL => {roles => [ROLES], scopes => [SCOPES]}} format. | <code title="map&#40;object&#40;&#123;&#10; roles &#61; list&#40;string&#41;&#10; scopes &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_default](variables.tf#L91) | Groups ID in IdP assigned to SecOps admins, editors, viewers roles. | <code title="object&#40;&#123;&#10; admins &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; editors &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; viewers &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [iam_principals](variables-fast.tf#L51) | IAM-format principals. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [kms_keys](variables-fast.tf#L59) | KMS key ids. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>2-security</code> |
| [parent_folder](variables.tf#L101) | Folder to use for created project. | <code>string</code> | | <code>&#34;&#36;folder_ids:secops&#47;dev&#34;</code> | |
| [project_id](variables.tf#L108) | Project id for newly created project, or id of existing project if project_create is false. | <code>string</code> | | <code>&#34;dev-secops-core-0&#34;</code> | |
| [project_ids](variables-fast.tf#L74) | Projects created in the bootstrap stage. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>0-org-setup</code> |
| [project_reuse](variables.tf#L115) | Whether to use an existing project. | <code>map&#40;string&#41;</code> | | <code>null</code> | |
| [region](variables.tf#L121) | Google Cloud region definition for resources. | <code>string</code> | | <code>&#34;europe-west8&#34;</code> | |
| [stage_config](variables.tf#L127) | FAST stage configuration used to find resource ids. Must match name defined for the stage in resource management. | <code title="object&#40;&#123;&#10; environment &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; environment &#61; &#34;dev&#34;&#10; name &#61; &#34;secops-dev&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [workspace_integration_config](variables.tf#L147) | SecOps Feeds configuration for Workspace logs and entities ingestion. | <code title="object&#40;&#123;&#10; workspace_customer_id &#61; string&#10; delegated_user &#61; string&#10; applications &#61; optional&#40;list&#40;string&#41;, &#91;&#34;access_transparency&#34;, &#34;admin&#34;, &#34;calendar&#34;, &#34;chat&#34;, &#34;drive&#34;, &#34;gcp&#34;,&#10; &#34;gplus&#34;, &#34;groups&#34;, &#34;groups_enterprise&#34;, &#34;jamboard&#34;, &#34;login&#34;, &#34;meet&#34;, &#34;mobile&#34;, &#34;rules&#34;, &#34;saml&#34;, &#34;token&#34;,&#10; &#34;user_accounts&#34;, &#34;context_aware_access&#34;, &#34;chrome&#34;, &#34;data_studio&#34;, &#34;keep&#34;,&#10; &#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs

View File

@@ -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

View File

@@ -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 = {}
}

View File

@@ -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" {

View File

@@ -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"]

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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"

View File

@@ -35,4 +35,4 @@ counts:
restful_resource: 6
outputs:
project_id: fast-dev-secops-0
project_id: fast-dev-secops-core-0