Initial commit
terraform / check (push) Canceled after 0s

This commit is contained in:
stolzor committed 2026-10-11 20:30:21 +03:00
commit 64be99308b
53 files changed
+1983

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
locals {
databases = var.service.database == null ? {} : {
(var.service.database.name) = var.service.database
}
}
+42
View File
@@ -0,0 +1,42 @@
resource "random_password" "this" {
for_each = local.databases
length = 32
special = false
}
resource "postgresql_role" "this" {
for_each = local.databases
name = each.value.username
login = true
password = random_password.this[each.key].result
}
resource "postgresql_database" "this" {
for_each = local.databases
name = each.key
owner = postgresql_role.this[each.key].name
}
resource "postgresql_extension" "vector" {
for_each = { for key, value in local.databases : key => value if value.pgvector }
name = "vector"
database = postgresql_database.this[each.key].name
}
resource "postgresql_grant" "public" {
for_each = postgresql_database.this
database = each.value.name
role = "public"
object_type = "database"
privileges = []
}
resource "postgresql_grant" "owner" {
for_each = postgresql_database.this
database = each.value.name
role = postgresql_role.this[each.key].name
object_type = "database"
privileges = ["CONNECT", "CREATE", "TEMPORARY"]
depends_on = [postgresql_grant.public]
}
+9
View File
@@ -0,0 +1,9 @@
output "credentials" {
value = one([for name, db in postgresql_database.this : {
database = db.name
username = postgresql_role.this[name].name
password = random_password.this[name].result
}])
description = "credentials of the service database, null if the service has no database"
sensitive = true
}
+10
View File
@@ -0,0 +1,10 @@
variable "service" {
type = object({
name = string
database = optional(object({
name = string
username = string
pgvector = optional(bool, false)
}))
})
}
+12
View File
@@ -0,0 +1,12 @@
terraform {
required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
version = ">= 1.27.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.9.1"
}
}
}
+71
View File
@@ -0,0 +1,71 @@
resource "minio_s3_bucket" "this" {
for_each = var.service.buckets
bucket = each.key
acl = "private"
}
resource "minio_s3_bucket_versioning" "this" {
for_each = { for name, b in var.service.buckets : name => b if b.versioning }
bucket = minio_s3_bucket.this[each.key].bucket
versioning_configuration {
status = "Enabled"
}
}
resource "minio_ilm_policy" "this" {
for_each = var.service.buckets
bucket = minio_s3_bucket.this[each.key].bucket
rule {
id = "clean-rule-1"
status = "Enabled"
dynamic "noncurrent_expiration" {
for_each = each.value.versioning ? [1] : []
content {
days = "${each.value.noncurrent_days}d"
}
}
expired_object_delete_marker = each.value.versioning
abort_incomplete_multipart_upload {
days_after_initiation = "3d"
}
}
}
resource "minio_iam_user" "this" {
name = var.service.name
}
resource "minio_iam_policy" "get_upd_del_policy" {
name = var.service.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow",
Action = ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
Resource = [
for bucket in minio_s3_bucket.this : "arn:aws:s3:::${bucket.bucket}/*"
]
},
{
Effect = "Allow",
Action = ["s3:ListBucket"],
Resource = [
for bucket in minio_s3_bucket.this : "arn:aws:s3:::${bucket.bucket}"
]
}]
})
}
resource "minio_iam_user_policy_attachment" "this" {
user_name = minio_iam_user.this.name
policy_name = minio_iam_policy.get_upd_del_policy.name
}
resource "minio_iam_service_account" "this" {
target_user = minio_iam_user.this.name
description = "keys of ${var.service.name}"
depends_on = [minio_iam_user_policy_attachment.this]
}
+15
View File
@@ -0,0 +1,15 @@
output "buckets" {
value = [for bucket in minio_s3_bucket.this : bucket.bucket]
description = "buckets of the service"
}
output "access_key" {
value = minio_iam_service_account.this.access_key
description = "access key of the service"
}
output "secret_key" {
value = minio_iam_service_account.this.secret_key
description = "secret key of the service"
sensitive = true
}
+13
View File
@@ -0,0 +1,13 @@
variable "service" {
type = object({
name = string
buckets = map(object({
versioning = optional(bool, true)
noncurrent_days = optional(number, 30)
}))
})
validation {
condition = length(var.service.buckets) > 0
error_message = "У сервиса должен быть хотя бы один бакет: IAM-политика с пустым списком Resource невалидна."
}
}
+8
View File
@@ -0,0 +1,8 @@
terraform {
required_providers {
minio = {
source = "aminueza/minio"
version = ">= 3.44.0"
}
}
}
+29
View File
@@ -0,0 +1,29 @@
output "infra" {
value = var.infra
}
output "services" {
value = var.services
}
output "vault" {
value = {
address = "http://${var.infra.vault.host}:${var.infra.vault.port}"
mount = var.infra.vault.mount
database_mount = var.infra.vault.database_mount
}
}
output "silo" {
value = {
server = "${var.infra.silo.host}:${var.infra.silo.port}"
internal_endpoint = "http://${var.infra.silo.internal_host}:${var.infra.silo.internal_port}"
admin_user = var.infra.silo.admin_user
state_bucket = var.infra.silo.state_bucket
backup_bucket = var.infra.silo.backup_bucket
}
}
output "postgres" {
value = var.infra.postgres
}
+60
View File
@@ -0,0 +1,60 @@
variable "infra" {
type = object({
postgres = object({
image = string
admin_user = string
host = string
port = number
internal_host = string
internal_port = number
})
pgadmin = object({
image = string
port = number
email = string
})
silo = object({
image = string
admin_user = string
host = string
port = number
console_port = number
internal_host = string
internal_port = number
state_bucket = string
backup_bucket = string
})
vault = object({
image = string
host = string
port = number
mount = string
database_mount = string
})
})
}
variable "services" {
type = map(object({
postgres = optional(object({
username = string
name = string
sslmode = optional(string, "disable")
pgvector = optional(bool, false)
}))
buckets = optional(map(object({
versioning = optional(bool, true)
noncurrent_days = optional(number, 30)
})), {})
}))
validation {
condition = alltrue(flatten([
for svc in values(var.services) : [
for name in keys(svc.buckets) :
can(regex("^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$", name))
]
]))
error_message = "Имя бакета: 3–63 символа, строчные латинские буквы, цифры, точки и дефисы."
}
}