diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml new file mode 100644 index 0000000..659d143 --- /dev/null +++ b/.github/workflows/terraform-validate.yml @@ -0,0 +1,59 @@ +name: Terraform Module Validation + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + validate_each_module: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + module_dir: + - "./authentik" + - "./dns" + - "./docker" + - "./docker-network" + - "./docker-service" + - "./docker-stack" + - "./nginx_config" + - "./oauth_auth" + - "./proxy_auth" + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "latest" + + - name: Validate Module ${{ matrix.module_dir }} + shell: bash + run: | + module_dir="${{ matrix.module_dir }}" + echo "--- Validating module: $module_dir ---" + + pushd "$module_dir" > /dev/null || { echo "Failed to change directory to $module_dir"; exit 1; } + + echo "Running terraform init in $module_dir..." + if ! terraform init -backend=false -input=false -upgrade; then + echo "::error file=$module_dir::Terraform init failed for module: $module_dir" + exit 1 + else + echo "Running terraform validate in $module_dir..." + if ! terraform validate; then + echo "::error file=$module_dir::Terraform validate failed for module: $module_dir" + exit 1 + fi + fi + + popd > /dev/null || { echo "Failed to return from directory"; exit 1; } + echo "Module $module_dir validated successfully!" diff --git a/ReadMe.md b/ReadMe.md index cd3c2af..aa08c50 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1 +1,10 @@ -Just working on breaking these modules out for now. Will update the documentation later. \ No newline at end of file +Just working on breaking these modules out for now. Will update the documentation later. + +Just a quick note. When using any of the following modules: + +- docker +- docker-service +- docker-network +- docker-stack + +The provider configuration has to be set by the caller. As outlined in the [legacy module documentation](https://developer.hashicorp.com/terraform/language/modules/develop/providers#legacy-shared-modules-with-provider-configurations) \ No newline at end of file diff --git a/docker-service/auth.tf b/docker-service/auth.tf index a7398dc..7e0763c 100644 --- a/docker-service/auth.tf +++ b/docker-service/auth.tf @@ -1,5 +1,5 @@ module "proxy_authentication" { - source = "git@github.com:DCCoder90/home-tf-modules.git//proxy_auth?ref=1.0.0" + source = "../proxy_auth" count = var.service.auth.enabled && var.service.auth.proxy.enabled ? 1 : 0 @@ -24,7 +24,7 @@ module "proxy_authentication" { } module "oauth_authentication" { - source = "git@github.com:DCCoder90/home-tf-modules.git//oauth_auth?ref=1.0.0" + source = "../oauth_auth" count = var.service.auth.enabled && var.service.auth.oauth.enabled ? 1 : 0 @@ -32,8 +32,8 @@ module "oauth_authentication" { description = var.service.description name = var.service.service_name create_access_group = true - access_group_name = "tf_${var.service.service_name}" - user_to_add_to_access_group = var.system.network_admin_username + access_group_name = "tf_${var.service.service_name}" + user_to_add_to_access_group = var.system.network_admin_username allowed_redirect_uris = concat( [ { @@ -43,7 +43,7 @@ module "oauth_authentication" { [ for uri_path in coalesce(var.service.auth.oauth.redirect_uris, []) : { matching_mode = "strict", - url = "https://${var.service.dns.domain_name}/${uri_path}" + url = "${uri_path}" } ] ) diff --git a/docker-service/dns.tf b/docker-service/dns.tf index 3c757ac..d7045e5 100644 --- a/docker-service/dns.tf +++ b/docker-service/dns.tf @@ -1,7 +1,7 @@ data "nginxproxymanager_access_lists" "access_lists" {} module "service_dns" { - source = "git@github.com:DCCoder90/home-tf-modules.git//dns?ref=1.0.0" + source = "../dns" count = var.service.dns.enabled ? 1 : 0 diff --git a/docker-service/locals.tf b/docker-service/locals.tf index d822d2b..ce8e197 100644 --- a/docker-service/locals.tf +++ b/docker-service/locals.tf @@ -29,8 +29,8 @@ locals { # Create a map of Nginx Proxy Manager access lists by name for easy lookup. npm_access_lists_by_name = { for al in data.nginxproxymanager_access_lists.access_lists.access_lists : al.name => al.id } - domain = var.service.dns.domain_name == null ? "test.example" : var.service.dns.domain_name + domain = var.service.dns.domain_name == null ? "test.example" : var.service.dns.domain_name domain_name_parts = split(".", local.domain) - zone_name = join(".", slice(local.domain_name_parts, length(local.domain_name_parts) - 2, length(local.domain_name_parts))) + zone_name = join(".", slice(local.domain_name_parts, length(local.domain_name_parts) - 2, length(local.domain_name_parts))) } diff --git a/docker-service/main.tf b/docker-service/main.tf index 85ac691..f52ffd8 100644 --- a/docker-service/main.tf +++ b/docker-service/main.tf @@ -1,5 +1,5 @@ module "service_container" { - source = "git@github.com:DCCoder90/home-tf-modules.git//docker?ref=1.0.0" + source = "../docker" icon = var.service.icon web_ui = try(var.service.network.service_port, null) != null && local.service_ip_address != null ? "http://${local.service_ip_address}:${var.service.network.service_port}" : null diff --git a/docker-service/secrets.tf b/docker-service/secrets.tf index 50abd63..c969a6f 100644 --- a/docker-service/secrets.tf +++ b/docker-service/secrets.tf @@ -7,14 +7,14 @@ data "infisical_projects" "home-net" { data "infisical_secrets" "secrets" { # Only fetch secrets if the stack configuration requests them. count = (var.service.secrets != null && length(var.service.secrets) > 0) || (try(var.service.auth.proxy.enabled, false)) ? 1 : 0 - + env_slug = var.system.infisical.environment workspace_id = data.infisical_projects.home-net.id # This path corresponds to where the root `secrets` module stores secrets. folder_path = var.system.infisical.folder } -locals{ +locals { # Create a list of environment variables from the secrets map. secret_envs = (var.service.secrets != null && length(var.service.secrets) > 0) ? [ for key, value in var.service.secrets : diff --git a/docker-service/variables.tf b/docker-service/variables.tf index 748803b..9aa8c76 100644 --- a/docker-service/variables.tf +++ b/docker-service/variables.tf @@ -20,11 +20,12 @@ variable "service" { volumes = optional(list(string)) # --- Environment & Secrets --- - env = optional(list(string)) - secrets = optional(map(string)) + env = optional(list(string)) + secrets = optional(map(string)) + host = optional(string, "tower") # --- Networking & DNS --- - network = optional(object({ + network = optional(object({ internal = optional(bool, false) service_port = optional(number) networks = optional(list(object({ diff --git a/docker-stack/main.tf b/docker-stack/main.tf index 88ac8ff..9f81fbb 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -5,37 +5,20 @@ data "infisical_projects" "home-net" { } module "custom_network" { - count = length(local.creatable_networks) > 0 ? 1 : 0 - source = "git@github.com:DCCoder90/home-tf-modules.git//docker-network?ref=1.0.0" + count = length(local.creatable_networks) > 0 ? 1 : 0 + source = "../docker-network" networks = local.creatable_networks } module "service_container" { for_each = var.stack.services - source = "git@github.com:DCCoder90/home-tf-modules.git//docker-service?ref=1.0.0" + source = "../docker-service" service = each.value - system = var.system + system = var.system -/* + /* Still need to combine environment, mounts, and networks! - - - icon = each.value.icon - web_ui = try(each.value.network.service_port, null) != null && local.service_ip_addresses[each.key] != null ? "http://${local.service_ip_addresses[each.key]}:${each.value.network.service_port}" : null - container_name = each.value.service_name - container_image = each.value.image_name - container_network_mode = each.value.network_mode - enable_gpu = each.value.enable_gpu - environment_vars = toset(concat(coalesce(var.stack.env, []), local.processed_envs[each.key], coalesce(var.stack.env, []), coalesce(local.oauth_envs[each.key], []))) - mounts = concat(coalesce(var.stack.mounts, []), coalesce(each.value.mounts, [])) - container_capabilities = each.value.capabilities - commands = each.value.commands - - # Attach the container to custom networks defined in the stack, but only if the service - # explicitly lists that network in its own configuration. - # The docker module expects a list of objects with `name` and `ipv4_address`. - networks = coalesce(each.value.network.networks, []) */ } diff --git a/docker-stack/variables.tf b/docker-stack/variables.tf index 39aa465..8e372b5 100644 --- a/docker-stack/variables.tf +++ b/docker-stack/variables.tf @@ -1,9 +1,10 @@ variable "stack" { type = object({ - env = optional(list(string)) - mounts = optional(list(string)) - volumes = optional(list(string)) - networks = optional(map(object({ + host = optional(string, "tower") + env = optional(list(string)) + mounts = optional(list(string)) + volumes = optional(list(string)) + networks = optional(map(object({ internal = optional(bool, false) driver = optional(string, "bridge") options = optional(map(string), {}) @@ -19,7 +20,7 @@ variable "stack" { enable_gpu = optional(bool, false) env = optional(list(string)) volumes = optional(list(string)), - secrets = optional(map(string)), + secrets = optional(map(string)), capabilities = optional(object({ add = optional(list(string)) drop = optional(list(string)) @@ -29,21 +30,21 @@ variable "stack" { internal = optional(bool, true), domain_name = optional(string) }) - auth = optional(object({ - enabled = optional(bool, false) - group = optional(string, "Uncategorized") - proxy = optional(object({ - enabled = optional(bool, false) - user_secret = optional(string) - pass_secret = optional(string) + auth = optional(object({ + enabled = optional(bool, false) + group = optional(string, "Uncategorized") + proxy = optional(object({ + enabled = optional(bool, false) + user_secret = optional(string) + pass_secret = optional(string) + }), {}) + oauth = optional(object({ + enabled = optional(bool, false), + keys = optional(map(string), {}), + scopes = optional(list(string)), + redirect_uris = optional(list(string)) + }), {}) }), {}) - oauth = optional(object({ - enabled = optional(bool, false), - keys = optional(map(string), {}), - scopes = optional(list(string)), - redirect_uris = optional(list(string)) - }), {}) - }), {}) network = optional(object({ internal = optional(bool, false) service_port = optional(number) diff --git a/docker/variables.tf b/docker/variables.tf index b816eed..c917a06 100644 --- a/docker/variables.tf +++ b/docker/variables.tf @@ -25,8 +25,8 @@ variable "networks" { } variable "enable_gpu" { - type = bool - default = false + type = bool + default = false description = "If true, use nvidia runtime to add GPU support to the container." } diff --git a/host-connection/main.tf b/host-connection/main.tf new file mode 100644 index 0000000..b57b0c7 --- /dev/null +++ b/host-connection/main.tf @@ -0,0 +1,10 @@ +data "infisical_projects" "home-net" { + slug = var.system.infisical.project +} + +data "infisical_secrets" "host_connections" { + env_slug = var.system.infisical.environment + workspace_id = data.infisical_projects.home-net.id + + folder_path = var.system.infisical.host_connections +} \ No newline at end of file diff --git a/host-connection/outputs.tf b/host-connection/outputs.tf new file mode 100644 index 0000000..d2da8b5 --- /dev/null +++ b/host-connection/outputs.tf @@ -0,0 +1,5 @@ +output "host_connection" { + description = "Connection details for host" + value = data.infisical_secrets.host_connections.secrets[var.service.host].value + sensitive = true +} \ No newline at end of file diff --git a/host-connection/variables.tf b/host-connection/variables.tf new file mode 100644 index 0000000..4e8afb1 --- /dev/null +++ b/host-connection/variables.tf @@ -0,0 +1,5 @@ +variable "host" { + type = string + default = "tower" + description = "Name of host connection string as stored in infisical" +} \ No newline at end of file