OSVauco/infrastructure/modules/vauco-bootstrap/main.tf

341 lines
9.2 KiB
HCL

resource "google_project_service" "apis" {
for_each = toset([
"aiplatform.googleapis.com",
"storage.googleapis.com",
"cloudbilling.googleapis.com",
"cloudresourcemanager.googleapis.com",
"iam.googleapis.com",
"run.googleapis.com",
"artifactregistry.googleapis.com",
"cloudbuild.googleapis.com",
"cloudbuildv2.googleapis.com",
"secretmanager.googleapis.com",
"monitoring.googleapis.com",
"logging.googleapis.com",
"cloudtrace.googleapis.com",
"pubsub.googleapis.com",
"cloudfunctions.googleapis.com",
])
service = each.key
disable_dependent_services = true
project = var.project_id
}
resource "google_service_account" "agent_sa" {
project = var.project_id
account_id = "${var.customer_id}-agent-sa"
display_name = "${var.customer_id} Agent Runner SA"
}
resource "google_project_iam_member" "agent_sa_roles" {
for_each = toset([
"roles/aiplatform.user",
"roles/storage.objectAdmin",
"roles/logging.logWriter",
"roles/cloudtrace.agent",
"roles/monitoring.metricWriter",
"roles/secretmanager.secretAccessor",
"roles/run.invoker",
])
project = var.project_id
role = each.key
member = "serviceAccount:${google_service_account.agent_sa.email}"
}
resource "google_pubsub_topic" "billing_alerts" {
project = var.project_id
name = "${var.customer_id}-billing-alert-auto-teardown"
}
resource "google_storage_bucket" "agent_staging" {
project = var.project_id
name = "${var.project_id}-${var.customer_id}-agent-staging"
location = var.region
force_destroy = true
lifecycle_rule {
action {
type = "Delete"
}
condition {
age = 7
}
}
}
resource "google_billing_budget" "prod_budget" {
billing_account = var.billing_account_id
display_name = "${var.customer_id}-Agent-Budget-500USD"
amount {
specified_amount {
currency_code = "NOK"
units = "2500"
}
}
threshold_rules {
threshold_percent = 0.5
}
threshold_rules {
threshold_percent = 0.8
}
threshold_rules {
threshold_percent = 1.0
}
all_updates_rule {
pubsub_topic = google_pubsub_topic.billing_alerts.id
schema_version = "1.0"
}
}
resource "google_billing_budget" "dev_budget" {
billing_account = var.billing_account_id
display_name = "${var.customer_id}-Dev-Budget-75USD"
amount {
specified_amount {
currency_code = "NOK"
units = "500"
}
}
threshold_rules {
threshold_percent = 0.7
}
threshold_rules {
threshold_percent = 0.9
}
threshold_rules {
threshold_percent = 1.0
}
all_updates_rule {
pubsub_topic = google_pubsub_topic.billing_alerts.id
schema_version = "1.0"
}
}
resource "google_artifact_registry_repository" "repo" {
project = var.project_id
location = var.region
repository_id = "${var.customer_id}-agent-images"
format = "DOCKER"
}
resource "google_cloud_run_v2_service" "agent_service" {
project = var.project_id
name = "${var.customer_id}-agent"
location = var.region
template {
service_account = google_service_account.agent_sa.email
scaling {
min_instance_count = 1
max_instance_count = 10
}
containers {
image = var.container_image
resources {
limits = {
cpu = "1"
memory = "1Gi"
}
}
env {
name = "PROJECT_ID"
value = var.project_id
}
env {
name = "REGION"
value = var.region
}
}
timeout = "300s"
max_instance_request_concurrency = 80
}
labels = {
env = "prod"
team = "osvaucoe"
customer = var.customer_id
}
depends_on = [google_project_service.apis]
}
resource "google_cloud_run_v2_service_iam_member" "invoker" {
project = var.project_id
location = google_cloud_run_v2_service.agent_service.location
name = google_cloud_run_v2_service.agent_service.name
role = "roles/run.invoker"
member = "user:chris.christiansen@vauco.no" # This should probably be a variable
}
resource "google_bigquery_dataset" "logs" {
project = var.project_id
dataset_id = "${var.customer_id}_logs"
friendly_name = "${var.customer_id} Logs"
location = var.region
default_table_expiration_ms = 7776000000 # 90 dager
}
resource "google_bigquery_table" "cloud_run_logs" {
project = var.project_id
dataset_id = google_bigquery_dataset.logs.dataset_id
table_id = "cloud_run_logs"
deletion_protection = false
time_partitioning {
type = "DAY"
field = "timestamp"
expiration_ms = 7776000000
}
schema = jsonencode([
{ name = "timestamp", type = "TIMESTAMP", mode = "REQUIRED" },
{ name = "severity", type = "STRING", mode = "NULLABLE" },
{ name = "service_name", type = "STRING", mode = "NULLABLE" },
{ name = "revision", type = "STRING", mode = "NULLABLE" },
{ name = "http_method", type = "STRING", mode = "NULLABLE" },
{ name = "http_url", type = "STRING", mode = "NULLABLE" },
{ name = "http_status", type = "INTEGER", mode = "NULLABLE" },
{ name = "latency_ms", type = "FLOAT", mode = "NULLABLE" },
{ name = "message", type = "STRING", mode = "NULLABLE" },
{ name = "trace", type = "STRING", mode = "NULLABLE" },
{ name = "labels", type = "JSON", mode = "NULLABLE" }
])
}
resource "google_project_iam_member" "bq_log_writer" {
project = var.project_id
role = "roles/bigquery.dataEditor"
member = "serviceAccount:${google_service_account.agent_sa.email}"
}
resource "google_logging_project_sink" "cloudrun_to_bq" {
project = var.project_id
name = "${var.customer_id}-cloudrun-sink"
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.logs.dataset_id}"
filter = "resource.type=\"cloud_run_revision\" AND resource.labels.service_name=\"${var.customer_id}-agent\""
unique_writer_identity = true
}
resource "google_project_iam_member" "log_sink_bq_writer" {
project = var.project_id
role = "roles/bigquery.dataEditor"
member = google_logging_project_sink.cloudrun_to_bq.writer_identity
}
resource "google_monitoring_notification_channel" "email" {
project = var.project_id
display_name = "${var.customer_id} Alert Email"
type = "email"
labels = {
email_address = var.alert_email
}
}
resource "google_logging_metric" "agent_error_count" {
project = var.project_id
name = "${var.customer_id}-agent-error-count"
description = "Count of ERROR severity logs from agent"
filter = "resource.type=\"cloud_run_revision\" AND resource.labels.service_name=\"${var.customer_id}-agent\" AND severity=ERROR"
}
resource "google_monitoring_uptime_check_config" "cloud_run_uptime" {
project = var.project_id
display_name = "${var.customer_id}-uptime"
http_check {
path = "/health"
port = 443
use_ssl = true
}
monitored_resource {
type = "uptime_url"
labels = {
host = replace(google_cloud_run_v2_service.agent_service.uri, "https://", "")
}
}
timeout = "10s"
period = "60s"
}
data "archive_file" "cost_guard_fn_zip" {
type = "zip"
source_dir = "${path.module}/../../terraform/cost-guard-fn"
output_path = "${path.module}/cost-guard-fn.zip"
}
resource "google_storage_bucket_object" "cost_guard_fn_source" {
name = "${var.customer_id}-cost-guard-fn-source.zip"
bucket = google_storage_bucket.agent_staging.name
source = data.archive_file.cost_guard_fn_zip.output_path
}
resource "google_cloudfunctions2_function" "billing_auto_teardown" {
project = var.project_id
name = "${var.customer_id}-billing-auto-teardown"
location = var.region
build_config {
runtime = "python312"
entry_point = "billing_alert"
source {
storage_source {
bucket = google_storage_bucket.agent_staging.name
object = google_storage_bucket_object.cost_guard_fn_source.name
}
}
}
service_config {
service_account_email = google_service_account.agent_sa.email
environment_variables = {
PROJECT_ID = var.project_id
CLOUD_RUN_SERVICE = "${var.customer_id}-agent"
REGION = var.region
}
}
event_trigger {
trigger_region = "global"
event_type = "google.cloud.pubsub.topic.v1.messagePublished"
pubsub_topic = google_pubsub_topic.billing_alerts.id
}
}
data "google_project" "project" {
project_id = var.project_id
}
resource "google_project_iam_member" "function_sa_run_admin" {
project = var.project_id
role = "roles/run.admin"
member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com"
}
resource "google_project_iam_member" "function_sa_secret_accessor" {
project = var.project_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com"
}
resource "google_billing_account_iam_member" "billing_viewers" {
for_each = toset(var.billing_viewer_emails)
billing_account_id = var.billing_account_id
role = "roles/billing.viewer"
member = "user:${each.key}"
}