diff --git a/infrastructure.yaml b/infrastructure.yaml index 9171716..14e3306 100644 --- a/infrastructure.yaml +++ b/infrastructure.yaml @@ -63,6 +63,22 @@ environments: lifecycle: ignore_changes: true + - name: testci-postgresql-secret + namespace: testci + type: database + dependencies: + cluster: "c9qa2coo5ukgcg93fldm" + db: testci_db + user: testci + lifecycle: + ignore_changes: true + custom_keys: + sslmode: "verify-full" + random_keys: + full-host: + length: 32 + special: false + - name: session-secret namespace: pulse diff --git a/live/stage/s3/terragrunt.hcl b/live/stage/s3/terragrunt.hcl index e8ef9ca..ee25658 100644 --- a/live/stage/s3/terragrunt.hcl +++ b/live/stage/s3/terragrunt.hcl @@ -1,4 +1,3 @@ - include "root" { path = find_in_parent_folders() } @@ -10,26 +9,20 @@ include "env" { } locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") + repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) + env_name = basename(dirname(get_terragrunt_dir())) env_config = local.infra_config.environments[local.env_name] - bucket = local.env_config.buckets[0] # Первый bucket из списка } - terraform { source = "${get_terragrunt_dir()}/../../../modules//yc-s3" } inputs = { - bucket_name = local.bucket.name - folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - acl = try(local.bucket.acl, "private") - versioning_enabled = try(local.bucket.versioning.enabled, false) - cors_enabled = try(local.bucket.cors.enabled, false) - cors_config = try(local.bucket.cors, {}) - + buckets = local.env_config.buckets + folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", "")) + yc_token = get_env("YC_TOKEN", "") yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") yc_cloud_id = get_env("YC_CLOUD_ID", "") diff --git a/modules/yc-database/main.tf b/modules/yc-database/main.tf index ecd0862..4a0d3ee 100644 --- a/modules/yc-database/main.tf +++ b/modules/yc-database/main.tf @@ -29,13 +29,11 @@ resource "yandex_mdb_postgresql_user" "this" { password = random_password.user_password[each.key].result conn_limit = try(each.value.user.conn_limit, 10) - # Permission на свою БД + дополнительные permissions - # Yandex API требует явный permission даже для владельца + # Permissions только на ДРУГИЕ существующие БД + # Permission на свою БД НЕ добавляем - owner получает доступ автоматически + # и БД ещё не существует на момент создания user dynamic "permission" { - for_each = distinct(concat( - [each.value.database.name], - try(each.value.user.permissions, []) - )) + for_each = distinct(compact(try(each.value.user.permissions, []))) content { database_name = permission.value } diff --git a/modules/yc-s3/main.tf b/modules/yc-s3/main.tf index 0ddb8cf..85d2605 100644 --- a/modules/yc-s3/main.tf +++ b/modules/yc-s3/main.tf @@ -1,35 +1,44 @@ -# Универсальный модуль для создания S3 бакета -# Не знает про конкретные сущности +# Универсальный модуль для создания S3 бакетов -# Создание сервисного аккаунта для S3 +locals { + buckets_map = { for bucket in var.buckets : bucket.name => bucket } +} + +# Создание сервисного аккаунта для каждого бакета resource "yandex_iam_service_account" "sa" { - name = "${var.bucket_name}-sa" - description = "Service account for ${var.bucket_name} bucket" + for_each = local.buckets_map + + name = "${each.key}-sa" + description = "Service account for ${each.key} bucket" } # Назначение роли storage.editor сервисному аккаунту resource "yandex_resourcemanager_folder_iam_member" "storage_editor" { + for_each = local.buckets_map + folder_id = var.folder_id role = "storage.editor" - member = "serviceAccount:${yandex_iam_service_account.sa.id}" + member = "serviceAccount:${yandex_iam_service_account.sa[each.key].id}" } # Создание статического ключа доступа resource "yandex_iam_service_account_static_access_key" "sa_key" { - service_account_id = yandex_iam_service_account.sa.id - description = "Static access key for ${var.bucket_name} bucket" + for_each = local.buckets_map + + service_account_id = yandex_iam_service_account.sa[each.key].id + description = "Static access key for ${each.key} bucket" } # Создание S3 бакета -# Bucket создается от имени текущего пользователя (YC_TOKEN), не сервисного аккаунта -# Ключи сервисного аккаунта используются только для доступа к bucket после создания resource "yandex_storage_bucket" "this" { - bucket = var.bucket_name - acl = var.acl + for_each = local.buckets_map + + bucket = each.key + acl = each.value.acl # Версионирование объектов dynamic "versioning" { - for_each = var.versioning_enabled ? [1] : [] + for_each = try(each.value.versioning.enabled, false) ? [1] : [] content { enabled = true } @@ -37,13 +46,13 @@ resource "yandex_storage_bucket" "this" { # Настройка CORS dynamic "cors_rule" { - for_each = var.cors_enabled ? [1] : [] + for_each = try(each.value.cors.enabled, false) ? [1] : [] content { - allowed_headers = var.cors_config.allowed_headers - allowed_methods = var.cors_config.allowed_methods - allowed_origins = var.cors_config.allowed_origins - expose_headers = try(var.cors_config.expose_headers, []) - max_age_seconds = try(var.cors_config.max_age_seconds, 3600) + allowed_headers = try(each.value.cors.allowed_headers, []) + allowed_methods = try(each.value.cors.allowed_methods, []) + allowed_origins = try(each.value.cors.allowed_origins, []) + expose_headers = try(each.value.cors.expose_headers, []) + max_age_seconds = try(each.value.cors.max_age_seconds, 3600) } } } diff --git a/modules/yc-s3/outputs.tf b/modules/yc-s3/outputs.tf index 2e4dd06..0a95b7c 100644 --- a/modules/yc-s3/outputs.tf +++ b/modules/yc-s3/outputs.tf @@ -1,21 +1,31 @@ +# Для обратной совместимости - первый бакет output "bucket_name" { - description = "Name of the created bucket" - value = yandex_storage_bucket.this.bucket + description = "Name of the first bucket (for backward compatibility)" + value = length(var.buckets) > 0 ? yandex_storage_bucket.this[var.buckets[0].name].bucket : "" } output "access_key" { - description = "Access key for S3 bucket" - value = yandex_iam_service_account_static_access_key.sa_key.access_key + description = "Access key for the first bucket (for backward compatibility)" + value = length(var.buckets) > 0 ? yandex_iam_service_account_static_access_key.sa_key[var.buckets[0].name].access_key : "" sensitive = true } output "secret_key" { - description = "Secret key for S3 bucket" - value = yandex_iam_service_account_static_access_key.sa_key.secret_key + description = "Secret key for the first bucket (for backward compatibility)" + value = length(var.buckets) > 0 ? yandex_iam_service_account_static_access_key.sa_key[var.buckets[0].name].secret_key : "" sensitive = true } -output "service_account_id" { - description = "ID of the service account" - value = yandex_iam_service_account.sa.id +# Новые outputs - map всех бакетов +output "buckets" { + description = "Map of all buckets (name => data)" + value = { + for name, bucket in yandex_storage_bucket.this : name => { + bucket_name = bucket.bucket + access_key = yandex_iam_service_account_static_access_key.sa_key[name].access_key + secret_key = yandex_iam_service_account_static_access_key.sa_key[name].secret_key + service_account_id = yandex_iam_service_account.sa[name].id + } + } + sensitive = true } diff --git a/modules/yc-s3/variables.tf b/modules/yc-s3/variables.tf index bdb2595..10b3b48 100644 --- a/modules/yc-s3/variables.tf +++ b/modules/yc-s3/variables.tf @@ -1,43 +1,23 @@ -variable "bucket_name" { - description = "Name of the S3 bucket" - type = string +variable "buckets" { + description = "List of S3 buckets to create" + type = list(object({ + name = string + acl = optional(string, "private") + versioning = optional(object({ + enabled = bool + }), { enabled = false }) + cors = optional(object({ + enabled = optional(bool, false) + allowed_headers = optional(list(string), []) + allowed_methods = optional(list(string), []) + allowed_origins = optional(list(string), []) + expose_headers = optional(list(string), []) + max_age_seconds = optional(number, 3600) + }), { enabled = false }) + })) } variable "folder_id" { description = "Yandex Cloud folder ID" type = string } - -variable "acl" { - description = "ACL для бакета" - type = string - default = "private" -} - -variable "versioning_enabled" { - description = "Enable versioning for the bucket" - type = bool - default = false -} - -variable "cors_enabled" { - description = "Enable CORS for the bucket" - type = bool - default = false -} - -variable "cors_config" { - description = "CORS configuration" - type = object({ - allowed_headers = list(string) - allowed_methods = list(string) - allowed_origins = list(string) - expose_headers = optional(list(string)) - max_age_seconds = optional(number) - }) - default = { - allowed_headers = [] - allowed_methods = [] - allowed_origins = [] - } -}