audit.tf (110 lines of code) (raw):
/**
* Copyright 2023 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.
*/
# Create dataset for monitoring
module "bigquery_audit" {
source = "terraform-google-modules/bigquery/google"
version = "7.0.0"
project_id = module.project_services.project_id
dataset_id = var.monitoring_dataset
dataset_name = var.monitoring_dataset
description = "Dataset ${var.monitoring_dataset} created by terraform"
location = var.bq_location
}
# Create audit table outline.
# This table contains just the fields needed for materialized views. The actual audit
# table will be expanded for all new columns once data starts flowing in.
resource "google_bigquery_table" "cloudaudit_table" {
dataset_id = module.bigquery_audit.bigquery_dataset.dataset_id
table_id = "cloudaudit_googleapis_com_data_access"
friendly_name = "cloudaudit_googleapis_com_data_access"
project = module.project_services.project_id
labels = {}
schema = file("${path.module}/audit_schema.json")
deletion_protection = false
time_partitioning {
type = "DAY"
expiration_ms = null
field = "timestamp"
require_partition_filter = false
}
lifecycle {
ignore_changes = [
encryption_configuration, # managed by google_bigquery_dataset.main.default_encryption_configuration
schema # logs router will expand the schema
]
}
}
# Extract table name from cloudaudit_table,
# In order to create the implicit dependencies between table and
# the downstream view
locals {
audit_table_id_array = split("/", google_bigquery_table.cloudaudit_table.id)
audit_table_name = element(local.audit_table_id_array, length(local.audit_table_id_array) - 1)
}
# Create the materialized view on the audit table
#
# This materialized view will extract and format common JSON fields for BigQuery audit logs,
# including DBT metadata.
data "template_file" "bigquery_jobs_view" {
template = file("${path.module}/bigquery_jobs_view.sql")
vars = {
monitoring_dataset = module.bigquery_audit.bigquery_dataset.dataset_id
audit_table = local.audit_table_name
}
}
resource "google_bigquery_table" "bigquery_materialized_view" {
project = module.project_services.project_id
dataset_id = module.bigquery_audit.bigquery_dataset.dataset_id
friendly_name = "bigquery_jobs"
table_id = "bigquery_jobs"
description = "BigQuery jobs (with DBT extras) logical view"
deletion_protection = false
#time_partitioning {
# field = "job_stats.create_time"
# type = "DAY"
#}
materialized_view {
query = data.template_file.bigquery_jobs_view.rendered
}
lifecycle {
ignore_changes = [
encryption_configuration
]
}
}
# Extract table name from bigquery_materialized_view,
# In order to create the implicit dependencies between table and
# the downstream view
locals {
job_table_id_array = split("/", google_bigquery_table.bigquery_materialized_view.id)
job_table_name = element(local.job_table_id_array, length(local.job_table_id_array) - 1)
}
# Create the next level view on the materialized view.
#
# This view will extract DBT-job-level metadata and bring it to
# all of the individual DBT jobs.
data "template_file" "dbt_jobs_view" {
template = file("${path.module}/dbt_jobs_view.sql")
vars = {
monitoring_dataset = module.bigquery_audit.bigquery_dataset.dataset_id
job_table = local.job_table_name
}
}
resource "google_bigquery_table" "dbt_view" {
project = module.project_services.project_id
dataset_id = module.bigquery_audit.bigquery_dataset.dataset_id
friendly_name = "dbt_jobs"
table_id = "dbt_jobs"
description = "DBT jobs (with DBT metadata) logical view"
deletion_protection = false
view {
query = data.template_file.dbt_jobs_view.rendered
use_legacy_sql = false
}
#lifecycle {
# ignore_changes = [
# encryption_configuration
# ]
#}
}
# Create the audit level sink into BigQuery
#
resource "google_logging_project_sink" "cloud_audit_sink" {
name = "bigquery_audit_export"
project = module.project_services.project_id
filter = "protoPayload.metadata.\"@type\"=\"type.googleapis.com/google.cloud.audit.BigQueryAuditMetadata\""
destination = "bigquery.googleapis.com/projects/${module.project_services.project_id}/datasets/${module.bigquery_audit.bigquery_dataset.dataset_id}"
unique_writer_identity = true
bigquery_options {
use_partitioned_tables = true
}
}
resource "google_project_iam_member" "bigquery_sink_member" {
project = module.project_services.project_id
role = "roles/bigquery.dataEditor"
member = element(concat(google_logging_project_sink.cloud_audit_sink[*].writer_identity, [""]), 0)
}