Add asset_feeds to resman modules (#3658)

* Add asset_feeds to resman modules

* Add examples and update readmes

* Extend pubsub_topic context to project and folder modules

* Use pubsub_topic context for pubsub_destination

* Update readmes and add project-factory asset_feed example

* Update context tests

* Update schemas
This commit is contained in:
Julio Castillo
2026-01-20 15:37:35 +01:00
committed by GitHub
parent 558e552b5e
commit d9e1b924a1
43 changed files with 1935 additions and 126 deletions

File diff suppressed because one or more lines are too long

46
modules/project/assets.tf Normal file
View File

@@ -0,0 +1,46 @@
/**
* Copyright 2026 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.
*/
resource "google_cloud_asset_project_feed" "default" {
for_each = var.asset_feeds
project = local.project.project_id
billing_project = coalesce(each.value.billing_project, local.project.project_id)
feed_id = each.key
content_type = each.value.content_type
asset_types = each.value.asset_types
asset_names = each.value.asset_names
feed_output_config {
pubsub_destination {
topic = lookup(
local.ctx.pubsub_topics,
each.value.feed_output_config.pubsub_destination.topic,
each.value.feed_output_config.pubsub_destination.topic
)
}
}
dynamic "condition" {
for_each = each.value.condition == null ? [] : [each.value.condition]
content {
expression = condition.value.expression
title = condition.value.title
description = condition.value.description
location = condition.value.location
}
}
}

View File

@@ -19,16 +19,38 @@
locals {
logging_sinks = {
for k, v in var.logging_sinks :
# rewrite destination and type when type="project"
k => merge(v, v.type != "project" ? {} : {
destination = "projects/${v.destination}"
type = "logging"
})
# expand destination contexts
k => merge(v,
v.type != "bigquery" ? {} : {
destination = lookup(
local.ctx.bigquery_datasets, v.destination, v.destination
)
},
v.type != "logging" ? {} : {
destination = lookup(
local.ctx.log_buckets, v.destination, v.destination
)
},
v.type != "project" ? {} : {
api = "logging"
destination = "projects/${lookup(local.ctx.project_ids, v.destination, v.destination)}"
},
v.type != "pubsub" ? {} : {
destination = lookup(
local.ctx.pubsub_topics, v.destination, v.destination
)
},
v.type != "storage" ? {} : {
destination = lookup(
local.ctx.storage_buckets, v.destination, v.destination
)
}
)
}
sink_bindings = {
for type in ["bigquery", "logging", "project", "pubsub", "storage"] :
type => {
for name, sink in var.logging_sinks :
for name, sink in local.logging_sinks :
name => sink if sink.iam && sink.type == type
}
}
@@ -66,7 +88,7 @@ resource "google_logging_project_sink" "sink" {
name = each.key
description = coalesce(each.value.description, "${each.key} (Terraform-managed).")
project = local.project.project_id
destination = "${each.value.type}.googleapis.com/${each.value.destination}"
destination = "${lookup(each.value, "api", each.value.type)}.googleapis.com/${each.value.destination}"
filter = each.value.filter
unique_writer_identity = each.value.unique_writer
disabled = each.value.disabled

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2025 Google LLC
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,40 @@
* limitations under the License.
*/
variable "asset_feeds" {
description = "Cloud Asset Inventory feeds."
type = map(object({
billing_project = optional(string)
content_type = optional(string)
asset_types = optional(list(string))
asset_names = optional(list(string))
feed_output_config = object({
pubsub_destination = object({
topic = string
})
})
condition = optional(object({
expression = string
title = optional(string)
description = optional(string)
location = optional(string)
}))
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.asset_feeds :
v.content_type == null || contains(
["RESOURCE", "IAM_POLICY", "ORG_POLICY", "ACCESS_POLICY", "OS_INVENTORY", "RELATIONSHIP"],
v.content_type
)
])
error_message = "Content type must be one of RESOURCE, IAM_POLICY, ORG_POLICY, ACCESS_POLICY, OS_INVENTORY, RELATIONSHIP."
}
}
variable "auto_create_network" {
description = "Whether to create the default network for the project."
type = bool
@@ -91,15 +125,18 @@ variable "contacts" {
variable "context" {
description = "Context-specific interpolations."
type = object({
bigquery_datasets = optional(map(string), {})
condition_vars = optional(map(map(string)), {})
custom_roles = optional(map(string), {})
email_addresses = optional(map(string), {})
folder_ids = optional(map(string), {})
kms_keys = optional(map(string), {})
iam_principals = optional(map(string), {})
notification_channels = optional(map(string), {})
kms_keys = optional(map(string), {})
log_buckets = optional(map(string), {})
notification_channels = optional(map(string), {})
project_ids = optional(map(string), {})
pubsub_topics = optional(map(string), {})
storage_buckets = optional(map(string), {})
tag_keys = optional(map(string), {})
tag_values = optional(map(string), {})
vpc_sc_perimeters = optional(map(string), {})