From ae76ef9c0bc2e6cf8084f67de0cb618c408cc6e1 Mon Sep 17 00:00:00 2001
From: Chris Cowley <1736762+chriscowley@users.noreply.github.com>
Date: Fri, 13 Sep 2024 10:12:28 +0200
Subject: [PATCH] many things

---
 20-post-k8s/backup.tf         |  46 ++++++++++++++++
 authentik/.terraform.lock.hcl |  34 ++++++------
 authentik/chat.tf             | 100 +++++++++++++++++-----------------
 authentik/dashy.tf            |  40 ++++++++++++++
 authentik/data.tf             |  18 ++++++
 authentik/forgejo.tf          |  53 ++++++++++++++++++
 authentik/grafana.tf          |  21 +------
 authentik/groups.tf           |   4 ++
 authentik/immich.tf           |  14 ++---
 authentik/jellyfin.tf         |  42 ++++++++++++++
 authentik/lidarr.tf           |  20 +++++++
 authentik/nextcloud.tf        |   8 +--
 authentik/paperless-ngx.tf    |   6 +-
 authentik/provider.tf         |   2 +-
 authentik/users.tf            |   1 +
 forgejo/.terraform.lock.hcl   |  25 +++++++++
 forgejo/Makefile              |   8 +++
 forgejo/main.tf               |   6 ++
 forgejo/provider.tf           |  12 ++++
 forgejo/variables.tf          |   5 ++
 20 files changed, 365 insertions(+), 100 deletions(-)
 create mode 100644 20-post-k8s/backup.tf
 create mode 100644 authentik/dashy.tf
 create mode 100644 authentik/data.tf
 create mode 100644 authentik/forgejo.tf
 create mode 100644 authentik/jellyfin.tf
 create mode 100644 authentik/lidarr.tf
 create mode 100644 forgejo/.terraform.lock.hcl
 create mode 100644 forgejo/Makefile
 create mode 100644 forgejo/main.tf
 create mode 100644 forgejo/provider.tf
 create mode 100644 forgejo/variables.tf

diff --git a/20-post-k8s/backup.tf b/20-post-k8s/backup.tf
new file mode 100644
index 0000000..1c17b55
--- /dev/null
+++ b/20-post-k8s/backup.tf
@@ -0,0 +1,46 @@
+resource "b2_bucket" "cowley-tech-home-backup" {
+  bucket_name = "cowley-tech-home-backup"
+  bucket_type = "allPrivate"
+}
+
+resource "b2_application_key" "user" {
+  for_each = toset(["timothy", "nicolas", "nadege"])
+
+  key_name = "cowley-tech-${each.key}-backup"
+  bucket_id = b2_bucket.cowley-tech-home-backup.id
+  capabilities       = [
+        "deleteFiles",
+        "listBuckets",
+        "listFiles",
+        "readBuckets",
+        "readFiles",
+        "writeFiles",
+    ]
+}
+
+resource "b2_application_key" "admin" {
+
+  key_name = "cowley-tech-admin-backup"
+  bucket_id = b2_bucket.cowley-tech-home-backup.id
+  capabilities       = [
+        "deleteFiles",
+        "listBuckets",
+        "listFiles",
+        "readBuckets",
+        "readFiles",
+        "writeFiles",
+    ]
+}
+#
+resource "kubernetes_secret" "b2-backup" {
+  for_each = toset(["timothy", "nicolas", "nadege"])
+
+  metadata {
+    name = "b2-backup-credentials-${each.key}"
+    namespace = "default"
+  }
+  data = {
+    B2_APPLICATION_KEY_ID = b2_application_key.user[each.key].application_key_id
+    B2_APPLICATION_KEY = b2_application_key.user[each.key].application_key
+  }
+}
diff --git a/authentik/.terraform.lock.hcl b/authentik/.terraform.lock.hcl
index 1df64a4..0f7aa76 100644
--- a/authentik/.terraform.lock.hcl
+++ b/authentik/.terraform.lock.hcl
@@ -2,24 +2,24 @@
 # Manual edits may be lost in future updates.
 
 provider "registry.opentofu.org/goauthentik/authentik" {
-  version     = "2024.6.0"
-  constraints = "2024.6.0"
+  version     = "2024.8.2"
+  constraints = "2024.8.2"
   hashes = [
-    "h1:S9p9njz1sEpXMOY7vL6YGqOVMfYsX1AbUy2GhJ121C0=",
-    "zh:1faa2890439a76b18b05f6c7c753502615de5e34157dc77a2d2d4bbfd6ab4dc8",
-    "zh:288ce51c155380b55eb5b6cd82158b1d7e7193cede072f8be4735a4d6b1421fe",
-    "zh:397e2a61f36fadbcf7e07f914d27139c3d828323c77445194e6e6721e5f4fb3b",
-    "zh:3bdff2f4131fdc70eb5d0ae88f28e0c470b8dbde00735b239603347a451a2df0",
-    "zh:3c959ad7d3f4645e942ae4f33ab8736781df44e12f7185e35622e00625ee6f96",
-    "zh:66f8e918229a0b4d9654244d6bca921547ea7ee6582d302c37d96db3252315a3",
-    "zh:68b098049de3818290978c5db855a6fc52618dea9f7c180c5e4e322144a9d801",
-    "zh:6986198640803382504afeaac069a3f7c89262f44e03f6916005766095f4ac80",
-    "zh:6edfe344fa96e55de95dba04d58d08b332b59dadf93c822d38e321f4cb6fe4f5",
-    "zh:a4325ae5bed223665f39534397cfae9b4f9364b98523d200200f240deaf7f797",
-    "zh:cb60056969297c1aaaf213a477080780ef957926ec64913fab1db33409bc4c08",
-    "zh:e744a42dc4dba812846a837fc328f73e390531a64c16a1e280a5c1fea4c7e176",
-    "zh:f1ea072c1d3a7becdc4579bc85903642532639f134c8cf7e49e2e0f3bad5aee3",
-    "zh:f4a0c5a664d131d5c6a00e194b855e76ac5e6f0e0404e85e6fc3fa95029b10c1",
+    "h1:+RVux9TSmkUsxIinptup4oOdfzObeXLaOnc0oi0Vat4=",
+    "zh:1a08cf73a35237bf84e8761eb026b4175bc34bab4c6a206110cb9a3d06c86391",
+    "zh:1f5807c2ab22e21a9f4c1d19bc64c52150ac003c6a90417315d8fafb6cbfd09d",
+    "zh:20237b247cbee340d03629f3bb4e156e8ccf65db246eeffb4cad3dabe34f26bb",
+    "zh:416ee251d684360e993ea3bdd7b9b3abb869f1d27d3bfe7c53731d444493bad3",
+    "zh:4d76186b29969509fb950ddce03b80eba9bc3409b6bbd20f8a9e7623d84b63c0",
+    "zh:588bbeb5768dc0e6d6b3e7bc67709ef7bc4a7f48eeb659801bc8511d646141ac",
+    "zh:5f95796b207c90e4dcf5d9f2945929351c5709754ce66839279e87279a04204f",
+    "zh:60263694ce7e107f3f78d5cc727d6143082e0eaa97b15727af83aaed8305d351",
+    "zh:6ecc4bd586e37987cfa057fc3a3f87bd461e3215d9efb5654fdd639a8d5318e9",
+    "zh:9e05d3d930a92f160cd788a699b3e11c80b59cb67b5f0b4a9970a1f7e9b08045",
+    "zh:c6ecaafa4176f12c8930fe2225c34a6d64eb9eb9774b50df17714d2ae338068d",
+    "zh:d781b9de7ce45a0b67b177705f755746b3afb11c4cac9171825bd9ace4017da6",
+    "zh:df6d9bc87b752c4e75f5246b32a98049a3253762389fd8476a9b4f96729f9cdd",
+    "zh:ef6c1ce79965e212929674063de6280abae5ee5c064049880ab81ca0e27b7434",
   ]
 }
 
diff --git a/authentik/chat.tf b/authentik/chat.tf
index bedac97..ca85085 100644
--- a/authentik/chat.tf
+++ b/authentik/chat.tf
@@ -1,50 +1,50 @@
-resource "random_id" "chat_client_id" {
-  byte_length = 16
-}
-
-resource "authentik_provider_oauth2" "chat" {
-  name = "Chat"
-  #  Required. You can use the output of:
-  #     $ openssl rand -hex 16
-  client_id = random_id.chat_client_id.id
-
-  # Optional: will be generated if not provided
-  # client_secret = "my_client_secret"
-
-  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
-
-  redirect_uris = [
-    "https://chat.lab.cowley.tech/oauth/oidc/callback"
-  ]
-  property_mappings = [
-    data.authentik_scope_mapping.scope-openid.id,
-    data.authentik_scope_mapping.scope-email.id,
-    data.authentik_scope_mapping.scope-profile.id,
-  ]
-  lifecycle {
-    ignore_changes = [
-      signing_key,
-      authentication_flow,
-    ]
-  }
-}
-
-resource "authentik_application" "chat" {
-  name              = "Chat"
-  slug              = "chat"
-  protocol_provider = authentik_provider_oauth2.chat.id
-}
-
-resource "kubernetes_secret" "chat" {
-  metadata {
-    name      = "open-webui-authentik"
-    namespace = "ollama"
-  }
-  data = {
-    OAUTH_CLIENT_ID     = authentik_provider_oauth2.chat.client_id
-    OAUTH_CLIENT_SECRET = authentik_provider_oauth2.chat.client_secret
-    OPENID_PROVIDER_URL = "https://auth.lab.cowley.tech/application/o/chat/.well-known/openid-configuration"
-    OAUTH_PROVIDER_NAME = "Authentik"
-    OAUTH_SCOPES        = "openid email profile"
-  }
-}
+#resource "random_id" "chat_client_id" {
+#  byte_length = 16
+#}
+#
+#resource "authentik_provider_oauth2" "chat" {
+#  name = "Chat"
+#  #  Required. You can use the output of:
+#  #     $ openssl rand -hex 16
+#  client_id = random_id.chat_client_id.id
+#
+#  # Optional: will be generated if not provided
+#  # client_secret = "my_client_secret"
+#
+#  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
+#
+#  redirect_uris = [
+#    "https://chat.lab.cowley.tech/oauth/oidc/callback"
+#  ]
+#  property_mappings = [
+#    data.authentik_scope_mapping.scope-openid.id,
+#    data.authentik_scope_mapping.scope-email.id,
+#    data.authentik_scope_mapping.scope-profile.id,
+#  ]
+#  lifecycle {
+#    ignore_changes = [
+#      signing_key,
+#      authentication_flow,
+#    ]
+#  }
+#}
+#
+#resource "authentik_application" "chat" {
+#  name              = "Chat"
+#  slug              = "chat"
+#  protocol_provider = authentik_provider_oauth2.chat.id
+#}
+#
+#resource "kubernetes_secret" "chat" {
+#  metadata {
+#    name      = "open-webui-authentik"
+#    namespace = "ollama"
+#  }
+#  data = {
+#    OAUTH_CLIENT_ID     = authentik_provider_oauth2.chat.client_id
+#    OAUTH_CLIENT_SECRET = authentik_provider_oauth2.chat.client_secret
+#    OPENID_PROVIDER_URL = "https://auth.lab.cowley.tech/application/o/chat/.well-known/openid-configuration"
+#    OAUTH_PROVIDER_NAME = "Authentik"
+#    OAUTH_SCOPES        = "openid email profile"
+#  }
+#}
diff --git a/authentik/dashy.tf b/authentik/dashy.tf
new file mode 100644
index 0000000..c623c25
--- /dev/null
+++ b/authentik/dashy.tf
@@ -0,0 +1,40 @@
+resource "random_id" "dashy_client_id" {
+  byte_length = 16
+}
+
+resource "authentik_provider_oauth2" "dashy" {
+  name = "Dashy"
+  #  Required. You can use the output of:
+  #     $ openssl rand -hex 16
+  client_id = random_id.dashy_client_id.id
+  authentication_flow = data.authentik_flow.default-authentication-flow.id
+  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
+
+  client_type = "public"
+
+  redirect_uris = [
+    "https://dash.lab.cowley.tech/",
+    ".*"
+  ]
+
+  sub_mode = "user_email"
+
+  property_mappings = [
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
+  ]
+  lifecycle {
+    ignore_changes = [
+      signing_key,
+      authentication_flow,
+    ]
+  }
+}
+
+resource "authentik_application" "dashy" {
+  name              = "Dashy"
+  slug              = "dashy"
+  protocol_provider = authentik_provider_oauth2.dashy.id
+  open_in_new_tab = true
+}
diff --git a/authentik/data.tf b/authentik/data.tf
new file mode 100644
index 0000000..d19828e
--- /dev/null
+++ b/authentik/data.tf
@@ -0,0 +1,18 @@
+data "authentik_flow" "default-provider-authorization-implicit-consent" {
+  slug = "default-provider-authorization-implicit-consent"
+}
+
+data "authentik_flow" "default-authentication-flow" {
+  slug = "default-authentication-flow"
+}
+data "authentik_property_mapping_provider_scope" "scope-email" {
+  name = "authentik default OAuth Mapping: OpenID 'email'"
+}
+
+data "authentik_property_mapping_provider_scope" "scope-profile" {
+  name = "authentik default OAuth Mapping: OpenID 'profile'"
+}
+
+data "authentik_property_mapping_provider_scope" "scope-openid" {
+  name = "authentik default OAuth Mapping: OpenID 'openid'"
+}
diff --git a/authentik/forgejo.tf b/authentik/forgejo.tf
new file mode 100644
index 0000000..07eff5d
--- /dev/null
+++ b/authentik/forgejo.tf
@@ -0,0 +1,53 @@
+resource "random_id" "forgejo_client_id" {
+  byte_length = 16
+}
+
+resource "authentik_provider_oauth2" "forgejo" {
+  name = "Forgejo"
+  #  Required. You can use the output of:
+  #     $ openssl rand -hex 16
+  client_id = random_id.forgejo_client_id.id
+
+  # Optional: will be generated if not provided
+  # client_secret = "my_client_secret"
+
+  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
+
+  redirect_uris = [
+    "https://code.lab.cowley.tech/user/oauth2/authentik/callback"
+  ]
+  property_mappings = [
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
+  ]
+  lifecycle {
+    ignore_changes = [
+      signing_key,
+      authentication_flow,
+    ]
+  }
+}
+
+resource "authentik_application" "forgejo" {
+  name              = "ForgeJo"
+  slug              = "forgejo"
+  protocol_provider = authentik_provider_oauth2.forgejo.id
+}
+
+resource "authentik_group" "forgejo-admins" {
+  name = "gitadmin"
+}
+resource "authentik_group" "forgejo-users" {
+  name = "gituser"
+}
+resource "kubernetes_secret" "forgejo-oauth" {
+  metadata {
+    name      = "forgejo-oauth"
+    namespace = "forgejo"
+  }
+  data = {
+    "key" = authentik_provider_oauth2.forgejo.client_id
+    "secret" =  authentik_provider_oauth2.forgejo.client_secret
+  }
+}
diff --git a/authentik/grafana.tf b/authentik/grafana.tf
index 9f4a5f3..38370ea 100644
--- a/authentik/grafana.tf
+++ b/authentik/grafana.tf
@@ -1,18 +1,3 @@
-data "authentik_flow" "default-provider-authorization-implicit-consent" {
-  slug = "default-provider-authorization-implicit-consent"
-}
-
-data "authentik_scope_mapping" "scope-email" {
-  name = "authentik default OAuth Mapping: OpenID 'email'"
-}
-
-data "authentik_scope_mapping" "scope-profile" {
-  name = "authentik default OAuth Mapping: OpenID 'profile'"
-}
-
-data "authentik_scope_mapping" "scope-openid" {
-  name = "authentik default OAuth Mapping: OpenID 'openid'"
-}
 
 resource "random_id" "client_id" {
   byte_length = 16
@@ -34,9 +19,9 @@ resource "authentik_provider_oauth2" "grafana" {
   ]
 
   property_mappings = [
-    data.authentik_scope_mapping.scope-email.id,
-    data.authentik_scope_mapping.scope-profile.id,
-    data.authentik_scope_mapping.scope-openid.id,
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
   ]
 
   lifecycle {
diff --git a/authentik/groups.tf b/authentik/groups.tf
index 0dd38e9..20129db 100644
--- a/authentik/groups.tf
+++ b/authentik/groups.tf
@@ -1,3 +1,7 @@
 data "authentik_group" "admins" {
   name = "authentik Admins"
 }
+
+resource "authentik_group" "arr-users" {
+  name = "arr_users"
+}
diff --git a/authentik/immich.tf b/authentik/immich.tf
index 162cdd6..60a026d 100644
--- a/authentik/immich.tf
+++ b/authentik/immich.tf
@@ -2,15 +2,15 @@
 #  slug = "default-provider-authorization-implicit-consent"
 #}
 #
-#data "authentik_scope_mapping" "scope-email" {
+#data "authentik_property_mapping_provider_scope" "scope-email" {
 #  name = "authentik default OAuth Mapping: OpenID 'email'"
 #}
 #
-#data "authentik_scope_mapping" "scope-profile" {
+#data "authentik_property_mapping_provider_scope" "scope-profile" {
 #  name = "authentik default OAuth Mapping: OpenID 'profile'"
 #}
 #
-#data "authentik_scope_mapping" "scope-openid" {
+#data "authentik_property_mapping_provider_scope" "scope-openid" {
 #  name = "authentik default OAuth Mapping: OpenID 'openid'"
 #}
 #
@@ -30,14 +30,14 @@ resource "authentik_provider_oauth2" "immich" {
   authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
 
   redirect_uris = [
-    "app.immich:/",
+    "app.immich:///oauth-callback",
     "https://photos.lab.cowley.tech/auth/login",
     "https://photos.lab.cowley.tech/user-settings",
   ]
   property_mappings = [
-    data.authentik_scope_mapping.scope-email.id,
-    data.authentik_scope_mapping.scope-profile.id,
-    data.authentik_scope_mapping.scope-openid.id,
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
   ]
   lifecycle {
     ignore_changes = [
diff --git a/authentik/jellyfin.tf b/authentik/jellyfin.tf
new file mode 100644
index 0000000..e824fe8
--- /dev/null
+++ b/authentik/jellyfin.tf
@@ -0,0 +1,42 @@
+resource "random_id" "jellyfin_client_id" {
+  byte_length = 16
+}
+
+resource "authentik_provider_oauth2" "jellyfin" {
+  name = "Jellyfin"
+  client_id = random_id.jellyfin_client_id.id
+
+  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
+
+  redirect_uris = [
+    "https://jellyfin.lab.cowley.tech/sso/OID/start/authentik"
+  ]
+
+  property_mappings = [
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
+  ]
+  lifecycle {
+    ignore_changes = [
+      signing_key,
+      authentication_flow,
+    ]
+  }
+}
+
+resource "authentik_application" "jellyfin" {
+  name              = "Jellyfin"
+  slug              = "jellyfin"
+  protocol_provider = authentik_provider_oauth2.jellyfin.id
+  meta_launch_url = "https://jellyfin.lab.cowley.tech/sso/OID/start/authentik"
+}
+resource "kubernetes_secret" "jellyfin_oidc" {
+  metadata {
+    name = "jellyfin-oidc"
+    namespace = "jellyfin"
+  }
+  data = {
+    client-secret = authentik_provider_oauth2.jellyfin.client_secret
+  }
+}
diff --git a/authentik/lidarr.tf b/authentik/lidarr.tf
new file mode 100644
index 0000000..f1e610c
--- /dev/null
+++ b/authentik/lidarr.tf
@@ -0,0 +1,20 @@
+#resource "authentik_provider_proxy" "lidarr" {
+#  name = "lidarr"
+#  internal_host = "http://lidarr.jellyfin:8686"
+#  external_host = "https://lidarr.lab.cowley.tech"
+#  authorization_flow = data.authentik_flow.default-provider-authorization-implicit-consent.id
+#}
+#
+#resource "authentik_outpost" "lidarr" {
+#  name = "lidarr-outpost"
+#  protocol_providers = [
+#    authentik_provider_proxy.lidarr.id
+#  ]
+#}
+#
+#resource "authentik_application" "lidarr" {
+#  name = "Lidarr"
+#  slug = "lidarr"
+#
+#  protocol_provider = authentik_provider_proxy.lidarr.id
+#}
diff --git a/authentik/nextcloud.tf b/authentik/nextcloud.tf
index 307e4a1..7c89026 100644
--- a/authentik/nextcloud.tf
+++ b/authentik/nextcloud.tf
@@ -1,7 +1,7 @@
-#data "authentik_scope_mapping" "nextcloud" {
+#data "authentik_property_mapping_provider_scope" "nextcloud" {
 #  name = "Nextcloud Profile"
 #}
-resource "authentik_scope_mapping" "nextcloud-scope" {
+resource "authentik_property_mapping_provider_scope" "nextcloud-scope" {
   name       = "Nextcloud Profile"
   scope_name = "profile"
   expression = <<EOF
@@ -48,8 +48,8 @@ resource "authentik_provider_oauth2" "nextcloud" {
   ]
 
   property_mappings = [
-    data.authentik_scope_mapping.scope-email.id,
-    authentik_scope_mapping.nextcloud-scope.id
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    authentik_property_mapping_provider_scope.nextcloud-scope.id
   ]
 
   lifecycle {
diff --git a/authentik/paperless-ngx.tf b/authentik/paperless-ngx.tf
index 68cbd37..a53234b 100644
--- a/authentik/paperless-ngx.tf
+++ b/authentik/paperless-ngx.tf
@@ -15,9 +15,9 @@ resource "authentik_provider_oauth2" "paperless" {
   ]
 
   property_mappings = [
-    data.authentik_scope_mapping.scope-email.id,
-    data.authentik_scope_mapping.scope-profile.id,
-    data.authentik_scope_mapping.scope-openid.id,
+    data.authentik_property_mapping_provider_scope.scope-email.id,
+    data.authentik_property_mapping_provider_scope.scope-profile.id,
+    data.authentik_property_mapping_provider_scope.scope-openid.id,
   ]
   lifecycle {
     ignore_changes = [
diff --git a/authentik/provider.tf b/authentik/provider.tf
index ff19ac0..5e03cc4 100644
--- a/authentik/provider.tf
+++ b/authentik/provider.tf
@@ -10,7 +10,7 @@ terraform {
     }
     authentik = {
       source  = "goauthentik/authentik"
-      version = "2024.6.0"
+      version = "2024.8.2"
     }
   }
 }
diff --git a/authentik/users.tf b/authentik/users.tf
index 909dbf5..e34db6b 100644
--- a/authentik/users.tf
+++ b/authentik/users.tf
@@ -20,6 +20,7 @@ resource "authentik_user" "chris" {
     data.authentik_group.admins.id,
     authentik_group.grafana_admins.id,
     authentik_group.nextcloud_admins.id,
+    authentik_group.arr-users.id
   ]
 #  attributes = jsonencode(
 #    {
diff --git a/forgejo/.terraform.lock.hcl b/forgejo/.terraform.lock.hcl
new file mode 100644
index 0000000..758644d
--- /dev/null
+++ b/forgejo/.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/go-gitea/gitea" {
+  version     = "0.1.0"
+  constraints = "0.1.0"
+  hashes = [
+    "h1:idV0H0z0z4SL5aaDaZTLUYz77LwXBHQSqHAZ1wGuwoY=",
+    "zh:039c743351f4bb17bb423a28a507a3d9b87267ce04e127dbb4ad1a80450a280b",
+    "zh:175b5d60b7e13abd477e82ccc2f53973c60cbcdcd88f49746aa76e452ba9af6d",
+    "zh:2cbf2f51ec50b1be06b2ffbb6e5bb4f57b9da9a730a626c26804ec8a7efd1f94",
+    "zh:3126714438319e1cfc20d100905443ce96fd6c68d77760f65107b2a69b369e54",
+    "zh:4709f799dbfa2629b61b0a4480e71dee2421ecc0539be6e402e87e734717c220",
+    "zh:6c4ca036adb3c958f5526a07d9254f0fb682e74e37741abcce033ddb7178afea",
+    "zh:7076b1f2e0052df6195e4ed7e57314524739d348d02b15aebbe874d1f1625c15",
+    "zh:8875b2d45ba7b1bd8cbfd2049961eb00f12de2b7a71a3a038a1f7f1c2a518e9d",
+    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+    "zh:9d13957c3bf2f8efdd64fb61fbc8a725d1844a7cc08930e601bcee94361988cc",
+    "zh:aff674f42a61dc3f629c8f5335cd85597767f283cae0fa367f402f5a9e9f714c",
+    "zh:c4c44488ccd284a622d982891f2948396fa781a02341426e9d159bb2309b050c",
+    "zh:c696f7481d7545ed305d43e8bdb5eff692af6d2f67a9b41ad22691c84e6651c4",
+    "zh:e28e01407386c324ef9ef2a8f5765aa13d6a1876bbcc50b77b30ce29bbbca3cd",
+    "zh:e9b5b0925391a8752c8af20855d00a376a89d038cf665e12b74c0de9eef2110e",
+  ]
+}
diff --git a/forgejo/Makefile b/forgejo/Makefile
new file mode 100644
index 0000000..9d23b4e
--- /dev/null
+++ b/forgejo/Makefile
@@ -0,0 +1,8 @@
+init:
+	@tofu init
+
+plan:
+	@tofu plan -out tfplan
+
+apply:plan
+	@tofu apply tfplan
diff --git a/forgejo/main.tf b/forgejo/main.tf
new file mode 100644
index 0000000..e55e915
--- /dev/null
+++ b/forgejo/main.tf
@@ -0,0 +1,6 @@
+data "gitea_user" "admin" {
+  username = "forgejoadmin"
+}
+data "gitea_user" "chris" {
+  username = "chris"
+}
diff --git a/forgejo/provider.tf b/forgejo/provider.tf
new file mode 100644
index 0000000..6f0c57a
--- /dev/null
+++ b/forgejo/provider.tf
@@ -0,0 +1,12 @@
+terraform {
+  required_providers {
+    gitea = {
+      source = "go-gitea/gitea"
+      version = "0.1.0"
+    }
+  }
+}
+
+provider "gitea" {
+  base_url = var.gitea_url 
+}
diff --git a/forgejo/variables.tf b/forgejo/variables.tf
new file mode 100644
index 0000000..04d4c84
--- /dev/null
+++ b/forgejo/variables.tf
@@ -0,0 +1,5 @@
+variable "gitea_url" {
+  type = string
+
+  default = "https://code.lab.cowley.tech"
+}