From 37a304d1614ab655733a50d5877b98b38a010a5f Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sun, 8 Jun 2025 19:33:26 +0200
Subject: [PATCH] Add minio to terraform

---
 .env.example                  |  3 ++
 .terraform.lock.hcl           | 20 +++++++++++++
 main.tf                       | 11 +++++++
 modules/services/main.tf      |  7 +++++
 modules/services/minio.tf     | 56 +++++++++++++++++++++++++++++++++++
 modules/services/output.tf    | 13 ++++++++
 modules/services/variables.tf |  2 ++
 output.tf                     |  5 ++++
 variables.tf                  | 13 ++++++++
 9 files changed, 130 insertions(+)
 create mode 100644 modules/services/minio.tf

diff --git a/.env.example b/.env.example
index 2ad1fa2..358d71a 100755
--- a/.env.example
+++ b/.env.example
@@ -25,6 +25,9 @@ TF_VAR_healthchecksio_api_key=
 
 TF_VAR_mailcow_api_key=
 
+TF_VAR_minio_user=
+TF_VAR_minio_password=
+
 TF_VAR_ovh_application_key=
 TF_VAR_ovh_application_secret=
 TF_VAR_ovh_consumer_key=
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
index 2b50708..87e0419 100644
--- a/.terraform.lock.hcl
+++ b/.terraform.lock.hcl
@@ -1,6 +1,26 @@
 # This file is maintained automatically by "tofu init".
 # Manual edits may be lost in future updates.
 
+provider "registry.opentofu.org/aminueza/minio" {
+  version     = "3.5.2"
+  constraints = "~> 3.5.2"
+  hashes = [
+    "h1:3G/Q/dlf4ItE5tvE1zvSDUW4bYvwdCMVsHNAhMq9328=",
+    "zh:5513c7b20eac89b7bc27b1f762ff03058b4c75456523d5065c41be170fc1ce53",
+    "zh:597ec8ab8169ab4d044b7d442e65b03bbce2516c15f718510e8c80b5fc451be6",
+    "zh:608ff0eb5929b840c11efee1da0273b81d21a8149d8f2d259989597068b48253",
+    "zh:71bee58a6ba43d2a2aadd604c0e04f621fa67cb82ab3633fc5d1366689a5be6b",
+    "zh:9871556bcc3d5daab3cd8e302d1d07bc5693038e1abf8bd11aaf07a439d67a0b",
+    "zh:a3272fbb1ac7dff2481e778284709a5d8b85eda61f26239867eaed9ede57e90a",
+    "zh:a5048a378d5b075a6afac14197fc0fc57f97788cd697749621c07cec7156344c",
+    "zh:a8f28d070653cbd78ca85f9e54d9391a164828de598d481ed53d04882944dcb7",
+    "zh:cbf6895d80828f66fdaa234c6fcf87c329c41eb72391a6d29056b917bce65426",
+    "zh:cd48186b94cee7757a59f848dd6a2bd1d2faa76738a849261ca7cf14e7ca76c2",
+    "zh:cdefdf9bb591ab19c3176c7c8796762e2626ebde0d49971b49393f6bf28533ba",
+    "zh:ef16beff601be117a837cd47a1813be24ee0463d4f36a5d5f7e42a19d6c02b3d",
+  ]
+}
+
 provider "registry.opentofu.org/cyrilgdn/postgresql" {
   version     = "1.23.0"
   constraints = "~> 1.23"
diff --git a/main.tf b/main.tf
index f337364..d8f8271 100644
--- a/main.tf
+++ b/main.tf
@@ -12,6 +12,10 @@ terraform {
       source = "kristofferahl/healthchecksio"
       version = "~> 1.6.0"
     }
+    minio = {
+      source  = "aminueza/minio"
+      version = "~> 3.5.2"
+    }
     ovh = {
       source = "ovh/ovh"
       version = "~> 0.48.0"
@@ -81,6 +85,13 @@ provider "healthchecksio" {
   api_key = var.healthchecksio_api_key
 }
 
+provider "minio" {
+  minio_server   = var.minio_server
+  minio_user     = var.minio_user
+  minio_password = var.minio_password
+  minio_ssl      = true
+}
+
 provider "ovh" {
   endpoint = "ovh-eu"
   application_key = var.ovh_application_key
diff --git a/modules/services/main.tf b/modules/services/main.tf
index 5ef7d96..fee3e85 100644
--- a/modules/services/main.tf
+++ b/modules/services/main.tf
@@ -4,6 +4,10 @@ terraform {
       source = "goauthentik/authentik"
       version = "~> 2025.2.0"
     }
+    minio = {
+      source  = "aminueza/minio"
+      version = "~> 3.5.2"
+    }
     mailcow = {
       source = "l-with/mailcow"
       version = "~> 0.7.5"
@@ -19,4 +23,7 @@ locals {
   services_auth = {for key, val in var.services : key => val if val.auth}
   services_database = {for key, val in var.services : key => val if val.database}
   services_mail = {for key, val in var.services : key => val if val.mail != null}
+
+  services_s3 = {for key, val in var.services : key => (val.s3_buckets != null) ? val.s3_buckets : [key] if (val.s3 == "internal")}
+  buckets_s3 = merge([for key, val in local.services_s3 : {for bucket in val : bucket => key}]...)
 }
diff --git a/modules/services/minio.tf b/modules/services/minio.tf
new file mode 100644
index 0000000..414ba39
--- /dev/null
+++ b/modules/services/minio.tf
@@ -0,0 +1,56 @@
+# Create a user first
+resource "minio_iam_user" "service_users" {
+  for_each = local.services_s3
+  name = each.key
+}
+
+resource "minio_accesskey" "service_access_keys" {
+  for_each = local.services_s3
+  user = minio_iam_user.service_users[each.key].name
+}
+
+resource "minio_s3_bucket" "service_buckets" {
+  for_each = local.buckets_s3
+  bucket = replace("${each.key}.serguzim.me", "_", "-")
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+
+resource "minio_iam_policy" "service_bucket_policies" {
+  for_each = local.buckets_s3
+  name     = each.key
+  policy   = jsonencode({
+    Version = "2012-10-17",
+    Statement = [
+      {
+        Sid = "${each.key} statement"
+        Effect = "Allow",
+        Action = ["s3:*"],
+        Principal = "*",
+        Resource = "${minio_s3_bucket.service_buckets[each.key].arn}/*"
+      }
+    ]
+  })
+}
+
+resource "minio_iam_user_policy_attachment" "service_bucket_policy_attachments" {
+  for_each    = local.buckets_s3
+  user_name   = minio_iam_user.service_users[each.value].id
+  policy_name = minio_iam_policy.service_bucket_policies[each.key].id
+}
+
+//resource "minio_iam_service_account" "service_accounts" {
+//  for_each = minio_iam_user.service_users
+//  target_user = each.value.name
+//  policy = jsonencode({
+//    Version = "2012-10-17",
+//    Statement = [{
+//      Action = [
+//        "s3:*",
+//      ],
+//      "Effect": "Allow",
+//      "Resource": []
+//    }]
+//  })
+//}
\ No newline at end of file
diff --git a/modules/services/output.tf b/modules/services/output.tf
index c63d091..fe49eb6 100644
--- a/modules/services/output.tf
+++ b/modules/services/output.tf
@@ -9,6 +9,19 @@ output "authentik_data" {
   sensitive = true
 }
 
+output "minio_data" {
+  value = {
+    for key, val in local.buckets_s3 : key => {
+      access_key   = minio_accesskey.service_access_keys[val].access_key
+      secret_key   = minio_accesskey.service_access_keys[val].secret_key
+      name         = minio_s3_bucket.service_buckets[key].bucket
+      region       = "eu-de-1" // TODO make dynamic
+      api_endpoint = "https://s3.serguzim.me" // TODO make dynamic
+    }
+  }
+  sensitive = true
+}
+
 output "postgresql_data" {
   value = {
     for key in keys(postgresql_database.service_databases) : key => {
diff --git a/modules/services/variables.tf b/modules/services/variables.tf
index 9ac5c49..a37e27a 100644
--- a/modules/services/variables.tf
+++ b/modules/services/variables.tf
@@ -17,6 +17,8 @@ variable "services" {
     auth = bool
     auth_cert = optional(string)
     auth_redirects = optional(list(string))
+    s3 = optional(string)
+    s3_buckets = optional(list(string))
     database = bool
     mail = optional(string)
   }))
diff --git a/output.tf b/output.tf
index 61c3440..3fbb9b6 100644
--- a/output.tf
+++ b/output.tf
@@ -22,6 +22,11 @@ output "mailcow_data" {
   sensitive = true
 }
 
+output "minio_data" {
+  value = module.services.minio_data
+  sensitive = true
+}
+
 output "postgresql_data" {
   value = module.services.postgresql_data
   sensitive = true
diff --git a/variables.tf b/variables.tf
index 8702cc4..4299102 100644
--- a/variables.tf
+++ b/variables.tf
@@ -51,6 +51,19 @@ variable "mailcow_api_key" {
 }
 
 
+variable "minio_server" {
+  default = "s3.serguzim.me"
+}
+
+variable "minio_user" {
+  sensitive = true
+}
+
+variable "minio_password" {
+  sensitive = true
+}
+
+
 variable "ovh_application_key" {
   sensitive = true
 }