From defcd6e385efa93d7612babce25b386e740c56db Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:29:25 -0500 Subject: [PATCH 01/28] Added provider config and variable --- docker/main.tf | 5 +++++ docker/variables.tf | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docker/main.tf b/docker/main.tf index ebd4b72..82056e4 100644 --- a/docker/main.tf +++ b/docker/main.tf @@ -7,6 +7,11 @@ terraform { } } +provider "docker" { + host = var.host_connection + ssh_opts = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] +} + resource "docker_image" "main" { name = var.container_image } diff --git a/docker/variables.tf b/docker/variables.tf index b816eed..b02880c 100644 --- a/docker/variables.tf +++ b/docker/variables.tf @@ -3,6 +3,11 @@ variable "container_name" { description = "Name for the container to be created" } +variable "host_connection" { + type = string + description = "Host connection string" +} + variable "container_image" { type = string description = "Name and tag of the image to use (ex. ubuntu:latest)" From 6c206988cac48871383533290684647320ecaa48 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:30:29 -0500 Subject: [PATCH 02/28] Formatting --- docker/variables.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/variables.tf b/docker/variables.tf index b02880c..e50a3e9 100644 --- a/docker/variables.tf +++ b/docker/variables.tf @@ -4,7 +4,7 @@ variable "container_name" { } variable "host_connection" { - type = string + type = string description = "Host connection string" } @@ -30,8 +30,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." } From ef9506b1dc887f0dc106953bf8fbf23d2d7a24ce Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:33:38 -0500 Subject: [PATCH 03/28] Added host config and formatting --- docker-service/auth.tf | 8 ++++---- docker-service/dns.tf | 2 +- docker-service/locals.tf | 4 ++-- docker-service/main.tf | 3 ++- docker-service/secrets.tf | 14 ++++++++++++-- docker-service/variables.tf | 5 +++-- 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docker-service/auth.tf b/docker-service/auth.tf index a7398dc..9eae373 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 = "git@github.com:DCCoder90/home-tf-modules.git//proxy_auth?ref=1.0.0" 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 = "git@github.com:DCCoder90/home-tf-modules.git//oauth_auth?ref=1.0.0" 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( [ { diff --git a/docker-service/dns.tf b/docker-service/dns.tf index 3c757ac..f64b7ba 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 = "git@github.com:DCCoder90/home-tf-modules.git//dns?ref=1.0.0" 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..a8a26ec 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 = "git@github.com:DCCoder90/home-tf-modules.git//docker?ref=1.0.0" 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 @@ -11,6 +11,7 @@ module "service_container" { mounts = var.service.mounts container_capabilities = var.service.capabilities commands = var.service.commands + host_connection = infisical_secrets.host_connections.secrets[var.service.host].value # Attach the container to custom networks defined in the stack, but only if the service # explicitly lists that network in its own configuration. diff --git a/docker-service/secrets.tf b/docker-service/secrets.tf index 50abd63..63084c3 100644 --- a/docker-service/secrets.tf +++ b/docker-service/secrets.tf @@ -7,14 +7,24 @@ 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{ +data "infisical_secrets" "host_connections" { + # 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.host_connections +} + +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..9ba90cb 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)) + 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({ From eb4ef54a9b7ad2f7f5d00fab3cba97367851895a Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:35:38 -0500 Subject: [PATCH 04/28] Added host to stack --- docker-stack/main.tf | 25 ++++--------------------- docker-stack/variables.tf | 37 +++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/docker-stack/main.tf b/docker-stack/main.tf index 88ac8ff..e02ac44 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -5,8 +5,8 @@ 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 = "git@github.com:DCCoder90/home-tf-modules.git//docker-network?ref=1.0.0" networks = local.creatable_networks } @@ -16,26 +16,9 @@ module "service_container" { source = "git@github.com:DCCoder90/home-tf-modules.git//docker-service?ref=1.0.0" 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..7abd37c 100644 --- a/docker-stack/variables.tf +++ b/docker-stack/variables.tf @@ -1,8 +1,9 @@ variable "stack" { type = object({ - env = optional(list(string)) - mounts = optional(list(string)) - volumes = optional(list(string)) + 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") @@ -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) From 34b0b46ad469ad5629e34d691244617a7cf1e647 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:38:57 -0500 Subject: [PATCH 05/28] Added host to each service from the stack. --- docker-stack/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-stack/main.tf b/docker-stack/main.tf index e02ac44..def76b2 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -15,7 +15,7 @@ module "service_container" { for_each = var.stack.services source = "git@github.com:DCCoder90/home-tf-modules.git//docker-service?ref=1.0.0" - service = each.value + service = merge(each.value, { host = var.stack.host }) system = var.system /* From 1cd2cf9b1a426361aafd8df8cf44f7355efbcac3 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:41:05 -0500 Subject: [PATCH 06/28] Removed copy/paste error --- docker-service/secrets.tf | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docker-service/secrets.tf b/docker-service/secrets.tf index 63084c3..99a0e81 100644 --- a/docker-service/secrets.tf +++ b/docker-service/secrets.tf @@ -15,12 +15,9 @@ data "infisical_secrets" "secrets" { } data "infisical_secrets" "host_connections" { - # 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.host_connections } From 3b4d967e316aa7993e1953d37a2f36fb62a55b24 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:47:29 -0500 Subject: [PATCH 07/28] Update modules to use relative paths --- docker-service/auth.tf | 4 ++-- docker-service/dns.tf | 2 +- docker-service/main.tf | 2 +- docker-stack/main.tf | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-service/auth.tf b/docker-service/auth.tf index 9eae373..1d74a81 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 diff --git a/docker-service/dns.tf b/docker-service/dns.tf index f64b7ba..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/main.tf b/docker-service/main.tf index a8a26ec..fa33ff6 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-stack/main.tf b/docker-stack/main.tf index def76b2..d89bf07 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -6,14 +6,14 @@ 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" + 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 = merge(each.value, { host = var.stack.host }) system = var.system From 7ca79acb34f19969fda18eb8788f8cb234d7c22e Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:52:08 -0500 Subject: [PATCH 08/28] Add workflow --- .github/workflows/terraform-validate.yml | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/terraform-validate.yml diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml new file mode 100644 index 0000000..ec3d4ea --- /dev/null +++ b/.github/workflows/terraform-validate.yml @@ -0,0 +1,92 @@ +name: Terraform Module Validation + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + find_modules: + runs-on: ubuntu-latest + outputs: + module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} + env: + MODULE_PATHS: "*" + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Find Terraform Module Directories + id: set_matrix + shell: bash + run: | + declare -a module_dirs_array + + # If MODULE_PATHS contains spaces, treat it as a list of explicit paths + if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then + echo "Detected multiple explicit module paths from MODULE_PATHS env var." + read -ra module_dirs_array <<< "${{ env.MODULE_PATHS }}" + else + echo "Searching for modules using pattern: ${{ env.MODULE_PATHS }}" + mapfile -t module_dirs_array < <(find . -path "${{ env.MODULE_PATHS }}" -type d -exec sh -c 'ls -1 "{}"/*.tf &> /dev/null' \; -print) + fi + + if [ ${#module_dirs_array[@]} -eq 0 ]; then + echo "No Terraform module directories found matching the pattern: ${{ env.MODULE_PATHS }}" + echo "::error::No Terraform module directories found. Please ensure MODULE_PATHS is correctly configured." + exit 1 + fi + + json_array=$(printf '%s\n' "${module_dirs_array[@]}" | jq -R . | jq -s .) + echo "Found the following module directories: $json_array" + echo "module_dirs=$json_array" >> "$GITHUB_OUTPUT" + + validate_each_module: + needs: find_modules + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + module_dir: ${{ fromJson(needs.find_modules.outputs.module_dirs) }} + + 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 ---" + + if [ ! -d "$module_dir" ]; then + echo "Error: Directory $module_dir does not exist. This should not happen if 'find_modules' succeeded." + exit 1 + fi + + 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!" From 7c4e884fadb71a194ba01d76fa88243ca0ab80ca Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:54:03 -0500 Subject: [PATCH 09/28] Added ignore directories --- .github/workflows/terraform-validate.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index ec3d4ea..b004c41 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -16,6 +16,7 @@ jobs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: MODULE_PATHS: "*" + IGNORE_DIRS: ".git .github .terraform" steps: - name: Checkout Repository @@ -27,6 +28,15 @@ jobs: run: | declare -a module_dirs_array + exclude_paths="" + if [[ -n "${{ env.IGNORE_DIRS }}" ]]; then + echo "Ignoring directories: ${{ env.IGNORE_DIRS }}" + read -ra ignore_dirs_array <<< "${{ env.IGNORE_DIRS }}" + for dir in "${ignore_dirs_array[@]}"; do + exclude_paths+=" -path \"./$dir\" -prune -o" + done + fi + # If MODULE_PATHS contains spaces, treat it as a list of explicit paths if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then echo "Detected multiple explicit module paths from MODULE_PATHS env var." From e10b293939aa057f89c8b076cd053c7b6fc4d556 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 02:55:22 -0500 Subject: [PATCH 10/28] Forgot to tell it to actually ignore them... --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index b004c41..96b1dc2 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -43,7 +43,7 @@ jobs: read -ra module_dirs_array <<< "${{ env.MODULE_PATHS }}" else echo "Searching for modules using pattern: ${{ env.MODULE_PATHS }}" - mapfile -t module_dirs_array < <(find . -path "${{ env.MODULE_PATHS }}" -type d -exec sh -c 'ls -1 "{}"/*.tf &> /dev/null' \; -print) + mapfile -t module_dirs_array < <(find . ${exclude_paths} -path "${{ env.MODULE_PATHS }}" -type d -exec sh -c 'ls -1 "{}"/*.tf &> /dev/null' \; -print) fi if [ ${#module_dirs_array[@]} -eq 0 ]; then From f9db425d717940ad35964206213c529a19c29727 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:01:00 -0500 Subject: [PATCH 11/28] Test --- .github/workflows/terraform-validate.yml | 86 ++++++++++++++++++------ 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 96b1dc2..4e2584e 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -15,7 +15,7 @@ jobs: outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "*" + MODULE_PATHS: "*" IGNORE_DIRS: ".git .github .terraform" steps: @@ -27,23 +27,61 @@ jobs: shell: bash run: | declare -a module_dirs_array - - exclude_paths="" - if [[ -n "${{ env.IGNORE_DIRS }}" ]]; then - echo "Ignoring directories: ${{ env.IGNORE_DIRS }}" - read -ra ignore_dirs_array <<< "${{ env.IGNORE_DIRS }}" - for dir in "${ignore_dirs_array[@]}"; do - exclude_paths+=" -path \"./$dir\" -prune -o" - done - fi - - # If MODULE_PATHS contains spaces, treat it as a list of explicit paths - if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then - echo "Detected multiple explicit module paths from MODULE_PATHS env var." - read -ra module_dirs_array <<< "${{ env.MODULE_PATHS }}" - else - echo "Searching for modules using pattern: ${{ env.MODULE_PATHS }}" - mapfile -t module_dirs_array < <(find . ${exclude_paths} -path "${{ env.MODULE_PATHS }}" -type d -exec sh -c 'ls -1 "{}"/*.tf &> /dev/null' \; -print) + declare -a ignore_dirs_list # Declare as array for easier iteration + + read -ra ignore_dirs_list <<< "${{ env.IGNORE_DIRS }}" + + echo "Searching for module directories..." + + find . -type d -print0 | while IFS= read -r -d $'\0' dir; do + # Skip the current directory "." if MODULE_PATHS is not explicitly "." or "*" + if [[ "$dir" == "." && "${{ env.MODULE_PATHS }}" != "." && "${{ env.MODULE_PATHS }}" != "*" ]]; then + continue + fi + + # Check if directory contains .tf files + if ! compgen -G "${dir}/*.tf" > /dev/null; then + continue # Skip if no .tf files + fi + + # Check if directory should be ignored + local is_ignored=false + for ignored_dir in "${ignore_dirs_list[@]}"; do + if [[ "$dir" == "./$ignored_dir"* || "$dir" == "$ignored_dir"* ]]; then + is_ignored=true + break + fi + done + if [[ "$is_ignored" == true ]]; then + continue + fi + + # Check if the directory matchesMODULE_PATHS pattern + local module_path_match=false + if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then + # Explicit list of paths + read -ra explicit_paths <<< "${{ env.MODULE_PATHS }}" + for path in "${explicit_paths[@]}"; do + if [[ "$dir" == "$path" ]]; then + module_path_match=true + break + fi + done + else + if [[ "$dir" == ${{ env.MODULE_PATHS }} ]]; then + module_path_match=true + fi + fi + + if [[ "$module_path_match" == true ]]; then + module_dirs_array+=("$dir") + fi + done + + # Ensure `jq` is installed + if ! command -v jq &> /dev/null; then + echo "jq is not installed. Installing jq..." + sudo apt-get update && sudo apt-get install -y jq fi if [ ${#module_dirs_array[@]} -eq 0 ]; then @@ -52,12 +90,18 @@ jobs: exit 1 fi + # Filter out duplicates + IFS=$'\n' sorted_unique_dirs=($(sort -u <<<"${module_dirs_array[*]}")) + unset IFS + module_dirs_array=("${sorted_unique_dirs[@]}") + + # Format the array json_array=$(printf '%s\n' "${module_dirs_array[@]}" | jq -R . | jq -s .) echo "Found the following module directories: $json_array" echo "module_dirs=$json_array" >> "$GITHUB_OUTPUT" validate_each_module: - needs: find_modules + needs: find_modules runs-on: ubuntu-latest strategy: fail-fast: false @@ -71,7 +115,7 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: - terraform_version: "latest" + terraform_version: "latest" - name: Validate Module ${{ matrix.module_dir }} shell: bash @@ -99,4 +143,4 @@ jobs: fi popd > /dev/null || { echo "Failed to return from directory"; exit 1; } - echo "Module $module_dir validated successfully!" + echo "Module $module_dir validated successfully!" \ No newline at end of file From a7dddfafabd5435a88619ce5040ee3b86e7ef34b Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:02:30 -0500 Subject: [PATCH 12/28] Test --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 4e2584e..cf83b39 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -45,7 +45,7 @@ jobs: fi # Check if directory should be ignored - local is_ignored=false + is_ignored=false for ignored_dir in "${ignore_dirs_list[@]}"; do if [[ "$dir" == "./$ignored_dir"* || "$dir" == "$ignored_dir"* ]]; then is_ignored=true From af2765236359dc818911e9028fc2d3beea929bf1 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:02:58 -0500 Subject: [PATCH 13/28] Test --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index cf83b39..7ffbc71 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -57,7 +57,7 @@ jobs: fi # Check if the directory matchesMODULE_PATHS pattern - local module_path_match=false + module_path_match=false if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then # Explicit list of paths read -ra explicit_paths <<< "${{ env.MODULE_PATHS }}" From 4aec8e41fc433220d1595dd745a62d71f879549a Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:04:05 -0500 Subject: [PATCH 14/28] Test --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 7ffbc71..8b6940e 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -15,7 +15,7 @@ jobs: outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "*" + MODULE_PATHS: "." IGNORE_DIRS: ".git .github .terraform" steps: From 1a3f7721f4aa63a7168dff176d6200f4a3d71638 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:04:28 -0500 Subject: [PATCH 15/28] Test --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 8b6940e..5fb0cef 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -15,7 +15,7 @@ jobs: outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "." + MODULE_PATHS: "./" IGNORE_DIRS: ".git .github .terraform" steps: From 8ee5165f41fabf8b36840bfe515a616ab973abdf Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:05:18 -0500 Subject: [PATCH 16/28] Test --- .github/workflows/terraform-validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 5fb0cef..0a8d7c1 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -15,8 +15,8 @@ jobs: outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "./" - IGNORE_DIRS: ".git .github .terraform" + MODULE_PATHS: "*" + IGNORE_DIRS: ".git .github" steps: - name: Checkout Repository From f29da5fb411d471b49eccae33ce6b04b46307d70 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:08:33 -0500 Subject: [PATCH 17/28] Test --- .github/workflows/terraform-validate.yml | 31 +++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 0a8d7c1..ae89513 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -27,7 +27,7 @@ jobs: shell: bash run: | declare -a module_dirs_array - declare -a ignore_dirs_list # Declare as array for easier iteration + declare -a ignore_dirs_list read -ra ignore_dirs_list <<< "${{ env.IGNORE_DIRS }}" @@ -39,11 +39,6 @@ jobs: continue fi - # Check if directory contains .tf files - if ! compgen -G "${dir}/*.tf" > /dev/null; then - continue # Skip if no .tf files - fi - # Check if directory should be ignored is_ignored=false for ignored_dir in "${ignore_dirs_list[@]}"; do @@ -56,10 +51,15 @@ jobs: continue fi - # Check if the directory matchesMODULE_PATHS pattern + if ! find "$dir" -maxdepth 1 -name "*.tf" -print -quit | grep -q .; then + continue + fi + module_path_match=false - if [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then - # Explicit list of paths + if [[ "${{ env.MODULE_PATHS }}" == "*" ]]; then + # If pattern is '*', all non-ignored directories with .tf files are modules + module_path_match=true + elif [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then read -ra explicit_paths <<< "${{ env.MODULE_PATHS }}" for path in "${explicit_paths[@]}"; do if [[ "$dir" == "$path" ]]; then @@ -68,9 +68,14 @@ jobs: fi done else - if [[ "$dir" == ${{ env.MODULE_PATHS }} ]]; then - module_path_match=true - fi + normalized_dir="${dir#./}" + normalized_module_paths="${{ env.MODULE_PATHS }#./}" + + case "$normalized_dir" in + "$normalized_module_paths") + module_path_match=true + ;; + esac fi if [[ "$module_path_match" == true ]]; then @@ -90,12 +95,10 @@ jobs: exit 1 fi - # Filter out duplicates IFS=$'\n' sorted_unique_dirs=($(sort -u <<<"${module_dirs_array[*]}")) unset IFS module_dirs_array=("${sorted_unique_dirs[@]}") - # Format the array json_array=$(printf '%s\n' "${module_dirs_array[@]}" | jq -R . | jq -s .) echo "Found the following module directories: $json_array" echo "module_dirs=$json_array" >> "$GITHUB_OUTPUT" From be201a4057cc4e1b9be1859d90ee35239edfded8 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:10:26 -0500 Subject: [PATCH 18/28] Test --- .github/workflows/terraform-validate.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index ae89513..b3937ea 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -68,8 +68,9 @@ jobs: fi done else + temp_module_paths="${{ env.MODULE_PATHS }}" normalized_dir="${dir#./}" - normalized_module_paths="${{ env.MODULE_PATHS }#./}" + normalized_module_paths="${temp_module_paths#./}" case "$normalized_dir" in "$normalized_module_paths") From 20c474f9e5f9ae20fd55b27600b940cd263992c5 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:11:33 -0500 Subject: [PATCH 19/28] Test --- .github/workflows/terraform-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index b3937ea..ad4c7cb 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -15,7 +15,7 @@ jobs: outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "*" + MODULE_PATHS: "." IGNORE_DIRS: ".git .github" steps: From 16b81ad900686820b6d344ea4244b4d25cf5e529 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:13:44 -0500 Subject: [PATCH 20/28] Maybe AI can help? --- .github/workflows/terraform-validate.yml | 109 +++++++++++++++++------ 1 file changed, 84 insertions(+), 25 deletions(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index ad4c7cb..8325340 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -7,16 +7,25 @@ on: pull_request: branches: - main - workflow_dispatch: + workflow_dispatch: # Allows manual triggering of the workflow jobs: + # This job finds all Terraform module directories find_modules: runs-on: ubuntu-latest outputs: module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} env: - MODULE_PATHS: "." - IGNORE_DIRS: ".git .github" + # Customize this variable to match the directory structure of your Terraform modules. + # Examples: + # - "modules/*": If your modules are directly under a 'modules' directory (e.g., modules/my-module, modules/another-module) + # - "*": If your modules are top-level directories in the repo (e.g., docker-network, docker-service) + # - "./docker-network ./docker-service": For specific known module paths + MODULE_PATHS: "./docker-network ./docker-service" # Adjust this based on your actual module locations + + # Optional: Specify directories to ignore when searching for modules. + # Use space-separated paths, e.g., ".git .github .terraform" + IGNORE_DIRS: ".git .github .terraform" steps: - name: Checkout Repository @@ -29,62 +38,109 @@ jobs: declare -a module_dirs_array declare -a ignore_dirs_list + # Split IGNORE_DIRS into an array read -ra ignore_dirs_list <<< "${{ env.IGNORE_DIRS }}" echo "Searching for module directories..." - find . -type d -print0 | while IFS= read -r -d $'\0' dir; do - # Skip the current directory "." if MODULE_PATHS is not explicitly "." or "*" - if [[ "$dir" == "." && "${{ env.MODULE_PATHS }}" != "." && "${{ env.MODULE_PATHS }}" != "*" ]]; then - continue - fi + # Build the find command's exclusion arguments + find_exclude_args="" + for ignored_dir in "${ignore_dirs_list[@]}"; do + # Ensure the path is relative to the current directory for find + # Add -path and -prune to exclude these directories from find's search + find_exclude_args+=" -path \"./$ignored_dir\" -prune" + done - # Check if directory should be ignored - is_ignored=false + # Use find to get all directories, excluding ignored ones, then filter in bash + # -mindepth 1 to exclude the current directory '.' from the initial find results + # unless it's explicitly included later by MODULE_PATHS logic. + # -print0 for safe handling of spaces/special chars + # `|| true` prevents `find` from exiting with an error if no results are found, + # allowing the script to continue and handle the empty list. + mapfile -t -d $'\0' potential_dirs < <(find . -mindepth 1 $find_exclude_args -type d -print0 || true) + + # Add the current directory '.' to potential_dirs if MODULE_PATHS explicitly targets it or is '*' + if [[ "${{ env.MODULE_PATHS }}" == "." || "${{ env.MODULE_PATHS }}" == "*" ]]; then + # Check if '.' is explicitly ignored + is_dot_ignored=false for ignored_dir in "${ignore_dirs_list[@]}"; do - if [[ "$dir" == "./$ignored_dir"* || "$dir" == "$ignored_dir"* ]]; then - is_ignored=true + if [[ "." == "./$ignored_dir"* || "." == "$ignored_dir"* ]]; then + is_dot_ignored=true break fi done - if [[ "$is_ignored" == true ]]; then - continue + if [[ "$is_dot_ignored" == false ]]; then + # Add '.' to the beginning of the array if it's not ignored + potential_dirs=("./" "${potential_dirs[@]}") + fi + fi + + + # Now iterate through potential_dirs and apply remaining filters + for dir in "${potential_dirs[@]}"; do + # Normalize directory path (remove leading './' for consistent matching) + normalized_dir="${dir#./}" + # If normalized_dir is empty, it means the original dir was '.' + if [[ -z "$normalized_dir" ]]; then + normalized_dir="." fi + echo "Processing directory: $dir (normalized: $normalized_dir)" + + # Check if directory contains .tf files + # This is a robust way to check for .tf files in the current directory if ! find "$dir" -maxdepth 1 -name "*.tf" -print -quit | grep -q .; then - continue + echo " No .tf files found in $dir. Skipping." + continue # Skip if no .tf files fi + echo " .tf files found in $dir." + # Check if the directory matches the MODULE_PATHS pattern module_path_match=false if [[ "${{ env.MODULE_PATHS }}" == "*" ]]; then # If pattern is '*', all non-ignored directories with .tf files are modules module_path_match=true + echo " MODULE_PATHS is '*', matching $dir." elif [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then + # Explicit list of paths (e.g., "./docker-network ./docker-service") read -ra explicit_paths <<< "${{ env.MODULE_PATHS }}" for path in "${explicit_paths[@]}"; do - if [[ "$dir" == "$path" ]]; then + local_path_normalized="${path#./}" # Normalize explicit paths too + if [[ "$normalized_dir" == "$local_path_normalized" ]]; then module_path_match=true + echo " Explicit path match: $normalized_dir == $local_path_normalized." break fi done else + # Single pattern (e.g., "modules/*", "./my-module") + # Assign env var to a bash var first, then apply bash parameter expansion temp_module_paths="${{ env.MODULE_PATHS }}" - normalized_dir="${dir#./}" - normalized_module_paths="${temp_module_paths#./}" + normalized_module_paths="${temp_module_paths#./}" # This will be like "modules/*" or "docker-network" + echo " Checking pattern: normalized_dir='$normalized_dir', normalized_module_paths='$normalized_module_paths'" + # Use case statement for robust pattern matching + # IMPORTANT: $normalized_module_paths should NOT be quoted here if it contains glob characters case "$normalized_dir" in - "$normalized_module_paths") + $normalized_module_paths) # Removed quotes around $normalized_module_paths to allow glob expansion module_path_match=true + echo " Pattern match: $normalized_dir matches $normalized_module_paths." + ;; + *) + echo " Pattern no match: $normalized_dir does not match $normalized_module_paths." ;; esac fi if [[ "$module_path_match" == true ]]; then module_dirs_array+=("$dir") + echo " Adding $dir to module_dirs_array." + else + echo " $dir did not match MODULE_PATHS criteria. Skipping." fi done - # Ensure `jq` is installed + # Ensure `jq` is installed for JSON processing if ! command -v jq &> /dev/null; then echo "jq is not installed. Installing jq..." sudo apt-get update && sudo apt-get install -y jq @@ -96,21 +152,24 @@ jobs: exit 1 fi + # Filter out duplicates and sort for consistent output IFS=$'\n' sorted_unique_dirs=($(sort -u <<<"${module_dirs_array[*]}")) unset IFS module_dirs_array=("${sorted_unique_dirs[@]}") + # Format the array as a JSON string for the matrix output json_array=$(printf '%s\n' "${module_dirs_array[@]}" | jq -R . | jq -s .) echo "Found the following module directories: $json_array" echo "module_dirs=$json_array" >> "$GITHUB_OUTPUT" + # This job validates each module found by the previous job validate_each_module: - needs: find_modules + needs: find_modules # This job depends on the 'find_modules' job runs-on: ubuntu-latest strategy: - fail-fast: false + fail-fast: false # Allows other matrix jobs to continue even if one fails matrix: - module_dir: ${{ fromJson(needs.find_modules.outputs.module_dirs) }} + module_dir: ${{ fromJson(needs.find_modules.outputs.module_dirs) }} # Use the output from find_modules steps: - name: Checkout Repository @@ -119,7 +178,7 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: - terraform_version: "latest" + terraform_version: "latest" # Or specify a version like "1.5.0" - name: Validate Module ${{ matrix.module_dir }} shell: bash @@ -147,4 +206,4 @@ jobs: fi popd > /dev/null || { echo "Failed to return from directory"; exit 1; } - echo "Module $module_dir validated successfully!" \ No newline at end of file + echo "Module $module_dir validated successfully!" From 87102a75191a6dc6c20cc337a57bbe6bc8b6af69 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:19:19 -0500 Subject: [PATCH 21/28] AI was useless, just going to make it stupid simple.... --- .github/workflows/terraform-validate.yml | 176 ++--------------------- 1 file changed, 13 insertions(+), 163 deletions(-) diff --git a/.github/workflows/terraform-validate.yml b/.github/workflows/terraform-validate.yml index 8325340..659d143 100644 --- a/.github/workflows/terraform-validate.yml +++ b/.github/workflows/terraform-validate.yml @@ -7,169 +7,24 @@ on: pull_request: branches: - main - workflow_dispatch: # Allows manual triggering of the workflow + workflow_dispatch: jobs: - # This job finds all Terraform module directories - find_modules: - runs-on: ubuntu-latest - outputs: - module_dirs: ${{ steps.set_matrix.outputs.module_dirs }} - env: - # Customize this variable to match the directory structure of your Terraform modules. - # Examples: - # - "modules/*": If your modules are directly under a 'modules' directory (e.g., modules/my-module, modules/another-module) - # - "*": If your modules are top-level directories in the repo (e.g., docker-network, docker-service) - # - "./docker-network ./docker-service": For specific known module paths - MODULE_PATHS: "./docker-network ./docker-service" # Adjust this based on your actual module locations - - # Optional: Specify directories to ignore when searching for modules. - # Use space-separated paths, e.g., ".git .github .terraform" - IGNORE_DIRS: ".git .github .terraform" - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Find Terraform Module Directories - id: set_matrix - shell: bash - run: | - declare -a module_dirs_array - declare -a ignore_dirs_list - - # Split IGNORE_DIRS into an array - read -ra ignore_dirs_list <<< "${{ env.IGNORE_DIRS }}" - - echo "Searching for module directories..." - - # Build the find command's exclusion arguments - find_exclude_args="" - for ignored_dir in "${ignore_dirs_list[@]}"; do - # Ensure the path is relative to the current directory for find - # Add -path and -prune to exclude these directories from find's search - find_exclude_args+=" -path \"./$ignored_dir\" -prune" - done - - # Use find to get all directories, excluding ignored ones, then filter in bash - # -mindepth 1 to exclude the current directory '.' from the initial find results - # unless it's explicitly included later by MODULE_PATHS logic. - # -print0 for safe handling of spaces/special chars - # `|| true` prevents `find` from exiting with an error if no results are found, - # allowing the script to continue and handle the empty list. - mapfile -t -d $'\0' potential_dirs < <(find . -mindepth 1 $find_exclude_args -type d -print0 || true) - - # Add the current directory '.' to potential_dirs if MODULE_PATHS explicitly targets it or is '*' - if [[ "${{ env.MODULE_PATHS }}" == "." || "${{ env.MODULE_PATHS }}" == "*" ]]; then - # Check if '.' is explicitly ignored - is_dot_ignored=false - for ignored_dir in "${ignore_dirs_list[@]}"; do - if [[ "." == "./$ignored_dir"* || "." == "$ignored_dir"* ]]; then - is_dot_ignored=true - break - fi - done - if [[ "$is_dot_ignored" == false ]]; then - # Add '.' to the beginning of the array if it's not ignored - potential_dirs=("./" "${potential_dirs[@]}") - fi - fi - - - # Now iterate through potential_dirs and apply remaining filters - for dir in "${potential_dirs[@]}"; do - # Normalize directory path (remove leading './' for consistent matching) - normalized_dir="${dir#./}" - # If normalized_dir is empty, it means the original dir was '.' - if [[ -z "$normalized_dir" ]]; then - normalized_dir="." - fi - - echo "Processing directory: $dir (normalized: $normalized_dir)" - - # Check if directory contains .tf files - # This is a robust way to check for .tf files in the current directory - if ! find "$dir" -maxdepth 1 -name "*.tf" -print -quit | grep -q .; then - echo " No .tf files found in $dir. Skipping." - continue # Skip if no .tf files - fi - echo " .tf files found in $dir." - - # Check if the directory matches the MODULE_PATHS pattern - module_path_match=false - if [[ "${{ env.MODULE_PATHS }}" == "*" ]]; then - # If pattern is '*', all non-ignored directories with .tf files are modules - module_path_match=true - echo " MODULE_PATHS is '*', matching $dir." - elif [[ "${{ env.MODULE_PATHS }}" == *" "* ]]; then - # Explicit list of paths (e.g., "./docker-network ./docker-service") - read -ra explicit_paths <<< "${{ env.MODULE_PATHS }}" - for path in "${explicit_paths[@]}"; do - local_path_normalized="${path#./}" # Normalize explicit paths too - if [[ "$normalized_dir" == "$local_path_normalized" ]]; then - module_path_match=true - echo " Explicit path match: $normalized_dir == $local_path_normalized." - break - fi - done - else - # Single pattern (e.g., "modules/*", "./my-module") - # Assign env var to a bash var first, then apply bash parameter expansion - temp_module_paths="${{ env.MODULE_PATHS }}" - normalized_module_paths="${temp_module_paths#./}" # This will be like "modules/*" or "docker-network" - - echo " Checking pattern: normalized_dir='$normalized_dir', normalized_module_paths='$normalized_module_paths'" - # Use case statement for robust pattern matching - # IMPORTANT: $normalized_module_paths should NOT be quoted here if it contains glob characters - case "$normalized_dir" in - $normalized_module_paths) # Removed quotes around $normalized_module_paths to allow glob expansion - module_path_match=true - echo " Pattern match: $normalized_dir matches $normalized_module_paths." - ;; - *) - echo " Pattern no match: $normalized_dir does not match $normalized_module_paths." - ;; - esac - fi - - if [[ "$module_path_match" == true ]]; then - module_dirs_array+=("$dir") - echo " Adding $dir to module_dirs_array." - else - echo " $dir did not match MODULE_PATHS criteria. Skipping." - fi - done - - # Ensure `jq` is installed for JSON processing - if ! command -v jq &> /dev/null; then - echo "jq is not installed. Installing jq..." - sudo apt-get update && sudo apt-get install -y jq - fi - - if [ ${#module_dirs_array[@]} -eq 0 ]; then - echo "No Terraform module directories found matching the pattern: ${{ env.MODULE_PATHS }}" - echo "::error::No Terraform module directories found. Please ensure MODULE_PATHS is correctly configured." - exit 1 - fi - - # Filter out duplicates and sort for consistent output - IFS=$'\n' sorted_unique_dirs=($(sort -u <<<"${module_dirs_array[*]}")) - unset IFS - module_dirs_array=("${sorted_unique_dirs[@]}") - - # Format the array as a JSON string for the matrix output - json_array=$(printf '%s\n' "${module_dirs_array[@]}" | jq -R . | jq -s .) - echo "Found the following module directories: $json_array" - echo "module_dirs=$json_array" >> "$GITHUB_OUTPUT" - - # This job validates each module found by the previous job validate_each_module: - needs: find_modules # This job depends on the 'find_modules' job runs-on: ubuntu-latest strategy: - fail-fast: false # Allows other matrix jobs to continue even if one fails + fail-fast: false matrix: - module_dir: ${{ fromJson(needs.find_modules.outputs.module_dirs) }} # Use the output from find_modules + module_dir: + - "./authentik" + - "./dns" + - "./docker" + - "./docker-network" + - "./docker-service" + - "./docker-stack" + - "./nginx_config" + - "./oauth_auth" + - "./proxy_auth" steps: - name: Checkout Repository @@ -178,7 +33,7 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: - terraform_version: "latest" # Or specify a version like "1.5.0" + terraform_version: "latest" - name: Validate Module ${{ matrix.module_dir }} shell: bash @@ -186,11 +41,6 @@ jobs: module_dir="${{ matrix.module_dir }}" echo "--- Validating module: $module_dir ---" - if [ ! -d "$module_dir" ]; then - echo "Error: Directory $module_dir does not exist. This should not happen if 'find_modules' succeeded." - exit 1 - fi - pushd "$module_dir" > /dev/null || { echo "Failed to change directory to $module_dir"; exit 1; } echo "Running terraform init in $module_dir..." From 00342e35c82750da73547ad10f8853589cbc3f72 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Thu, 10 Jul 2025 03:21:08 -0500 Subject: [PATCH 22/28] oops --- docker-service/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-service/main.tf b/docker-service/main.tf index fa33ff6..99d5df9 100644 --- a/docker-service/main.tf +++ b/docker-service/main.tf @@ -11,7 +11,7 @@ module "service_container" { mounts = var.service.mounts container_capabilities = var.service.capabilities commands = var.service.commands - host_connection = infisical_secrets.host_connections.secrets[var.service.host].value + host_connection = data.infisical_secrets.host_connections.secrets[var.service.host].value # Attach the container to custom networks defined in the stack, but only if the service # explicitly lists that network in its own configuration. From 9e4ef280775cce64c0762d2deeee8cbd36513c18 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:18:37 -0500 Subject: [PATCH 23/28] Update uri path in auth module --- docker-service/auth.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-service/auth.tf b/docker-service/auth.tf index 1d74a81..7e0763c 100644 --- a/docker-service/auth.tf +++ b/docker-service/auth.tf @@ -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}" } ] ) From cd5476b6832177ec634cd284db5c79119b7e4967 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:44:42 -0500 Subject: [PATCH 24/28] Moved provider configuration out of modules. This will require caller to use new host-connection module to get connection string and then passthat into the module under host_connection variable. --- docker-service/main.tf | 2 +- docker-service/secrets.tf | 7 ------- docker-service/variables.tf | 7 ++++--- docker-stack/variables.tf | 11 ++++++----- host-connection/main.tf | 10 ++++++++++ host-connection/outputs.tf | 5 +++++ host-connection/variables.tf | 5 +++++ 7 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 host-connection/main.tf create mode 100644 host-connection/outputs.tf create mode 100644 host-connection/variables.tf diff --git a/docker-service/main.tf b/docker-service/main.tf index 99d5df9..99bde14 100644 --- a/docker-service/main.tf +++ b/docker-service/main.tf @@ -11,7 +11,7 @@ module "service_container" { mounts = var.service.mounts container_capabilities = var.service.capabilities commands = var.service.commands - host_connection = data.infisical_secrets.host_connections.secrets[var.service.host].value + host_connection = var.service.host_connection # Attach the container to custom networks defined in the stack, but only if the service # explicitly lists that network in its own configuration. diff --git a/docker-service/secrets.tf b/docker-service/secrets.tf index 99a0e81..c969a6f 100644 --- a/docker-service/secrets.tf +++ b/docker-service/secrets.tf @@ -14,13 +14,6 @@ data "infisical_secrets" "secrets" { folder_path = var.system.infisical.folder } -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 -} - locals { # Create a list of environment variables from the secrets map. secret_envs = (var.service.secrets != null && length(var.service.secrets) > 0) ? [ diff --git a/docker-service/variables.tf b/docker-service/variables.tf index 9ba90cb..a9cd430 100644 --- a/docker-service/variables.tf +++ b/docker-service/variables.tf @@ -20,9 +20,10 @@ variable "service" { volumes = optional(list(string)) # --- Environment & Secrets --- - env = optional(list(string)) - secrets = optional(map(string)) - host = optional(string, "tower") + env = optional(list(string)) + secrets = optional(map(string)) + host = optional(string, "tower") + host_connection = optional(string) # --- Networking & DNS --- network = optional(object({ diff --git a/docker-stack/variables.tf b/docker-stack/variables.tf index 7abd37c..8d2e8bb 100644 --- a/docker-stack/variables.tf +++ b/docker-stack/variables.tf @@ -1,10 +1,11 @@ variable "stack" { type = object({ - host = optional(string, "tower") - env = optional(list(string)) - mounts = optional(list(string)) - volumes = optional(list(string)) - networks = optional(map(object({ + host = optional(string, "tower") + host_connection = optional(string) + 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), {}) 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 From c35849f2ab9bb3f295b3cde111f513c4c7fcac61 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:46:53 -0500 Subject: [PATCH 25/28] Forgot to include host_connection in docker-stack module --- docker-stack/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-stack/main.tf b/docker-stack/main.tf index d89bf07..fb88672 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -15,7 +15,7 @@ module "service_container" { for_each = var.stack.services source = "../docker-service" - service = merge(each.value, { host = var.stack.host }) + service = merge(each.value, { host = var.stack.host, host_connection=var.stack.host_connection }) system = var.system /* From 1063abf9a3bceaf36caf893c8b3f3d7283f4d288 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:51:56 -0500 Subject: [PATCH 26/28] Remove all provider configuration for docker provider from module. --- docker-service/variables.tf | 1 - docker-stack/main.tf | 2 +- docker-stack/variables.tf | 1 - docker/main.tf | 14 -------------- docker/variables.tf | 5 ----- 5 files changed, 1 insertion(+), 22 deletions(-) diff --git a/docker-service/variables.tf b/docker-service/variables.tf index a9cd430..9aa8c76 100644 --- a/docker-service/variables.tf +++ b/docker-service/variables.tf @@ -23,7 +23,6 @@ variable "service" { env = optional(list(string)) secrets = optional(map(string)) host = optional(string, "tower") - host_connection = optional(string) # --- Networking & DNS --- network = optional(object({ diff --git a/docker-stack/main.tf b/docker-stack/main.tf index fb88672..9f81fbb 100644 --- a/docker-stack/main.tf +++ b/docker-stack/main.tf @@ -15,7 +15,7 @@ module "service_container" { for_each = var.stack.services source = "../docker-service" - service = merge(each.value, { host = var.stack.host, host_connection=var.stack.host_connection }) + service = each.value system = var.system /* diff --git a/docker-stack/variables.tf b/docker-stack/variables.tf index 8d2e8bb..8e372b5 100644 --- a/docker-stack/variables.tf +++ b/docker-stack/variables.tf @@ -1,7 +1,6 @@ variable "stack" { type = object({ host = optional(string, "tower") - host_connection = optional(string) env = optional(list(string)) mounts = optional(list(string)) volumes = optional(list(string)) diff --git a/docker/main.tf b/docker/main.tf index 82056e4..79d8d10 100644 --- a/docker/main.tf +++ b/docker/main.tf @@ -1,17 +1,3 @@ -terraform { - required_providers { - docker = { - source = "kreuzwerker/docker" - version = "3.6.2" - } - } -} - -provider "docker" { - host = var.host_connection - ssh_opts = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] -} - resource "docker_image" "main" { name = var.container_image } diff --git a/docker/variables.tf b/docker/variables.tf index e50a3e9..c917a06 100644 --- a/docker/variables.tf +++ b/docker/variables.tf @@ -3,11 +3,6 @@ variable "container_name" { description = "Name for the container to be created" } -variable "host_connection" { - type = string - description = "Host connection string" -} - variable "container_image" { type = string description = "Name and tag of the image to use (ex. ubuntu:latest)" From 055753a91e18801208dedef1228e8f1de045d998 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:55:00 -0500 Subject: [PATCH 27/28] . --- ReadMe.md | 11 ++++++++++- docker/main.tf | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) 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/main.tf b/docker/main.tf index 79d8d10..ebd4b72 100644 --- a/docker/main.tf +++ b/docker/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "3.6.2" + } + } +} + resource "docker_image" "main" { name = var.container_image } From ba85c7ddacaa39c8d24cc4097025756e0564618c Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Mon, 14 Jul 2025 09:56:43 -0500 Subject: [PATCH 28/28] . --- docker-service/main.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-service/main.tf b/docker-service/main.tf index 99bde14..f52ffd8 100644 --- a/docker-service/main.tf +++ b/docker-service/main.tf @@ -11,7 +11,6 @@ module "service_container" { mounts = var.service.mounts container_capabilities = var.service.capabilities commands = var.service.commands - host_connection = var.service.host_connection # Attach the container to custom networks defined in the stack, but only if the service # explicitly lists that network in its own configuration.