1 parent
64be99308b
commit
5cba142003
38 files changed
+565
-353
No files matched your search
@@ -1,5 +0,0 @@
|
||||
locals {
|
||||
databases = var.service.database == null ? {} : {
|
||||
(var.service.database.name) = var.service.database
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
variable "service" {
|
||||
type = object({
|
||||
name = string
|
||||
database = optional(object({
|
||||
name = string
|
||||
username = string
|
||||
pgvector = optional(bool, false)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
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 невалидна."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
locals {
|
||||
buckets = concat(var.read_write, var.read_only)
|
||||
|
||||
statements = [
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"]
|
||||
Resource = [for bucket in var.read_write : "arn:aws:s3:::${bucket}/*"]
|
||||
},
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = ["s3:GetObject"]
|
||||
Resource = [for bucket in var.read_only : "arn:aws:s3:::${bucket}/*"]
|
||||
},
|
||||
{
|
||||
Effect = "Allow"
|
||||
Action = ["s3:ListBucket"]
|
||||
Resource = [for bucket in local.buckets : "arn:aws:s3:::${bucket}"]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
resource "minio_iam_user" "this" {
|
||||
name = var.name
|
||||
}
|
||||
|
||||
resource "minio_iam_policy" "this" {
|
||||
name = var.name
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [for statement in local.statements : statement if length(statement.Resource) > 0]
|
||||
})
|
||||
}
|
||||
|
||||
resource "minio_iam_user_policy_attachment" "this" {
|
||||
user_name = minio_iam_user.this.name
|
||||
policy_name = minio_iam_policy.this.name
|
||||
}
|
||||
|
||||
resource "minio_iam_service_account" "this" {
|
||||
target_user = minio_iam_user.this.name
|
||||
description = "keys of ${var.name}"
|
||||
|
||||
depends_on = [minio_iam_user_policy_attachment.this]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
output "buckets" {
|
||||
value = local.buckets
|
||||
}
|
||||
|
||||
output "access_key" {
|
||||
value = minio_iam_service_account.this.access_key
|
||||
}
|
||||
|
||||
output "secret_key" {
|
||||
value = minio_iam_service_account.this.secret_key
|
||||
sensitive = true
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "read_write" {
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
variable "read_only" {
|
||||
type = list(string)
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,33 @@
|
||||
resource "minio_s3_bucket" "this" {
|
||||
bucket = var.name
|
||||
acl = "private"
|
||||
}
|
||||
|
||||
resource "minio_s3_bucket_versioning" "this" {
|
||||
count = var.versioning ? 1 : 0
|
||||
bucket = minio_s3_bucket.this.bucket
|
||||
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
}
|
||||
}
|
||||
|
||||
resource "minio_ilm_policy" "this" {
|
||||
bucket = minio_s3_bucket.this.bucket
|
||||
|
||||
rule {
|
||||
id = "cleanup"
|
||||
status = "Enabled"
|
||||
expiration = var.expire_days == null ? null : "${var.expire_days}d"
|
||||
dynamic "noncurrent_expiration" {
|
||||
for_each = var.versioning ? [1] : []
|
||||
content {
|
||||
days = "${var.noncurrent_days}d"
|
||||
}
|
||||
}
|
||||
expired_object_delete_marker = var.versioning && var.expire_days == null
|
||||
abort_incomplete_multipart_upload {
|
||||
days_after_initiation = "3d"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
output "name" {
|
||||
value = minio_s3_bucket.this.bucket
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "versioning" {
|
||||
type = bool
|
||||
}
|
||||
|
||||
variable "noncurrent_days" {
|
||||
type = number
|
||||
}
|
||||
|
||||
variable "expire_days" {
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
minio = {
|
||||
source = "aminueza/minio"
|
||||
version = ">= 3.44.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,42 @@ output "infra" {
|
||||
value = var.infra
|
||||
}
|
||||
|
||||
output "buckets" {
|
||||
value = var.buckets
|
||||
}
|
||||
|
||||
output "databases" {
|
||||
value = {
|
||||
for name, db in var.databases : name => merge(db, { owner = "${name}_owner" })
|
||||
}
|
||||
}
|
||||
|
||||
output "services" {
|
||||
value = var.services
|
||||
}
|
||||
|
||||
output "service_buckets" {
|
||||
value = {
|
||||
for name, svc in var.services : name => {
|
||||
read_write = [for bucket, access in svc.buckets : bucket if access == "rw"]
|
||||
read_only = [for bucket, access in svc.buckets : bucket if access == "ro"]
|
||||
} if length(svc.buckets) > 0
|
||||
}
|
||||
}
|
||||
|
||||
output "service_databases" {
|
||||
value = merge([
|
||||
for name, svc in var.services : {
|
||||
for db in svc.databases : "${name}-${db}" => {
|
||||
service = name
|
||||
database = db
|
||||
role = "${name}-${db}"
|
||||
owner = "${db}_owner"
|
||||
}
|
||||
}
|
||||
]...)
|
||||
}
|
||||
|
||||
output "vault" {
|
||||
value = {
|
||||
address = "http://${var.infra.vault.host}:${var.infra.vault.port}"
|
||||
|
||||
+67
-15
@@ -7,6 +7,7 @@ variable "infra" {
|
||||
port = number
|
||||
internal_host = string
|
||||
internal_port = number
|
||||
sslmode = string
|
||||
})
|
||||
pgadmin = object({
|
||||
image = string
|
||||
@@ -34,27 +35,78 @@ variable "infra" {
|
||||
})
|
||||
}
|
||||
|
||||
variable "buckets" {
|
||||
type = map(object({
|
||||
versioning = optional(bool, true)
|
||||
noncurrent_days = optional(number, 30)
|
||||
expire_days = optional(number)
|
||||
}))
|
||||
default = {}
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for name in keys(var.buckets) : can(regex("^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$", name))
|
||||
])
|
||||
error_message = "Имя бакета: 3–63 символа, строчные латинские буквы, цифры, точки и дефисы."
|
||||
}
|
||||
|
||||
validation {
|
||||
condition = contains(keys(var.buckets), var.infra.silo.backup_bucket)
|
||||
error_message = "Бакет из infra.silo.backup_bucket должен быть описан в buckets."
|
||||
}
|
||||
|
||||
validation {
|
||||
condition = !contains(keys(var.buckets), var.infra.silo.state_bucket)
|
||||
error_message = "Бакет из infra.silo.state_bucket создаётся отдельно, в buckets его описывать нельзя."
|
||||
}
|
||||
}
|
||||
|
||||
variable "databases" {
|
||||
type = map(object({
|
||||
pgvector = optional(bool, false)
|
||||
}))
|
||||
default = {}
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for name in keys(var.databases) : can(regex("^[a-z_][a-z0-9_]{0,55}$", name))
|
||||
])
|
||||
error_message = "Имя базы: до 56 символов, строчные латинские буквы, цифры и подчёркивания, не с цифры."
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})), {})
|
||||
buckets = optional(map(string), {})
|
||||
databases = optional(list(string), [])
|
||||
}))
|
||||
default = {}
|
||||
|
||||
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))
|
||||
]
|
||||
for svc in values(var.services) : [for bucket in keys(svc.buckets) : contains(keys(var.buckets), bucket)]
|
||||
]))
|
||||
error_message = "Имя бакета: 3–63 символа, строчные латинские буквы, цифры, точки и дефисы."
|
||||
error_message = "Сервис ссылается на бакет, которого нет в buckets."
|
||||
}
|
||||
|
||||
validation {
|
||||
condition = alltrue(flatten([
|
||||
for svc in values(var.services) : [for access in values(svc.buckets) : contains(["rw", "ro"], access)]
|
||||
]))
|
||||
error_message = "Доступ к бакету: rw или ro."
|
||||
}
|
||||
|
||||
validation {
|
||||
condition = alltrue(flatten([
|
||||
for svc in values(var.services) : [for db in svc.databases : contains(keys(var.databases), db)]
|
||||
]))
|
||||
error_message = "Сервис ссылается на базу, которой нет в databases."
|
||||
}
|
||||
|
||||
validation {
|
||||
condition = alltrue(flatten([
|
||||
for name, svc in var.services : [for db in svc.databases : length("${name}-${db}") <= 63]
|
||||
]))
|
||||
error_message = "Имя роли <сервис>-<база> длиннее 63 символов."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
resource "random_password" "this" {
|
||||
length = 32
|
||||
special = false
|
||||
}
|
||||
|
||||
resource "postgresql_role" "this" {
|
||||
name = var.role
|
||||
login = true
|
||||
password = random_password.this.result
|
||||
roles = [var.owner]
|
||||
assume_role = var.owner
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
output "username" {
|
||||
value = postgresql_role.this.name
|
||||
}
|
||||
|
||||
output "password" {
|
||||
value = random_password.this.result
|
||||
sensitive = true
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
variable "role" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "owner" {
|
||||
type = string
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,30 @@
|
||||
resource "postgresql_role" "owner" {
|
||||
name = var.owner
|
||||
}
|
||||
|
||||
resource "postgresql_database" "this" {
|
||||
name = var.name
|
||||
owner = postgresql_role.owner.name
|
||||
}
|
||||
|
||||
resource "postgresql_extension" "vector" {
|
||||
count = var.pgvector ? 1 : 0
|
||||
name = "vector"
|
||||
database = postgresql_database.this.name
|
||||
}
|
||||
|
||||
resource "postgresql_grant" "public" {
|
||||
database = postgresql_database.this.name
|
||||
role = "public"
|
||||
object_type = "database"
|
||||
privileges = []
|
||||
}
|
||||
|
||||
resource "postgresql_grant" "owner" {
|
||||
database = postgresql_database.this.name
|
||||
role = postgresql_role.owner.name
|
||||
object_type = "database"
|
||||
privileges = ["CONNECT", "CREATE", "TEMPORARY"]
|
||||
|
||||
depends_on = [postgresql_grant.public]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
output "name" {
|
||||
value = postgresql_database.this.name
|
||||
}
|
||||
|
||||
output "owner" {
|
||||
value = postgresql_role.owner.name
|
||||
|
||||
depends_on = [postgresql_grant.owner]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "owner" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "pgvector" {
|
||||
type = bool
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
postgresql = {
|
||||
source = "cyrilgdn/postgresql"
|
||||
version = ">= 1.27.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user