diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4ecd567 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# System +TIMEZONE="Australia/Brisbane" +DATA_DIR="/mnt/appdata" +PUID="1000" +PGID="1000" + +# Cloudflare +CLOUDFLARE_API_TOKEN="your-cloudflare-api-token" +CLOUDFLARE_ACCOUNT_ID="your-cloudflare-account-id" +CLOUDFLARE_ZONE_ID="your-cloudflare-zone-id" +DOMAIN="yourdomain.com" diff --git a/.gitignore b/.gitignore index 6a068d5..1538f56 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ override.tf override.tf.json .terraformrc terraform.rc +.env diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index 6ddc078..0703959 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -24,6 +24,29 @@ provider "registry.opentofu.org/cloudflare/cloudflare" { ] } +provider "registry.opentofu.org/germanbrew/dotenv" { + version = "1.2.5" + constraints = "1.2.5" + hashes = [ + "h1:aUUdKCjUPHBgapuUb36pa8BUhWscSPpt5q1/JKNODsc=", + "zh:01d2c432515ef0ceffc321473a87c7571aaf068c31c36bed203c3450828e5ab5", + "zh:026dec6dcc688cfb6011e71e7c16219af02cc5acfa6ef4e6f803972c85b57eda", + "zh:31a63b727b5a5aea529bea1e557fbe04067c05c032db77afab61c2db0328dbf2", + "zh:3c53fb73bed50012019bdc83bc0502926a80c60e4c8f8fdade11e3a705baee32", + "zh:420b26f57d16fa8750da0e9a45b32846f39efc909de60b5e3b6e23596ab0ff15", + "zh:4e559274f355c79c9c5367b55f26d2a054d585502978dae15b715732d8717772", + "zh:84ab9b4024d53edbd67c83dc56a9af41089148d125e7dd1dab04ee402a8880e8", + "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", + "zh:8f17d9cf82f08e5a6d451d80fec912aebf8ce8a0c880609666613b21de3393ad", + "zh:9f3c17e254c0f2a0eae5b53103976763241e049afcada375cd84af95aa012c18", + "zh:a3fffa256fe3eed985831f3ea90826ac7feab869f433a60c912a80d8d41783e8", + "zh:c6f6aa8249aaaefbf00b6c4c1eadb1166e469a07d36e738e4a3106b87e1b7340", + "zh:d2e89f25111485fb97d75b165bdfe06b47467e1ec62aedc884c9d30b3c138196", + "zh:d588bcb33fab3991fdd27d9d0a8a842244ecc45a32aa2b32ec12fc631c5dd5fb", + "zh:e78ce7620cfa89de13b93e55ba2bb51d68bac410b2f6fbea77d57ba9252f0f80", + ] +} + provider "registry.opentofu.org/hashicorp/random" { version = "3.5.1" constraints = "~> 3.5.1" diff --git a/environments/README.md b/environments/README.md deleted file mode 100644 index aba5ba9..0000000 --- a/environments/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Homelab Environments - -This directory contains environment-specific configurations that help organize your infrastructure modules into logical groupings. - -Each subdirectory represents a category or environment that can be applied independently or together with others. - -``` -/environments/ -├── core/ # Essential infrastructure (tunnel, monitoring) -├── services/ # Application services (ActualBudget, EmulatorJS) -└── network/ # (Future) Network configs -``` \ No newline at end of file diff --git a/environments/core/main.tf b/environments/core/main.tf deleted file mode 100644 index 3165631..0000000 --- a/environments/core/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -// Core infrastructure components -// These are the foundational services that other services depend on - -locals { - module_dir = "../../modules" -} - -// Core monitoring and maintenance service -module "watchtower" { - source = "${local.module_dir}/20-services-apps/watchtower" - - timezone = var.timezone - poll_interval = 86400 - cleanup = true - enable_notifications = var.watchtower_enable_notifications - notification_url = var.watchtower_notification_url -} diff --git a/environments/core/variables.tf b/environments/core/variables.tf deleted file mode 100644 index 2bf4cd4..0000000 --- a/environments/core/variables.tf +++ /dev/null @@ -1,19 +0,0 @@ -// Generic -variable "timezone" { - description = "Timezone for the system" - type = string -} - -// Watchtower -variable "watchtower_enable_notifications" { - description = "Enable Watchtower update notifications" - type = bool - default = false -} - -variable "watchtower_notification_url" { - description = "Webhook URL for Watchtower notifications (Discord, Slack, etc.)" - type = string - sensitive = true - default = "" -} diff --git a/environments/network/main.tf b/environments/network/main.tf deleted file mode 100644 index a80858a..0000000 --- a/environments/network/main.tf +++ /dev/null @@ -1,45 +0,0 @@ -// Network environment -// Contains configurations for network infrastructure - -locals { - module_dir = "../../modules" -} - -module "cloudflare_globals" { - source = "${local.module_dir}/00-globals/cloudflare" - - cloudflare_api_token = var.cloudflare_api_token - cloudflare_account_id = var.cloudflare_account_id - cloudflare_zone_id = var.cloudflare_zone_id - domain = var.domain -} - -module "homelab_docker_network" { - source = "${local.module_dir}/01-networking/docker-network" - - name = "homelab-network" - driver = "bridge" - attachable = true - subnet = "10.100.0.0/16" -} - -module "homelab_cloudflared_tunnel" { - source = "${local.module_dir}/01-networking/cloudflared-tunnel" - - cloudflare_account_id = module.cloudflare_globals.cloudflare_account_id - cloudflare_zone_id = module.cloudflare_globals.cloudflare_zone_id - - tunnel_name = "homelab" - container_name = "cloudflared-homelab" - - ingress_rules = [ - { - hostname = "budget.${var.domain}" - service = "http://actualbudget:5006" - }, - ] - - networks = [module.homelab_docker_network.name] - - monitoring = true -} diff --git a/environments/network/outputs.tf b/environments/network/outputs.tf deleted file mode 100644 index beeb5e0..0000000 --- a/environments/network/outputs.tf +++ /dev/null @@ -1,36 +0,0 @@ -output "cloudflare_account_id" { - description = "Cloudflare account ID" - value = module.cloudflare_globals.cloudflare_account_id -} - -output "cloudflare_zone_id" { - description = "Cloudflare zone ID" - value = module.cloudflare_globals.cloudflare_zone_id -} - -output "domain" { - description = "Base domain name" - value = module.cloudflare_globals.domain -} - -// Docker network outputs -output "homelab_docker_network_name" { - description = "Name of the Docker network" - value = module.homelab_docker_network.name -} - -// Tunnel outputs -output "homelab_cloudflared_tunnel_id" { - description = "ID of the Cloudflare tunnel" - value = module.homelab_cloudflared_tunnel.tunnel_id -} - -output "homelab_cloudflared_tunnel_name" { - description = "Name of the Cloudflare tunnel" - value = module.homelab_cloudflared_tunnel.tunnel_name -} - -output "homelab_cloudflared_tunnel_cname_target" { - description = "CNAME target for the tunnel" - value = module.homelab_cloudflared_tunnel.cname_target -} diff --git a/environments/network/variables.tf b/environments/network/variables.tf deleted file mode 100644 index b0fc7a7..0000000 --- a/environments/network/variables.tf +++ /dev/null @@ -1,21 +0,0 @@ - -variable "cloudflare_api_token" { - description = "API token for Cloudflare with the necessary permissions" - type = string - sensitive = true -} - -variable "cloudflare_account_id" { - description = "Cloudflare account ID" - type = string -} - -variable "cloudflare_zone_id" { - description = "Cloudflare zone ID for the domain" - type = string -} - -variable "domain" { - description = "Base domain name (e.g., example.com)" - type = string -} diff --git a/environments/services/main.tf b/environments/services/main.tf deleted file mode 100644 index 80f76dd..0000000 --- a/environments/services/main.tf +++ /dev/null @@ -1,36 +0,0 @@ -// Application services environment -// Contains configurations for all application services - -// Import global Terraform settings -terraform { - # Include backend configuration if needed - # backend "local" { ... } -} - -locals { - module_dir = "../../modules" -} - -module "actualbudget" { - source = "${local.module_dir}/20-services-apps/actualbudget" - - container_name = "actualbudget" - timezone = var.timezone - data_volume_path = "${var.data_dir}/actual/data" - port = var.actualbudget_port - networks = var.default_networks -} - -module "emulatorjs" { - source = "${local.module_dir}/20-services-apps/emulatorjs" - - container_name = "emulatorjs" - timezone = var.timezone - puid = var.puid - pgid = var.pgid - config_volume_path = "${var.data_dir}/emulatorjs/config" - data_volume_path = "${var.data_dir}/emulatorjs/data" - frontend_port = var.emulatorjs_frontend_port - config_port = var.emulatorjs_config_port - backend_port = var.emulatorjs_backend_port -} diff --git a/environments/services/outputs.tf b/environments/services/outputs.tf deleted file mode 100644 index e44b79b..0000000 --- a/environments/services/outputs.tf +++ /dev/null @@ -1,38 +0,0 @@ -// Services environment outputs - -// ActualBudget -output "actualbudget_container_name" { - description = "The name of the ActualBudget container" - value = module.actualbudget.container_name -} - -output "actualbudget_container_id" { - description = "The ID of the ActualBudget container" - value = module.actualbudget.container_id -} - -output "actualbudget_local_url" { - description = "The local URL to access ActualBudget" - value = module.actualbudget.local_url -} - -// EmulatorJS -output "emulatorjs_container_name" { - description = "The name of the EmulatorJS container" - value = module.emulatorjs.container_name -} - -output "emulatorjs_container_id" { - description = "The ID of the EmulatorJS container" - value = module.emulatorjs.container_id -} - -output "emulatorjs_frontend_url" { - description = "The frontend URL for EmulatorJS" - value = module.emulatorjs.frontend_url -} - -output "emulatorjs_config_url" { - description = "The configuration URL for EmulatorJS" - value = module.emulatorjs.config_url -} diff --git a/environments/services/variables.tf b/environments/services/variables.tf deleted file mode 100644 index d235554..0000000 --- a/environments/services/variables.tf +++ /dev/null @@ -1,50 +0,0 @@ -// Variables for the services environment - -// Generic -variable "timezone" { - description = "Timezone for the system" - type = string -} - -variable "puid" { - description = "User ID for the container" - type = number -} - -variable "pgid" { - description = "Group ID for the container" - type = number -} - -variable "data_dir" { - description = "Base directory for data volumes" - type = string -} - -variable "default_networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -// ActualBudget -variable "actualbudget_port" { - description = "External port for the ActualBudget server" - type = number -} - -// EmulatorJS -variable "emulatorjs_frontend_port" { - description = "External port for the EmulatorJS frontend" - type = number -} - -variable "emulatorjs_config_port" { - description = "External port for the EmulatorJS configuration interface" - type = number -} - -variable "emulatorjs_backend_port" { - description = "External port for the EmulatorJS backend" - type = number -} diff --git a/main.tf b/main.tf index b1910a9..516cc78 100644 --- a/main.tf +++ b/main.tf @@ -1,49 +1,24 @@ -// Root module that orchestrates all environments -// This unified approach keeps a single entry point while organizing by function - -// Network infrastructure -module "network" { - source = "./environments/network" - - // Cloudflare variables - cloudflare_api_token = var.cloudflare_api_token - cloudflare_account_id = var.cloudflare_account_id - cloudflare_zone_id = var.cloudflare_zone_id - domain = var.domain +module "cloudflare_globals" { + source = "./modules/00-globals/cloudflare" } -// Core infrastructure (monitoring, globals) -module "core" { - source = "./environments/core" - - depends_on = [module.network] - - timezone = var.timezone - - // Watchtower variables - watchtower_enable_notifications = var.watchtower_enable_notifications - watchtower_notification_url = var.watchtower_notification_url +module "watchtower" { + source = "./modules/20-services-apps/watchtower" } // Application services module "services" { - source = "./environments/services" - - depends_on = [module.core, module.network] - - timezone = var.timezone - puid = var.puid - pgid = var.pgid - data_dir = var.data_dir - - // ActualBudget variables - actualbudget_port = var.actualbudget_port - - // EmulatorJS variables - emulatorjs_frontend_port = var.emulatorjs_frontend_port - emulatorjs_config_port = var.emulatorjs_config_port - emulatorjs_backend_port = var.emulatorjs_backend_port - - // Docker network variables - default_networks = [module.network.homelab_docker_network_name] + source = "./services" +} + +module "homelab_cloudflared_tunnel" { + source = "./modules/01-networking/cloudflared-tunnel" + cloudflare_account_id = module.cloudflare_globals.cloudflare_account_id + cloudflare_zone_id = module.cloudflare_globals.cloudflare_zone_id + domain = module.cloudflare_globals.domain + tunnel_name = "homelab" + container_name = "cloudflared-homelab" + service_definitions = module.services.service_definitions + networks = [module.services.homelab_docker_network_name] + monitoring = true } diff --git a/modules/00-globals/cloudflare/main.tf b/modules/00-globals/cloudflare/main.tf new file mode 100644 index 0000000..fd5d611 --- /dev/null +++ b/modules/00-globals/cloudflare/main.tf @@ -0,0 +1,32 @@ +terraform { + required_providers { + dotenv = { + source = "germanbrew/dotenv" + } + } +} + +data "dotenv_sensitive" "cloudflare_credentials" {} +data "dotenv" "cloudflare_config" {} + +// Outputs +output "cloudflare_account_id" { + description = "Cloudflare account ID" + value = data.dotenv.cloudflare_config.entries.CLOUDFLARE_ACCOUNT_ID +} + +output "cloudflare_zone_id" { + description = "Cloudflare zone ID" + value = data.dotenv.cloudflare_config.entries.CLOUDFLARE_ZONE_ID +} + +output "domain" { + description = "Base domain name" + value = data.dotenv.cloudflare_config.entries.DOMAIN +} + +output "cloudflare_api_token" { + description = "API token for Cloudflare" + value = data.dotenv_sensitive.cloudflare_credentials.entries.CLOUDFLARE_API_TOKEN + sensitive = true +} diff --git a/modules/00-globals/cloudflare/outputs.tf b/modules/00-globals/cloudflare/outputs.tf deleted file mode 100644 index ccf0b4f..0000000 --- a/modules/00-globals/cloudflare/outputs.tf +++ /dev/null @@ -1,20 +0,0 @@ -output "cloudflare_account_id" { - description = "Cloudflare account ID" - value = var.cloudflare_account_id -} - -output "cloudflare_zone_id" { - description = "Cloudflare zone ID" - value = var.cloudflare_zone_id -} - -output "domain" { - description = "Base domain name" - value = var.domain -} - -output "cloudflare_api_token" { - description = "API token for Cloudflare" - value = var.cloudflare_api_token - sensitive = true -} diff --git a/modules/00-globals/cloudflare/variables.tf b/modules/00-globals/cloudflare/variables.tf deleted file mode 100644 index cc9d7e2..0000000 --- a/modules/00-globals/cloudflare/variables.tf +++ /dev/null @@ -1,20 +0,0 @@ -variable "cloudflare_api_token" { - description = "API token for Cloudflare with tunnel, DNS, and zone management permissions" - type = string - sensitive = true -} - -variable "cloudflare_account_id" { - description = "Cloudflare account ID" - type = string -} - -variable "cloudflare_zone_id" { - description = "Cloudflare zone ID for your domain" - type = string -} - -variable "domain" { - description = "Base domain name (e.g., example.com)" - type = string -} \ No newline at end of file diff --git a/modules/00-globals/system/main.tf b/modules/00-globals/system/main.tf new file mode 100644 index 0000000..ae9ddee --- /dev/null +++ b/modules/00-globals/system/main.tf @@ -0,0 +1,30 @@ +terraform { + required_providers { + dotenv = { + source = "germanbrew/dotenv" + } + } +} + +data "dotenv" "system_config" {} + +// Outputs +output "timezone" { + description = "System timezone" + value = data.dotenv.system_config.entries.TIMEZONE +} + +output "data_dir" { + description = "Base directory for data volumes" + value = data.dotenv.system_config.entries.DATA_DIR +} + +output "puid" { + description = "PUID for Docker containers" + value = data.dotenv.system_config.entries.PUID +} + +output "pgid" { + description = "PGID for Docker containers" + value = data.dotenv.system_config.entries.PGID +} diff --git a/modules/01-networking/cloudflared-tunnel/main.tf b/modules/01-networking/cloudflared-tunnel/main.tf index be48442..75c5bc3 100644 --- a/modules/01-networking/cloudflared-tunnel/main.tf +++ b/modules/01-networking/cloudflared-tunnel/main.tf @@ -4,12 +4,10 @@ terraform { required_providers { cloudflare = { - source = "cloudflare/cloudflare" - version = "~> 4.0" + source = "cloudflare/cloudflare" } random = { - source = "hashicorp/random" - version = "~> 3.5.1" + source = "hashicorp/random" } } } @@ -27,15 +25,31 @@ resource "cloudflare_zero_trust_tunnel_cloudflared" "this" { secret = var.tunnel_secret != "" ? var.tunnel_secret : random_id.tunnel_secret[0].b64_std } -locals { - all_ingress_rules = [for rule in var.ingress_rules : rule if rule != null] +locals { + // Transform service definitions into ingress rules format, only for services with ingress_enabled + service_ingress_rules = flatten([ + for service in var.service_definitions : + // Only process services with hostnames AND where ingress is enabled (or default to true for backward compatibility) + (length(service.hostnames) > 0) ? [ + for hostname in service.hostnames : { + hostname = "${hostname}.${var.domain}" + service = service.endpoint + } + ] : [] + ]) + + // Combine manual ingress rules and service-generated ones + all_ingress_rules = concat( + [for rule in var.ingress_rules : rule if rule != null], + local.service_ingress_rules + ) } // Configure tunnel routing resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" { account_id = var.cloudflare_account_id tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.this.id - + config { // Add all service ingress rules dynamic "ingress_rule" { @@ -45,7 +59,7 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" { service = ingress_rule.value.service } } - + // Default catch-all rule (required) ingress_rule { service = "http_status:404" @@ -55,8 +69,11 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" { // Create DNS record for each service resource "cloudflare_record" "service" { - for_each = { for rule in var.ingress_rules : rule.hostname => rule } - + for_each = { + for rule in local.all_ingress_rules : rule.hostname => rule + if rule.hostname != null && rule.hostname != "" + } + zone_id = var.cloudflare_zone_id name = split(".", each.value.hostname)[0] // Extract subdomain content = "${cloudflare_zero_trust_tunnel_cloudflared.this.id}.cfargotunnel.com" @@ -76,20 +93,20 @@ module "cloudflared" { container_name = var.container_name image = "cloudflare/cloudflared" tag = local.image_tag - + // Environment variables with tunnel token - env_vars = { + env_vars = { TUNNEL_TOKEN = cloudflare_zero_trust_tunnel_cloudflared.this.tunnel_token } - + // Command to run tunnel - command = ["tunnel", "--no-autoupdate", "run"] - + command = ["tunnel", "--no-autoupdate", "run"] + // Restart policy restart_policy = "unless-stopped" - - // Enable monitoring for the container via Watchtower if specified - monitoring = var.monitoring - networks = var.networks + // Enable monitoring for the container via Watchtower if specified + monitoring = var.monitoring + + networks = var.networks } diff --git a/modules/01-networking/cloudflared-tunnel/variables.tf b/modules/01-networking/cloudflared-tunnel/variables.tf index c2a3b35..7f392a9 100644 --- a/modules/01-networking/cloudflared-tunnel/variables.tf +++ b/modules/01-networking/cloudflared-tunnel/variables.tf @@ -1,5 +1,4 @@ // Variables for the Cloudflare tunnel module - variable "cloudflare_account_id" { description = "Cloudflare account ID" type = string @@ -10,6 +9,11 @@ variable "cloudflare_zone_id" { type = string } +variable "domain" { + description = "The domain name to use for creating DNS records" + type = string +} + variable "container_name" { description = "Name of the Cloudflare tunnel container" type = string @@ -35,7 +39,7 @@ variable "tunnel_secret" { } variable "ingress_rules" { - description = "List of ingress rules for services to be exposed through the tunnel" + description = "List of ingress rules to configure manually" type = list(object({ hostname = string service = string @@ -43,6 +47,17 @@ variable "ingress_rules" { default = [] } +variable "service_definitions" { + description = "List of service definitions containing name, endpoints and hostname configuration" + type = list(object({ + name = string + primary_port = number + endpoint = string + hostnames = optional(list(string), []) + })) + default = [] +} + variable "monitoring" { description = "Enable monitoring via Watchtower" type = bool diff --git a/modules/01-networking/docker-network/main.tf b/modules/01-networking/docker-network/main.tf index 00a7a93..6609058 100644 --- a/modules/01-networking/docker-network/main.tf +++ b/modules/01-networking/docker-network/main.tf @@ -1,31 +1,27 @@ -// Docker Network Module -// This module creates a Docker network for container communication - terraform { required_providers { docker = { - source = "kreuzwerker/docker" - version = "~> 3.6.0" + source = "kreuzwerker/docker" } } } resource "docker_network" "this" { - name = var.name - driver = var.driver - internal = var.internal - attachable = var.attachable - ipam_driver = var.ipam_driver - + name = var.name + driver = var.driver + internal = var.internal + attachable = var.attachable + ipam_driver = var.ipam_driver + dynamic "ipam_config" { for_each = var.subnet != "" ? [1] : [] content { - subnet = var.subnet - gateway = var.gateway - ip_range = var.ip_range - aux_address = var.aux_address + subnet = var.subnet + gateway = var.gateway + ip_range = var.ip_range + aux_address = var.aux_address } } - - options = var.options + + options = var.options } diff --git a/modules/01-networking/docker-network/outputs.tf b/modules/01-networking/docker-network/outputs.tf index 4f1b10f..2325b2f 100644 --- a/modules/01-networking/docker-network/outputs.tf +++ b/modules/01-networking/docker-network/outputs.tf @@ -1,5 +1,4 @@ // Outputs for Docker Network module - output "network_id" { description = "The ID of the Docker network" value = docker_network.this.id diff --git a/modules/10-services-generic/docker-service/README.md b/modules/10-services-generic/docker-service/README.md index f1547dc..75c433f 100644 --- a/modules/10-services-generic/docker-service/README.md +++ b/modules/10-services-generic/docker-service/README.md @@ -67,8 +67,7 @@ This module requires the Docker provider to be configured in your root module: terraform { required_providers { docker = { - source = "kreuzwerker/docker" - version = ">= 3.0.0" + source = "kreuzwerker/docker" } } } diff --git a/modules/10-services-generic/docker-service/main.tf b/modules/10-services-generic/docker-service/main.tf index 0fc3d14..eee1b0a 100644 --- a/modules/10-services-generic/docker-service/main.tf +++ b/modules/10-services-generic/docker-service/main.tf @@ -1,20 +1,33 @@ // Generic Docker service module // Creates and manages a Docker container with configurable options +module "system_globals" { + source = "../../00-globals/system" +} terraform { required_providers { docker = { source = "kreuzwerker/docker" - version = ">= 3.0.0" + } + dotenv = { + source = "germanbrew/dotenv" } } } locals { - network_mode = var.network_mode + network_mode = var.network_mode container_name = var.container_name - image_name = "${var.image}:${var.tag}" - + image_name = "${var.image}:${var.tag}" + + default_env_vars = { + TZ = module.system_globals.timezone + PUID = module.system_globals.puid + PGID = module.system_globals.pgid + } + + env_vars = merge(var.env_vars, local.default_env_vars) + // Prepare ports configuration ports_config = [ for port in var.ports : { @@ -23,7 +36,7 @@ locals { protocol = port.protocol } ] - + // Prepare volumes configuration volumes_config = [ for volume in var.volumes : { @@ -32,20 +45,20 @@ locals { read_only = volume.read_only } ] - + // Define monitoring labels if enabled monitoring_labels = var.monitoring ? { "com.centurylinklabs.watchtower.enable" = "true" } : {} - + // Merge provided labels with monitoring labels merged_labels = merge(var.labels, local.monitoring_labels) } // Pull the Docker image resource "docker_image" "service_image" { - name = local.image_name - keep_locally = var.keep_image_locally + name = local.image_name + keep_locally = var.keep_image_locally pull_triggers = [var.tag] } @@ -53,12 +66,12 @@ resource "docker_image" "service_image" { resource "docker_container" "service_container" { name = local.container_name image = docker_image.service_image.image_id - + restart = var.restart_policy - + # Set the network mode (bridge, host, etc.) network_mode = local.network_mode - + # Dynamically configure ports based on the provided list dynamic "ports" { for_each = local.ports_config @@ -68,7 +81,7 @@ resource "docker_container" "service_container" { protocol = ports.value.protocol } } - + # Dynamically configure networks based on the provided list dynamic "networks_advanced" { for_each = var.networks @@ -76,7 +89,7 @@ resource "docker_container" "service_container" { name = networks_advanced.value } } - + # Dynamically configure volumes based on the provided list dynamic "volumes" { for_each = local.volumes_config @@ -86,10 +99,10 @@ resource "docker_container" "service_container" { read_only = volumes.value.read_only } } - + # Configure environment variables - map to array of strings - env = [for k, v in var.env_vars : "${k}=${v}"] - + env = [for k, v in local.env_vars : "${k}=${v}"] + # Set container labels dynamic "labels" { for_each = local.merged_labels @@ -98,7 +111,7 @@ resource "docker_container" "service_container" { value = labels.value } } - + # Add container healthcheck if configured dynamic "healthcheck" { for_each = var.healthcheck != null ? [var.healthcheck] : [] @@ -110,23 +123,23 @@ resource "docker_container" "service_container" { retries = healthcheck.value.retries } } - + # Set resource limits if specified - memory = var.memory_limit - memory_swap = var.memory_swap_limit - cpu_shares = var.cpu_shares - + memory = var.memory_limit + memory_swap = var.memory_swap_limit + cpu_shares = var.cpu_shares + # Other container options - dns = var.dns - dns_search = var.dns_search - hostname = var.hostname - domainname = var.domainname - user = var.user - working_dir = var.working_dir - command = var.command - entrypoint = var.entrypoint - privileged = var.privileged - + dns = var.dns + dns_search = var.dns_search + hostname = var.hostname + domainname = var.domainname + user = var.user + working_dir = var.working_dir + command = var.command + entrypoint = var.entrypoint + privileged = var.privileged + # Set log options log_driver = var.log_driver log_opts = var.log_opts diff --git a/modules/10-services-generic/docker-service/variables.tf b/modules/10-services-generic/docker-service/variables.tf index e5b8f86..acfbe4b 100644 --- a/modules/10-services-generic/docker-service/variables.tf +++ b/modules/10-services-generic/docker-service/variables.tf @@ -23,7 +23,7 @@ variable "keep_image_locally" { variable "restart_policy" { description = "Docker restart policy (no, always, unless-stopped, on-failure)" type = string - default = "unless-stopped" + default = "always" } variable "network_mode" { diff --git a/modules/20-services-apps/actualbudget/main.tf b/modules/20-services-apps/actualbudget/main.tf index 5798774..984e80c 100644 --- a/modules/20-services-apps/actualbudget/main.tf +++ b/modules/20-services-apps/actualbudget/main.tf @@ -1,49 +1,51 @@ -// ActualBudget module for budgeting -// This module configures an ActualBudget container with the specified volumes +variable "image_tag" { + description = "Tag of the ActualBudget image to use" + type = string + default = "latest" +} + +variable "volume_path" { + description = "Host path for ActualBudget data volume" + type = string +} + +variable "networks" { + description = "List of networks to which the container should be attached" + type = list(string) +} locals { - container_name = var.container_name != "" ? var.container_name : "actualbudget" + container_name = "actualbudget" + image = "actualbudget/actual-server" image_tag = var.image_tag != "" ? var.image_tag : "latest" - - default_env_vars = { - TZ = var.timezone - PUID = var.puid - PGID = var.pgid - } - + monitoring = true + exposed_port = 5006 + hostnames = ["budget"] default_volumes = [ { container_path = "/data" - host_path = var.data_volume_path + host_path = "${var.volume_path}/data" read_only = false } ] } module "actualbudget" { - source = "../../10-services-generic/docker-service" - - container_name = var.container_name - image = "actualbudget/actual-server" - tag = var.image_tag - - // Environment variables - env_vars = local.default_env_vars - - // Port mapping - ports = [ - { - internal = 5006 - external = var.port - protocol = "tcp" - } - ] - - // Volume mapping - volumes = local.default_volumes - - // Enable monitoring for the container via Watchtower - monitoring = var.monitoring - - networks = var.networks + source = "../../10-services-generic/docker-service" + container_name = local.container_name + image = local.image + tag = local.image_tag + volumes = local.default_volumes + networks = var.networks + monitoring = local.monitoring +} + +output "service_definition" { + description = "General service definition with optional ingress configuration" + value = { + name = local.container_name + primary_port = local.exposed_port + endpoint = "http://${local.container_name}:${local.exposed_port}" + hostnames = local.hostnames + } } diff --git a/modules/20-services-apps/actualbudget/outputs.tf b/modules/20-services-apps/actualbudget/outputs.tf deleted file mode 100644 index c9c03a2..0000000 --- a/modules/20-services-apps/actualbudget/outputs.tf +++ /dev/null @@ -1,24 +0,0 @@ -output "container_name" { - description = "The name of the ActualBudget container" - value = module.actualbudget.container_name -} - -output "container_id" { - description = "The ID of the ActualBudget container" - value = module.actualbudget.container_id -} - -output "image_id" { - description = "The ID of the ActualBudget image" - value = module.actualbudget.image_id -} - -output "ip_address" { - description = "The IP address of the ActualBudget container" - value = module.actualbudget.ip_address -} - -output "local_url" { - description = "The local URL to access the ActualBudget interface" - value = "http://localhost:${var.port}" -} diff --git a/modules/20-services-apps/actualbudget/variables.tf b/modules/20-services-apps/actualbudget/variables.tf deleted file mode 100644 index 517796e..0000000 --- a/modules/20-services-apps/actualbudget/variables.tf +++ /dev/null @@ -1,52 +0,0 @@ -variable "container_name" { - description = "Name of the ActualBudget container" - type = string - default = "actualbudget" -} - -variable "timezone" { - description = "Timezone for the container" - type = string - default = "UTC" -} - -variable "image_tag" { - description = "Tag of the ActualBudget image to use" - type = string - default = "latest" -} - -variable "port" { - description = "External port for ActualBudget server" - type = number - default = 5006 -} - -variable "data_volume_path" { - description = "Host path for ActualBudget data volume" - type = string -} - -variable "puid" { - description = "User ID for the container" - type = number - default = 1000 -} - -variable "pgid" { - description = "Group ID for the container" - type = number - default = 1000 -} - -variable "monitoring" { - description = "Enable monitoring for the container via Watchtower" - type = bool - default = true -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} \ No newline at end of file diff --git a/modules/20-services-apps/emulatorjs/.env.example b/modules/20-services-apps/emulatorjs/.env.example new file mode 100644 index 0000000..52884d3 --- /dev/null +++ b/modules/20-services-apps/emulatorjs/.env.example @@ -0,0 +1,3 @@ +EMULATORJS_FRONTEND_PORT=5823 +EMULATORJS_CONFIG_PORT=5824 +EMULATORJS_BACKEND_PORT=5825 diff --git a/modules/20-services-apps/emulatorjs/main.tf b/modules/20-services-apps/emulatorjs/main.tf index a7d9b4a..71e564e 100644 --- a/modules/20-services-apps/emulatorjs/main.tf +++ b/modules/20-services-apps/emulatorjs/main.tf @@ -1,70 +1,78 @@ -// EmulatorJS module for retro game emulation -// This module configures an EmulatorJS container with the specified volumes +terraform { + required_providers { + dotenv = { + source = "germanbrew/dotenv" + } + } +} + +variable "image_tag" { + description = "The tag for the EmulatorJS container image" + type = string + default = "latest" +} + +variable "volume_path" { + description = "Base directory for volumes" + type = string +} locals { - container_name = var.container_name != "" ? var.container_name : "emulatorjs" + container_name = "emulatorjs" + image = "linuxserver/emulatorjs" image_tag = var.image_tag != "" ? var.image_tag : "latest" - - default_env_vars = { - TZ = var.timezone - } - - // Merge default env vars with any additional ones provided - env_vars = merge(local.default_env_vars, var.additional_env_vars) - - // Default volumes for EmulatorJS - default_volumes = [ + monitoring = true + env_file = "${path.module}/.env" + frontend_port = provider::dotenv::get_by_key("EMULATORJS_FRONTEND_PORT", local.env_file) + config_port = provider::dotenv::get_by_key("EMULATORJS_CONFIG_PORT", local.env_file) + backend_port = provider::dotenv::get_by_key("EMULATORJS_BACKEND_PORT", local.env_file) + ports = [ { - host_path = var.config_volume_path + internal = 3000 + external = local.config_port + protocol = "tcp" + }, + { + internal = 80 + external = local.frontend_port + protocol = "tcp" + }, + { + internal = 4001 + external = local.backend_port + protocol = "tcp" + } + ] + volumes = [ + { + host_path = "${var.volume_path}/config" container_path = "/config" read_only = false }, { - host_path = var.data_volume_path + host_path = "${var.volume_path}/data" container_path = "/data" read_only = false } ] - - // Merge default volumes with any additional ones provided - volumes = concat(local.default_volumes, var.additional_volumes) } -// Use the generic docker-service module to deploy EmulatorJS module "emulatorjs" { source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = "linuxserver/emulatorjs" - tag = local.image_tag - - restart_policy = var.restart_policy - network_mode = "bridge" - - env_vars = local.env_vars - volumes = local.volumes - - labels = var.labels - - // Default ports for EmulatorJS - ports = [ - { - internal = 3000 - external = var.config_port - protocol = "tcp" - }, - { - internal = 80 - external = var.frontend_port - protocol = "tcp" - }, - { - internal = 4001 - external = var.backend_port - protocol = "tcp" - } - ] - - // Enable monitoring for the container via Watchtower - monitoring = var.monitoring + container_name = local.container_name + image = local.image + tag = local.image_tag + volumes = local.volumes + ports = local.ports + monitoring = local.monitoring +} + +output "service_definition" { + description = "General service definition with optional ingress configuration" + value = { + name = module.emulatorjs.container_name + primary_port = local.frontend_port + endpoint = "http://${module.emulatorjs.container_name}:${local.frontend_port}" + } } diff --git a/modules/20-services-apps/emulatorjs/outputs.tf b/modules/20-services-apps/emulatorjs/outputs.tf deleted file mode 100644 index aed0377..0000000 --- a/modules/20-services-apps/emulatorjs/outputs.tf +++ /dev/null @@ -1,26 +0,0 @@ -// Outputs for the EmulatorJS module - -output "container_name" { - description = "Name of the created EmulatorJS container" - value = module.emulatorjs.container_name -} - -output "container_id" { - description = "ID of the created EmulatorJS container" - value = module.emulatorjs.container_id -} - -output "image_id" { - description = "ID of the EmulatorJS image used" - value = module.emulatorjs.image_id -} - -output "frontend_url" { - description = "URL to access the EmulatorJS frontend interface" - value = "http://localhost:${var.frontend_port}" -} - -output "config_url" { - description = "URL to access the EmulatorJS web configuration interface" - value = "http://localhost:${var.config_port}" -} diff --git a/modules/20-services-apps/emulatorjs/variables.tf b/modules/20-services-apps/emulatorjs/variables.tf deleted file mode 100644 index 982aa31..0000000 --- a/modules/20-services-apps/emulatorjs/variables.tf +++ /dev/null @@ -1,91 +0,0 @@ -variable "container_name" { - description = "Name for the EmulatorJS container" - type = string - default = "emulatorjs" -} - -variable "image_tag" { - description = "The tag for the EmulatorJS container image" - type = string - default = "latest" -} - -variable "restart_policy" { - description = "Restart policy for the container" - type = string - default = "unless-stopped" -} - -variable "timezone" { - description = "Timezone for the container" - type = string - default = "Etc/UTC" -} - -variable "puid" { - description = "User ID the container will run as" - type = number - default = 1000 -} - -variable "pgid" { - description = "Group ID the container will run as" - type = number - default = 1000 -} - -variable "config_volume_path" { - description = "Host path for the EmulatorJS config directory" - type = string -} - -variable "data_volume_path" { - description = "Host path for the EmulatorJS data directory" - type = string -} - -variable "frontend_port" { - description = "External port for the EmulatorJS frontend" - type = number - default = 3000 -} - -variable "config_port" { - description = "External port for the EmulatorJS configuration interface" - type = number - default = 8080 -} - -variable "backend_port" { - description = "External port for the EmulatorJS backend" - type = number - default = 4001 -} - -variable "additional_env_vars" { - description = "Additional environment variables for EmulatorJS" - type = map(string) - default = {} -} - -variable "additional_volumes" { - description = "Additional volumes to mount in the container" - type = list(object({ - host_path = string - container_path = string - read_only = bool - })) - default = [] -} - -variable "labels" { - description = "Labels to set on the container" - type = map(string) - default = {} -} - -variable "monitoring" { - description = "Enable monitoring for the container via Watchtower" - type = bool - default = true -} diff --git a/modules/20-services-apps/watchtower/.env.example b/modules/20-services-apps/watchtower/.env.example new file mode 100644 index 0000000..45c8e43 --- /dev/null +++ b/modules/20-services-apps/watchtower/.env.example @@ -0,0 +1,7 @@ +WATCHTOWER_CLEANUP=true +WATCHTOWER_POLL_INTERVAL=86400 +WATCHTOWER_INCLUDE_STOPPED=false +WATCHTOWER_REVIVE_STOPPED=false +WATCHTOWER_ROLLING_RESTART=true +WATCHTOWER_NOTIFICATIONS=shoutrrr +WATCHTOWER_NOTIFICATION_URL=discord://token@webhook_id \ No newline at end of file diff --git a/modules/20-services-apps/watchtower/main.tf b/modules/20-services-apps/watchtower/main.tf index f884b5b..21b0c73 100644 --- a/modules/20-services-apps/watchtower/main.tf +++ b/modules/20-services-apps/watchtower/main.tf @@ -1,58 +1,48 @@ -// Watchtower module for automatic Docker container updates -// This module configures a Watchtower container that monitors and updates other containers +terraform { + required_providers { + dotenv = { + source = "germanbrew/dotenv" + } + } +} + +variable "image_tag" { + description = "The tag for the Watchtower container image" + type = string + default = "latest" +} locals { - container_name = var.container_name != "" ? var.container_name : "watchtower" + container_name = "watchtower" + image = "containrrr/watchtower" image_tag = var.image_tag != "" ? var.image_tag : "latest" - - default_env_vars = { - TZ = var.timezone - WATCHTOWER_CLEANUP = var.cleanup - WATCHTOWER_POLL_INTERVAL = var.poll_interval - WATCHTOWER_INCLUDE_STOPPED = var.include_stopped - WATCHTOWER_REVIVE_STOPPED = var.revive_stopped - WATCHTOWER_ROLLING_RESTART = var.rolling_restart - WATCHTOWER_NOTIFICATION_URL = var.notification_url - WATCHTOWER_NOTIFICATIONS = var.enable_notifications ? "shoutrrr" : "" + env_file = "${path.module}/.env" + + env_vars = { + WATCHTOWER_CLEANUP = provider::dotenv::get_by_key("WATCHTOWER_CLEANUP", local.env_file) + WATCHTOWER_POLL_INTERVAL = provider::dotenv::get_by_key("WATCHTOWER_POLL_INTERVAL", local.env_file) + WATCHTOWER_INCLUDE_STOPPED = false + WATCHTOWER_REVIVE_STOPPED = false + WATCHTOWER_ROLLING_RESTART = true + WATCHTOWER_NOTIFICATION_URL = provider::dotenv::get_by_key("WATCHTOWER_NOTIFICATION_URL", local.env_file) + WATCHTOWER_NOTIFICATIONS = provider::dotenv::get_by_key("WATCHTOWER_NOTIFICATIONS", local.env_file) } - - // Merge default env vars with any additional ones provided - env_vars = merge(local.default_env_vars, var.additional_env_vars) - - // Default volumes for Docker socket access - default_volumes = [ + + volumes = [ { host_path = "/var/run/docker.sock" container_path = "/var/run/docker.sock" read_only = true } ] - - // Merge default volumes with any additional ones provided - volumes = concat(local.default_volumes, var.additional_volumes) } -// Use the generic docker-service module to deploy Watchtower module "watchtower" { - source = "../../10-services-generic/docker-service" - - container_name = local.container_name - image = "containrrr/watchtower" - tag = local.image_tag - - restart_policy = var.restart_policy - network_mode = "bridge" - - env_vars = local.env_vars - volumes = local.volumes - - labels = var.labels - - // Watchtower doesn't typically expose ports but we'll include the option - ports = var.ports - - // Add monitoring label if enabled - monitoring = var.monitoring - - depends_on = [] + source = "../../10-services-generic/docker-service" + container_name = local.container_name + image = local.image + tag = local.image_tag + env_vars = local.env_vars + volumes = local.volumes } + diff --git a/modules/20-services-apps/watchtower/outputs.tf b/modules/20-services-apps/watchtower/outputs.tf deleted file mode 100644 index bd424d2..0000000 --- a/modules/20-services-apps/watchtower/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "container_name" { - description = "Name of the created Watchtower container" - value = module.watchtower.container_name -} - -output "container_id" { - description = "ID of the created Watchtower container" - value = module.watchtower.container_id -} - -output "image_id" { - description = "ID of the Watchtower image used" - value = module.watchtower.image_id -} diff --git a/modules/20-services-apps/watchtower/variables.tf b/modules/20-services-apps/watchtower/variables.tf deleted file mode 100644 index f867e4a..0000000 --- a/modules/20-services-apps/watchtower/variables.tf +++ /dev/null @@ -1,103 +0,0 @@ -variable "container_name" { - description = "Name for the Watchtower container" - type = string - default = "watchtower" -} - -variable "image_tag" { - description = "The tag for the Watchtower container image" - type = string - default = "latest" -} - -variable "restart_policy" { - description = "Restart policy for the container" - type = string - default = "unless-stopped" -} - -variable "timezone" { - description = "Timezone for the container" - type = string - default = "Etc/UTC" -} - -variable "cleanup" { - description = "Remove old images after updating" - type = bool - default = true -} - -variable "poll_interval" { - description = "Poll interval (in seconds) for checking for updates" - type = number - default = 86400 // Default: check once per day -} - -variable "include_stopped" { - description = "Include stopped containers when checking for updates" - type = bool - default = false -} - -variable "revive_stopped" { - description = "Restart stopped containers after updating" - type = bool - default = false -} - -variable "rolling_restart" { - description = "Restart containers one by one instead of all at once" - type = bool - default = true -} - -variable "notification_url" { - description = "URL for sending update notifications via shoutrrr" - type = string - default = "" -} - -variable "enable_notifications" { - description = "Enable shoutrrr notifications" - type = bool - default = false -} - -variable "additional_env_vars" { - description = "Additional environment variables for Watchtower" - type = map(string) - default = {} -} - -variable "additional_volumes" { - description = "Additional volumes to mount in the container" - type = list(object({ - host_path = string - container_path = string - read_only = bool - })) - default = [] -} - -variable "labels" { - description = "Labels to set on the container" - type = map(string) - default = {} -} - -variable "ports" { - description = "Ports to expose (Watchtower typically doesn't need ports exposed)" - type = list(object({ - internal = number - external = number - protocol = string - })) - default = [] -} - -variable "monitoring" { - description = "Enable monitoring for the container" - type = bool - default = true -} diff --git a/outputs.tf b/outputs.tf index b94ee24..409b83f 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,33 +1,9 @@ -// Root outputs that expose important information from each environment - -// Network environment outputs -output "cloudflare_domain" { - description = "Base domain for the homelab" - value = module.network.domain -} - -output "homelab_cloudflared_tunnel_name" { - description = "Name of the Cloudflare tunnel" - value = module.network.homelab_cloudflared_tunnel_name -} - -output "homelab_cloudflared_tunnel_cname_target" { - description = "CNAME target for the Cloudflare tunnel" - value = module.network.homelab_cloudflared_tunnel_cname_target -} - -// Service URLs -output "actualbudget_local_url" { - description = "Local URL for accessing ActualBudget" - value = module.services.actualbudget_local_url -} - -output "emulatorjs_frontend_url" { - description = "URL for the EmulatorJS frontend" - value = module.services.emulatorjs_frontend_url -} - -output "emulatorjs_config_url" { - description = "URL for the EmulatorJS configuration" - value = module.services.emulatorjs_config_url +output "services" { + description = "Service definitions for all services" + value = [ + for service in module.services.service_definitions : { + name = service.name + endpoint = contains(keys(service), "hostnames") ? "${service.hostnames[0]}.${module.cloudflare_globals.domain}" : service.endpoint + } + ] } diff --git a/providers.tf b/providers.tf index 419c0d2..70c4e7d 100644 --- a/providers.tf +++ b/providers.tf @@ -1,20 +1,24 @@ terraform { - required_providers { - docker = { - source = "kreuzwerker/docker" - version = "~> 3.6.0" - } - cloudflare = { - source = "cloudflare/cloudflare" - version = "~> 4.0" - } - random = { - source = "hashicorp/random" - version = "~> 3.5.1" - } + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~> 3.6.0" } + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 4.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5.1" + } + dotenv = { + source = "germanbrew/dotenv" + version = "1.2.5" + } + } } provider "cloudflare" { - api_token = var.cloudflare_api_token + api_token = module.cloudflare_globals.cloudflare_api_token } diff --git a/services/main.tf b/services/main.tf new file mode 100644 index 0000000..83ce882 --- /dev/null +++ b/services/main.tf @@ -0,0 +1,30 @@ +locals { + module_dir = "../modules" + data_dir = module.system_globals.data_dir +} + +module "system_globals" { + source = "${local.module_dir}/00-globals/system" +} + +// Docker network used for modules that needs to be exposed to the internet +// using Cloudflared +module "homelab_docker_network" { + source = "${local.module_dir}/01-networking/docker-network" + + name = "homelab-network" + driver = "bridge" + attachable = true + subnet = "10.100.0.0/16" +} + +module "actualbudget" { + source = "${local.module_dir}/20-services-apps/actualbudget" + volume_path = "${local.data_dir}/actual" + networks = [module.homelab_docker_network.name] +} + +module "emulatorjs" { + source = "${local.module_dir}/20-services-apps/emulatorjs" + volume_path = "${local.data_dir}/emulatorjs" +} diff --git a/services/outputs.tf b/services/outputs.tf new file mode 100644 index 0000000..5673d3a --- /dev/null +++ b/services/outputs.tf @@ -0,0 +1,15 @@ +// Services environment outputs + +// Consolidated service definitions for networking +output "service_definitions" { + description = "Service definitions for all services" + value = [ + module.actualbudget.service_definition, + module.emulatorjs.service_definition + ] +} + +output "homelab_docker_network_name" { + description = "The name of the Docker network" + value = module.homelab_docker_network.name +} diff --git a/terraform.tfvars.example b/terraform.tfvars.example deleted file mode 100644 index 79849a4..0000000 --- a/terraform.tfvars.example +++ /dev/null @@ -1,26 +0,0 @@ -# Example terraform.tfvars file -# Copy to terraform.tfvars and fill with your values - -# Generic -timezone = "Australia/Sydney" -puid = 1000 -pgid = 1000 -data_dir = "/srv/docker_data" - -# Watchtower -watchtower_enable_notifications = false -# watchtower_notification_url = "discord://token@webhookId" - -# EmulatorJS -emulatorjs_frontend_port = 5823 -emulatorjs_config_port = 5824 -emulatorjs_backend_port = 5825 - -# ActualBudget -actualbudget_port = 5006 - -# Cloudflare -cloudflare_api_token = "your-cloudflare-api-token" # API token with required permissions -cloudflare_account_id = "your-cloudflare-account-id" # Found in Cloudflare dashboard URL -cloudflare_zone_id = "your-cloudflare-zone-id" # Found in the domain overview page -domain = "yourdomain.com" # Your domain on Cloudflare diff --git a/variables.tf b/variables.tf deleted file mode 100644 index 558b815..0000000 --- a/variables.tf +++ /dev/null @@ -1,79 +0,0 @@ -// Generic -variable "timezone" { - description = "Timezone for the system" - type = string -} - -variable "puid" { - description = "User ID for the container" - type = number -} - -variable "pgid" { - description = "Group ID for the container" - type = number -} - -variable "data_dir" { - description = "Base directory for data volumes" - type = string -} - -// Watchtower -variable "watchtower_enable_notifications" { - description = "Enable Watchtower update notifications" - type = bool - default = false -} - -variable "watchtower_notification_url" { - description = "Webhook URL for Watchtower notifications (Discord, Slack, etc.)" - type = string - sensitive = true // This flags the variable as sensitive in logs and outputs - default = "" -} - -// EmulatorJS -variable "emulatorjs_frontend_port" { - description = "External port for the EmulatorJS frontend" - type = number -} - -variable "emulatorjs_config_port" { - description = "External port for the EmulatorJS configuration interface" - type = number -} - -variable "emulatorjs_backend_port" { - description = "External port for the EmulatorJS backend" - type = number -} - -// ActualBudget -variable "actualbudget_port" { - description = "External port for the ActualBudget server" - type = number -} - -// Cloudflare -variable "cloudflare_api_token" { - description = "API token for Cloudflare with tunnel, DNS, and zone management permissions" - type = string - sensitive = true -} - -variable "cloudflare_account_id" { - description = "Cloudflare account ID" - type = string -} - -variable "cloudflare_zone_id" { - description = "Cloudflare zone ID for your domain" - type = string -} - -variable "domain" { - description = "Base domain name (e.g., example.com)" - type = string -} - diff --git a/versions.tf b/versions.tf deleted file mode 100644 index e69de29..0000000