From 11c1e040fee0c4349bb4e424bf707c9d766aa346 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 02:51:10 -0500 Subject: [PATCH 01/29] Update arr services --- services/arrr/locals.tf | 20 +++++++++++++++++--- services/arrr/main.tf | 33 ++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/services/arrr/locals.tf b/services/arrr/locals.tf index 343fbb1..8377272 100644 --- a/services/arrr/locals.tf +++ b/services/arrr/locals.tf @@ -1,8 +1,22 @@ locals { - domain_name="test.mallett.family" - ip_address = "192.168.5.13" + zone_name = "mallett.family" + username = "default_user" password = "SecretPassword1!" + + proxy_ip = "192.168.4.2" authentik_ip = "192.168.4.55" - service_port = 9696 + authentik_port = 9000 + + services = { + 1 = { + service_name = "prowlarr" + image_name = "linuxserver/prowlarr:1.37.0" + username = "prowlarr_user" + password = "ProwlarrPassword1!" + domain_name = "prowlarr.mallett.family" + service_port = 9696 + ip_address = "192.168.5.13" + } + } } \ No newline at end of file diff --git a/services/arrr/main.tf b/services/arrr/main.tf index 39a1bee..4f0677b 100644 --- a/services/arrr/main.tf +++ b/services/arrr/main.tf @@ -1,22 +1,24 @@ module "service_docker" { + for_each = local.services source = "../../modules/docker" - container_name = "AAutomated_Test" - container_image = "linuxserver/prowlarr:1.37.0" + container_name = each.value.service_name + container_image = each.value.image_name attach_to_br0 = false attach_to_br1 = true - br1_ipv4_addr = local.ip_address + br1_ipv4_addr = each.value.ip_address } module "service_dns" { source = "../../modules/dns" + for_each = local.services internal_only = true - service_port = 9000//local.service_port - If using proxy_auth it needs to be the port to authentik - zone_name = "mallett.family" - domain_name = local.domain_name + service_port = local.authentik_port + zone_name = local.zone_name + domain_name = each.value.domain_name access_list_id = var.access_list_id - internal_host_ipv4 = "192.168.4.2" //Port to Nginx + internal_host_ipv4 = local.proxy_ip service_ipv4 = local.authentik_ip admin_email = var.admin_email dns_cloudflare_api_token = var.cloudflare_token @@ -27,19 +29,20 @@ module "service_dns" { module "authentication" { source = "../../modules/proxy_auth" + for_each = local.services - internal_host = "http://${local.ip_address}:${local.service_port}" - external_host = local.domain_name - name = "prowlarr" - username_attribute = "prowlarr_username" - password_attribute = "prowlarr_password" + internal_host = "http://${each.value.ip_address}:${each.value.service_port}" + external_host = each.value.domain_name + name = each.value.service_name + username_attribute = "${each.value.service_name}_username" + password_attribute = "${each.value.service_name}_password" create_access_group = true - access_group_name = "terraform_prowlarr" + access_group_name = "tf_${each.value.service_name}" user_to_add_to_access_group = var.admin_username access_group_attributes = jsonencode( { - prowlarr_username: local.username, - prowlarr_password: local.password + "${each.value.service_name}_username": local.username, + "${each.value.service_name}_password": local.password } ) } \ No newline at end of file From 2d1f488305314930744672877b62ece4910c4940 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 02:53:44 -0500 Subject: [PATCH 02/29] Update arr services --- services/arrr/locals.tf | 6 +----- services/arrr/main.tf | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/services/arrr/locals.tf b/services/arrr/locals.tf index 8377272..fb03102 100644 --- a/services/arrr/locals.tf +++ b/services/arrr/locals.tf @@ -1,9 +1,5 @@ locals { zone_name = "mallett.family" - - username = "default_user" - password = "SecretPassword1!" - proxy_ip = "192.168.4.2" authentik_ip = "192.168.4.55" authentik_port = 9000 @@ -12,7 +8,7 @@ locals { 1 = { service_name = "prowlarr" image_name = "linuxserver/prowlarr:1.37.0" - username = "prowlarr_user" + username = "default_username" password = "ProwlarrPassword1!" domain_name = "prowlarr.mallett.family" service_port = 9696 diff --git a/services/arrr/main.tf b/services/arrr/main.tf index 4f0677b..4b9f536 100644 --- a/services/arrr/main.tf +++ b/services/arrr/main.tf @@ -41,8 +41,8 @@ module "authentication" { user_to_add_to_access_group = var.admin_username access_group_attributes = jsonencode( { - "${each.value.service_name}_username": local.username, - "${each.value.service_name}_password": local.password + "${each.value.service_name}_username": each.value.username, + "${each.value.service_name}_password": each.value.password } ) } \ No newline at end of file From 7cbea3972fda1818314a1c54226f96f8abae6107 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:05:24 -0500 Subject: [PATCH 03/29] Services setup, still need bindmount volumes --- services/arrr/locals.tf | 33 +++++++++++++++ services/arrr/main.tf | 1 + services/arrr/service_config.tf | 74 +++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 services/arrr/service_config.tf diff --git a/services/arrr/locals.tf b/services/arrr/locals.tf index fb03102..8acfd72 100644 --- a/services/arrr/locals.tf +++ b/services/arrr/locals.tf @@ -13,6 +13,39 @@ locals { domain_name = "prowlarr.mallett.family" service_port = 9696 ip_address = "192.168.5.13" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 2 = { + service_name = "radarr" + image_name = "linuxserver/radarr:5.26.2" + username = "default_username" + password = "RadarrPassword1!" + domain_name = "radarr.mallett.family" + service_port = 7878 + ip_address = "192.168.5.14" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 3 = { + service_name = "sonarr" + image_name = "linuxserver/sonarr:4.0.14.2939-ls281" + username = "default_username" + password = "SonarrPassword1!" + domain_name = "sonarr.mallett.family" + service_port = 8989 + ip_address = "192.168.5.15" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] } } } \ No newline at end of file diff --git a/services/arrr/main.tf b/services/arrr/main.tf index 4b9f536..4ab4506 100644 --- a/services/arrr/main.tf +++ b/services/arrr/main.tf @@ -7,6 +7,7 @@ module "service_docker" { attach_to_br0 = false attach_to_br1 = true br1_ipv4_addr = each.value.ip_address + environment_vars = each.value.env } module "service_dns" { diff --git a/services/arrr/service_config.tf b/services/arrr/service_config.tf new file mode 100644 index 0000000..7b456b1 --- /dev/null +++ b/services/arrr/service_config.tf @@ -0,0 +1,74 @@ +locals{ + services = { + 1 = { + service_name = "prowlarr" + image_name = "linuxserver/prowlarr:1.37.0" + username = "default_username" + password = "ProwlarrPassword1!" + domain_name = "prowlarr.mallett.family" + service_port = 9696 + ip_address = "192.168.5.13" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 2 = { + service_name = "radarr" + image_name = "linuxserver/radarr:5.26.2" + username = "default_username" + password = "RadarrPassword1!" + domain_name = "radarr.mallett.family" + service_port = 7878 + ip_address = "192.168.5.14" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 3 = { + service_name = "sonarr" + image_name = "linuxserver/sonarr:4.0.14.2939-ls281" + username = "default_username" + password = "SonarrPassword1!" + domain_name = "sonarr.mallett.family" + service_port = 8989 + ip_address = "192.168.5.15" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 4 = { + service_name = "lidarr" + image_name = "linuxserver/lidarr:2.12.4" + username = "default_username" + password = "LidarrPassword1!" + domain_name = "lidarr.mallett.family" + service_port = 8686 + ip_address = "192.168.5.16" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + }, + 5 = { + service_name = "readarr" + image_name = "linuxserver/readarr:0.4.17-develop" + username = "default_username" + password = "ReadarrPassword1!" + domain_name = "readarr.mallett.family" + service_port = 8787 + ip_address = "192.168.5.17" + env = [ + "PUID=1000", + "PGID=1000", + "TZ=America/Chicago", + ] + } + } +} \ No newline at end of file From 6c653cd7e3b10a566e3539a4100fc78c1798365d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:29:44 -0500 Subject: [PATCH 04/29] Added github workflows --- .github/workflows/run-apply.yml | 56 +++++++++++++++++++++++++++++++++ .github/workflows/run-plan.yml | 33 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 .github/workflows/run-apply.yml create mode 100644 .github/workflows/run-plan.yml diff --git a/.github/workflows/run-apply.yml b/.github/workflows/run-apply.yml new file mode 100644 index 0000000..de8f913 --- /dev/null +++ b/.github/workflows/run-apply.yml @@ -0,0 +1,56 @@ +name: Terraform Apply + +on: + push: + branches: + - main + workflow_dispatch: # + inputs: + tfc_run_id: + description: 'Optional: Terraform Cloud Run ID to apply. If left empty, a new run will be created.' + required: false + type: string + +jobs: + terraform_apply: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Trigger Terraform Cloud Apply Plan + uses: hashicorp/tfc-workflows-github/actions/create-run@v1.3.2 + id: create-run + if: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && (github.event.inputs.tfc_run_id == '' || github.event.inputs.tfc_run_id == null)) }} + with: + token: ${{ secrets.TF_API_TOKEN }} + hostname: app.terraform.io + organization: ${{ secrets.TF_ORGANIZATION }} + workspace: ${{ secrets.TF_WORKSPACE }} + message: "Triggered by push to main: ${{ github.sha }}" + is_destroy: false + + - name: Check Terraform Cloud Run Status + if: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && (github.event.inputs.tfc_run_id == '' || github.event.inputs.tfc_run_id == null)) }} + run: | + echo "Terraform Cloud Plan ID: ${{ steps.create-run.outputs.run_id }}" + echo "Terraform Cloud Plan URL: ${{ steps.create-run.outputs.run_url }}" + + - name: Run Plan + uses: hashicorp/tfc-workflows-github/actions/apply-run@v1.3.2 + id: apply + continue-on-error: true + with: + hostname: app.terraform.io + token: ${{ secrets.TF_API_TOKEN }} + run: ${{ github.event.inputs.tfc_run_id || steps.create-run.outputs.run_id }} + comment: "Confirmed from GitHub Actions CI" + + - name: Check Terraform Cloud Run Status + run: | + echo "Terraform Cloud Apply ID: ${{ steps.apply.outputs.run_id }}" + echo "Terraform Cloud Apply URL: ${{ steps.apply.outputs.run_url }}" diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml new file mode 100644 index 0000000..bdc7204 --- /dev/null +++ b/.github/workflows/run-plan.yml @@ -0,0 +1,33 @@ +name: Terraform Plan + +on: + pull_request: + branches: + - main + +jobs: + terraform_plan: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Trigger Terraform Cloud Plan + uses: hashicorp/tfc-workflows-github/actions/create-run@v1.3.2 + id: create-run + with: + token: ${{ secrets.TF_API_TOKEN }} + hostname: app.terraform.io + organization: ${{ secrets.TF_ORGANIZATION }} + workspace: ${{ secrets.TF_WORKSPACE }} + message: "Triggered by PR push: ${{ github.event.pull_request.head.ref }}" + plan_only: true + + - name: Check Terraform Cloud Run Status + run: | + echo "Terraform Cloud Run ID: ${{ steps.create-run.outputs.run_id }}" + echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_url }}" \ No newline at end of file From 5b2b7326bfd85885c6f733df06e298143581da5b Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:32:20 -0500 Subject: [PATCH 05/29] Update workflows --- .github/workflows/run-apply.yml | 2 +- .github/workflows/run-plan.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-apply.yml b/.github/workflows/run-apply.yml index de8f913..c763a68 100644 --- a/.github/workflows/run-apply.yml +++ b/.github/workflows/run-apply.yml @@ -53,4 +53,4 @@ jobs: - name: Check Terraform Cloud Run Status run: | echo "Terraform Cloud Apply ID: ${{ steps.apply.outputs.run_id }}" - echo "Terraform Cloud Apply URL: ${{ steps.apply.outputs.run_url }}" + echo "Terraform Cloud Apply URL: ${{ steps.apply.outputs.run_link }}" diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index bdc7204..293800a 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -30,4 +30,4 @@ jobs: - name: Check Terraform Cloud Run Status run: | echo "Terraform Cloud Run ID: ${{ steps.create-run.outputs.run_id }}" - echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_url }}" \ No newline at end of file + echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_link }}" \ No newline at end of file From 2bdd2419f33245f0a201e8021faa08dadf9e6ab9 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:34:47 -0500 Subject: [PATCH 06/29] Removed comment from arrr services --- services.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services.tf b/services.tf index b253a2d..0075299 100644 --- a/services.tf +++ b/services.tf @@ -1,5 +1,4 @@ -/* -module "arr_service" { +module "arr_services" { source = "./services/arrr" admin_email = var.network_admin_email @@ -9,6 +8,7 @@ module "arr_service" { public_facing_ip = var.public_facing_ip } +/* module "grafana_service" { source = "./services/grafana" From d41a5abf83baa39a7b4f538045390ee800713d3c Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:42:10 -0500 Subject: [PATCH 07/29] Update plan --- .github/workflows/run-plan.yml | 52 +++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 293800a..6916db3 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -30,4 +30,54 @@ jobs: - name: Check Terraform Cloud Run Status run: | echo "Terraform Cloud Run ID: ${{ steps.create-run.outputs.run_id }}" - echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_link }}" \ No newline at end of file + echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_link }}" + + - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.3.2 + id: plan-output + with: + plan: ${{ steps.run.outputs.plan_id }} + + - uses: actions/github-script@v6 + if: github.event_name == 'pull_request' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // 1. Retrieve existing bot comments for the PR + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }) + const botComment = comments.find(comment => { + return comment.user.type === 'Bot' && comment.body.includes('HCP Terraform Plan Output') + }) + const output = `#### HCP Terraform Plan Output + \`\`\`\n + Plan: ${{ steps.plan-output.outputs.add }} to add, ${{ steps.plan-output.outputs.change }} to change, ${{ steps.plan-output.outputs.destroy }} to destroy. + \`\`\` + [HCP Terraform Plan](${{ steps.run.outputs.run_link }}) + ` + // 3. If we have a comment, update it, otherwise create a new one + if (botComment) { + github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: output + }) + } else { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + } + + - id: terraform-cloud-check-run-status + if: ${{ steps.run.outputs.run_status != 'planned_and_finished'}} + run: | + echo "HCP Terraform Run Failed or Requires Further Attention" + echo "Run Status: '${{ steps.run.outputs.run_status }}'" + echo "${{ steps.run.outputs.run_link }}" + exit 1 \ No newline at end of file From 6f2318608caac1876f10f22677989d05af595dde Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:45:07 -0500 Subject: [PATCH 08/29] Update plan --- .github/workflows/run-plan.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 6916db3..45ccaaf 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -27,15 +27,12 @@ jobs: message: "Triggered by PR push: ${{ github.event.pull_request.head.ref }}" plan_only: true - - name: Check Terraform Cloud Run Status - run: | - echo "Terraform Cloud Run ID: ${{ steps.create-run.outputs.run_id }}" - echo "Terraform Cloud Run URL: ${{ steps.create-run.outputs.run_link }}" - - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.3.2 id: plan-output with: - plan: ${{ steps.run.outputs.plan_id }} + plan: ${{ steps.create-run.outputs.plan_id }} + token: ${{ secrets.TF_API_TOKEN }} + organization: ${{ secrets.TF_ORGANIZATION }} - uses: actions/github-script@v6 if: github.event_name == 'pull_request' @@ -55,7 +52,7 @@ jobs: \`\`\`\n Plan: ${{ steps.plan-output.outputs.add }} to add, ${{ steps.plan-output.outputs.change }} to change, ${{ steps.plan-output.outputs.destroy }} to destroy. \`\`\` - [HCP Terraform Plan](${{ steps.run.outputs.run_link }}) + [HCP Terraform Plan](${{ steps.create-run.outputs.run_link }}) ` // 3. If we have a comment, update it, otherwise create a new one if (botComment) { @@ -75,9 +72,9 @@ jobs: } - id: terraform-cloud-check-run-status - if: ${{ steps.run.outputs.run_status != 'planned_and_finished'}} + if: ${{ steps.create-run.outputs.run_status != 'planned_and_finished'}} run: | echo "HCP Terraform Run Failed or Requires Further Attention" - echo "Run Status: '${{ steps.run.outputs.run_status }}'" - echo "${{ steps.run.outputs.run_link }}" + echo "Run Status: '${{ steps.create-run.outputs.run_status }}'" + echo "${{ steps.create-run.outputs.run_link }}" exit 1 \ No newline at end of file From 219ff40b95426c8e124786866a8e4a73d0780430 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:56:33 -0500 Subject: [PATCH 09/29] Updated ReadMe.md --- ReadMe.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index c959b4c..2f2e320 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,8 +1,10 @@ # Home-Net -This repository contains Terraform configurations for managing various services and infrastructure components within my home network. +This repository contains configurations for managing various services and infrastructure components within my home network. -Should I move this from Terraform to OpenTofu? Hmmm.... +## Purpose + +I want an easy way to automate and keep track of my local home lab. It would be nice to also have a way to easily and quickly recreate it in case of a catastrophic failure (flood, house fire, etc etc) ## Modules @@ -15,10 +17,10 @@ The `services/` directory contains specific Terraform configurations that deploy ## Prerequisites * **Target Environment:** This setup is primarily designed to run on an **Unraid server**. -* **Terraform Agent:** A Terraform agent (e.g., HCP Terraform self-hosted agent, GitLab Runner, GitHub Actions self-hosted runner, etc.) must be running on the Unraid server. +* **Terraform Agent:** A Terraform agent [must be running on the Unraid server](https://developer.hashicorp.com/terraform/cloud-docs/agents/agents#run-an-agent-with-docker). * This agent requires **direct access to the Docker socket** (typically `/var/run/docker.sock`) to manage Docker resources. Ensure the user running the agent has the necessary permissions. -* **Terraform CLI:** Terraform (version compatible with the configurations, e.g., v1.12.2 or later) installed on the machine where `terraform plan/apply` commands are initiated or on the agent itself. -* **Docker:** Docker must be installed and running on the Unraid server. +* **Terraform CLI:** Terraform (version compatible with the configurations, e.g., v1.12.2 or later) installed. +* **Docker:** Docker must be configured and running on the Unraid server. ## Usage @@ -33,12 +35,12 @@ The `services/` directory contains specific Terraform configurations that deploy ```bash terraform init ``` -4. **Review and Apply:** +4. **Review:** ```bash terraform plan - terraform apply ``` +Any applies should be done using the [run-apply](./.github/workflows/run-apply.yml) action. ## How is this run? @@ -58,15 +60,13 @@ This is run on a self-hosted agent on the Unraid Server. This is invoked using |AUTHENTIK_TOKEN|env|Y| |AUTHENTIK_URL|env|N| -You may be wondering why some things such as "public facing API" and "network admin email" are set to sensitive. Well, I want to update this soon to be invoked here by github actions, and honestly I don't want the world knowing those details. Aside from that, there's no reason for them to be marked sensitive. +You may be wondering why some things such as "public facing API" and "network admin email" are set to sensitive. Well, I honestly I don't want the world knowing those details. Aside from that, there's no reason for them to be marked sensitive. -Quick note: +### Cloudflare -Cloudflare API token must have these permissions: +The Cloudflare API token must have these permissions: - Zone:Read - DNS:Edit -## Purpose - -I want an easy way to automate and keep track of my local home lab. It would be nice to also have a way to easily and quickly recreate it in case of a catastrophic failure (flood, house fire, etc etc) \ No newline at end of file +In addition it must also be able to access any zones that are wished to be used. \ No newline at end of file From 1150abff02fc219de4660bfb20b471a0dde15cbf Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 03:57:54 -0500 Subject: [PATCH 10/29] Ignore non-tf paths --- .github/workflows/run-apply.yml | 5 +++++ .github/workflows/run-plan.yml | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-apply.yml b/.github/workflows/run-apply.yml index c763a68..db189ef 100644 --- a/.github/workflows/run-apply.yml +++ b/.github/workflows/run-apply.yml @@ -4,6 +4,11 @@ on: push: branches: - main + paths-ignore: + - .github/workflows/run-apply.yml + - .github/workflows/run-plan.yml + - ReadMe.md + - .gitignore workflow_dispatch: # inputs: tfc_run_id: diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 45ccaaf..2cff9ce 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -4,7 +4,11 @@ on: pull_request: branches: - main - + paths-ignore: + - .github/workflows/run-apply.yml + - .github/workflows/run-plan.yml + - ReadMe.md + - .gitignore jobs: terraform_plan: runs-on: ubuntu-latest From b278c5d680c7168f38c6deaa09712646dc52221d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 04:09:46 -0500 Subject: [PATCH 11/29] Update to use config.yaml --- config.yaml | 63 ++++++++++++++++++++++++++++++++++++++ services.tf | 1 + services/arrr/locals.tf | 45 --------------------------- services/arrr/main.tf | 6 ++-- services/arrr/variables.tf | 14 +++++++++ 5 files changed, 81 insertions(+), 48 deletions(-) diff --git a/config.yaml b/config.yaml index bfb8512..3e9fbc0 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,67 @@ system: +stacks: + arr_services: + prowlarr: + service_name: "prowlarr" + service_port: 9696 + image_name: "linuxserver/prowlarr:1.37.0" + username: "default_username" + password: "ProwlarrPassword1!" + domain_name: "prowlarr.dcapi.app" + ip_address: "192.168.5.13" + env: + - "PUID=1000" + - "PGID=1000" + - "TZ=America/Chicago" + sonarr: + service_name: "sonarr" + service_port: 8989 + image_name: "linuxserver/sonarr:4.0.14.2939-ls281" + username: "default_username" + password: "SonarrPassword1!" + domain_name: "sonarr.dcapi.app" + ip_address: "" + env: + - "PUID=1000" + - "PGID=1000" + - "TZ=America/Chicago" + radarr: + service_name: "radarr" + service_port: 7878 + image_name: "linuxserver/radarr:5.26.2" + username: "default_username" + password: "RadarrPassword1!" + domain_name: "radarr.dcapi.app" + ip_address: "" + env: + - "PUID=1000" + - "PGID=1000" + - "TZ=America/Chicago" + lidarr: #Need to fix + service_name: "lidarr" + service_port: 8686 + image_name: "linuxserver/lidarr:1.0.2.1495-ls108" + username: "default_username" + password: "LidarrPassword1!" + domain_name: "lidarr.dcapi.app" + ip_address: "" + env: + - "PUID=1000" + - "PGID=1000" + - "TZ=America/Chicago" + readarr: #Need to fix + service_name: "readarr" + service_port: 8787 + image_name: "linuxserver/readarr:1.0.2.1495-ls108" + username: "default_username" + password: "ReadarrPassword1!" + domain_name: "readarr.dcapi.app" + ip_address: "" + env: + - "PUID=1000" + - "PGID=1000" + - "TZ=America/Chicago" + services: authentik: admin-user: "" \ No newline at end of file diff --git a/services.tf b/services.tf index 0075299..516e5ee 100644 --- a/services.tf +++ b/services.tf @@ -6,6 +6,7 @@ module "arr_services" { cloudflare_token = var.cloudflare_api_token access_list_id = module.nginx_conf.internal_access_list_id public_facing_ip = var.public_facing_ip + services = local.config.stacks.arr_services } /* diff --git a/services/arrr/locals.tf b/services/arrr/locals.tf index 8acfd72..2cda7ed 100644 --- a/services/arrr/locals.tf +++ b/services/arrr/locals.tf @@ -3,49 +3,4 @@ locals { proxy_ip = "192.168.4.2" authentik_ip = "192.168.4.55" authentik_port = 9000 - - services = { - 1 = { - service_name = "prowlarr" - image_name = "linuxserver/prowlarr:1.37.0" - username = "default_username" - password = "ProwlarrPassword1!" - domain_name = "prowlarr.mallett.family" - service_port = 9696 - ip_address = "192.168.5.13" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 2 = { - service_name = "radarr" - image_name = "linuxserver/radarr:5.26.2" - username = "default_username" - password = "RadarrPassword1!" - domain_name = "radarr.mallett.family" - service_port = 7878 - ip_address = "192.168.5.14" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 3 = { - service_name = "sonarr" - image_name = "linuxserver/sonarr:4.0.14.2939-ls281" - username = "default_username" - password = "SonarrPassword1!" - domain_name = "sonarr.mallett.family" - service_port = 8989 - ip_address = "192.168.5.15" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - } - } } \ No newline at end of file diff --git a/services/arrr/main.tf b/services/arrr/main.tf index 4ab4506..53fa805 100644 --- a/services/arrr/main.tf +++ b/services/arrr/main.tf @@ -1,5 +1,5 @@ module "service_docker" { - for_each = local.services + for_each = var.services source = "../../modules/docker" container_name = each.value.service_name @@ -12,7 +12,7 @@ module "service_docker" { module "service_dns" { source = "../../modules/dns" - for_each = local.services + for_each = var.services internal_only = true service_port = local.authentik_port @@ -30,7 +30,7 @@ module "service_dns" { module "authentication" { source = "../../modules/proxy_auth" - for_each = local.services + for_each = var.services internal_host = "http://${each.value.ip_address}:${each.value.service_port}" external_host = each.value.domain_name diff --git a/services/arrr/variables.tf b/services/arrr/variables.tf index 395157f..129120b 100644 --- a/services/arrr/variables.tf +++ b/services/arrr/variables.tf @@ -21,4 +21,18 @@ variable "access_list_id" { variable "public_facing_ip" { type = string description = "" +} + +variable "services" { + type = map(object({ + service_name = string + image_name = string + username = string + password = string + domain_name = string + service_port = number + ip_address = string + env = list(string) + })) + description = "Map of services to be deployed with their configurations" } \ No newline at end of file From 4b5c621640f03faa81c6e3cd975649cfb53ec51a Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 04:18:49 -0500 Subject: [PATCH 12/29] Update TF config --- .github/workflows/run-plan.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 2cff9ce..f7b6973 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -15,11 +15,21 @@ jobs: permissions: contents: read pull-requests: write + env: + TF_WORKSPACE: ${{ secrets.TF_WORKSPACE }} + CONFIG_DIRECTORY: "./" steps: - name: Checkout Code uses: actions/checkout@v4 + - name: Upload Configuration + uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.3.2 + id: upload-configuration + with: + workspace: ${{ env.TF_WORKSPACE }} + directory: ${{ env.CONFIG_DIRECTORY }} + - name: Trigger Terraform Cloud Plan uses: hashicorp/tfc-workflows-github/actions/create-run@v1.3.2 id: create-run From 799be8c2c32228a5950de0c302209c37a6e41681 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 04:19:52 -0500 Subject: [PATCH 13/29] Update TF config --- .github/workflows/run-plan.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index f7b6973..24cffcf 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -29,6 +29,8 @@ jobs: with: workspace: ${{ env.TF_WORKSPACE }} directory: ${{ env.CONFIG_DIRECTORY }} + token: ${{ secrets.TF_API_TOKEN }} + organization: ${{ secrets.TF_ORGANIZATION }} - name: Trigger Terraform Cloud Plan uses: hashicorp/tfc-workflows-github/actions/create-run@v1.3.2 From 7246793bb1acae1b2dc0494f6654bcea217ea609 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 04:22:44 -0500 Subject: [PATCH 14/29] Update configuration for apply --- .github/workflows/run-apply.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-apply.yml b/.github/workflows/run-apply.yml index db189ef..7fa3bf0 100644 --- a/.github/workflows/run-apply.yml +++ b/.github/workflows/run-apply.yml @@ -22,11 +22,23 @@ jobs: permissions: contents: read pull-requests: write - + env: + TF_WORKSPACE: ${{ secrets.TF_WORKSPACE }} + CONFIG_DIRECTORY: "./" + steps: - name: Checkout Code uses: actions/checkout@v4 + - name: Upload Configuration + uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.3.2 + id: upload-configuration + with: + workspace: ${{ env.TF_WORKSPACE }} + directory: ${{ env.CONFIG_DIRECTORY }} + token: ${{ secrets.TF_API_TOKEN }} + organization: ${{ secrets.TF_ORGANIZATION }} + - name: Trigger Terraform Cloud Apply Plan uses: hashicorp/tfc-workflows-github/actions/create-run@v1.3.2 id: create-run From f7618b5461dc42b5526a8b547cee4974b56d8952 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Wed, 18 Jun 2025 04:38:24 -0500 Subject: [PATCH 15/29] Removed service_config.tf and added service level ReadMe.md --- services/arrr/ReadMe.md | 20 +++++++++ services/arrr/service_config.tf | 74 --------------------------------- 2 files changed, 20 insertions(+), 74 deletions(-) create mode 100644 services/arrr/ReadMe.md delete mode 100644 services/arrr/service_config.tf diff --git a/services/arrr/ReadMe.md b/services/arrr/ReadMe.md new file mode 100644 index 0000000..d4b6972 --- /dev/null +++ b/services/arrr/ReadMe.md @@ -0,0 +1,20 @@ +# ARRR Stack Setup +This folder contains the Terraform configuration files responsible for deploying and managing the "Arrr" stack of services. This setup leverages Docker containers, secured by an Nginx reverse proxy with SSL, and integrated with Authentik for authentication. Local DNS entries are also configured to simplify access from a local network. + +## Services Deployed +The following services are part of this deployment: + +Prowlarr +Sonarr +Radarr +Lidarr +Readarr + +Each service runs as a Docker container with PUID=1000, PGID=1000, and TZ=America/Chicago environment variables set. + +## Terraform Modules Used +This setup is orchestrated using Terraform and relies on the following local modules: + +**Authentik Proxy Authentication:** Configured via ../../modules/proxy_auth +**Nginx Proxy, SSL, and Local DNS:** Managed by ../../modules/dns +**Docker Containers:** Defined in ../../modules/docker \ No newline at end of file diff --git a/services/arrr/service_config.tf b/services/arrr/service_config.tf deleted file mode 100644 index 7b456b1..0000000 --- a/services/arrr/service_config.tf +++ /dev/null @@ -1,74 +0,0 @@ -locals{ - services = { - 1 = { - service_name = "prowlarr" - image_name = "linuxserver/prowlarr:1.37.0" - username = "default_username" - password = "ProwlarrPassword1!" - domain_name = "prowlarr.mallett.family" - service_port = 9696 - ip_address = "192.168.5.13" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 2 = { - service_name = "radarr" - image_name = "linuxserver/radarr:5.26.2" - username = "default_username" - password = "RadarrPassword1!" - domain_name = "radarr.mallett.family" - service_port = 7878 - ip_address = "192.168.5.14" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 3 = { - service_name = "sonarr" - image_name = "linuxserver/sonarr:4.0.14.2939-ls281" - username = "default_username" - password = "SonarrPassword1!" - domain_name = "sonarr.mallett.family" - service_port = 8989 - ip_address = "192.168.5.15" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 4 = { - service_name = "lidarr" - image_name = "linuxserver/lidarr:2.12.4" - username = "default_username" - password = "LidarrPassword1!" - domain_name = "lidarr.mallett.family" - service_port = 8686 - ip_address = "192.168.5.16" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - }, - 5 = { - service_name = "readarr" - image_name = "linuxserver/readarr:0.4.17-develop" - username = "default_username" - password = "ReadarrPassword1!" - domain_name = "readarr.mallett.family" - service_port = 8787 - ip_address = "192.168.5.17" - env = [ - "PUID=1000", - "PGID=1000", - "TZ=America/Chicago", - ] - } - } -} \ No newline at end of file From f2a67a93f98010cefe17e029ff1df377afd9cd2a Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Fri, 20 Jun 2025 07:40:22 -0500 Subject: [PATCH 16/29] Move service to reusable module --- modules/docker/main.tf | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/docker/main.tf b/modules/docker/main.tf index 1b19d5a..72d6b06 100644 --- a/modules/docker/main.tf +++ b/modules/docker/main.tf @@ -12,34 +12,34 @@ resource "docker_image" "main" { } resource "docker_container" "container" { - lifecycle { + lifecycle { ignore_changes = [ # Ignore changes to log_opts since unraid manages that log_opts, ] } - name = var.container_name - image = docker_image.main.image_id - env = var.environment_vars + name = var.container_name + image = docker_image.main.image_id + env = var.environment_vars network_mode = local.effective_network_mode - user = var.container_user - restart = var.container_restart - dns = local.effective_network_mode != "host" ? var.container_dns_servers : null - privileged = var.container_privileged_mode + user = var.container_user + restart = var.container_restart + dns = local.effective_network_mode != "host" ? var.container_dns_servers : null + privileged = var.container_privileged_mode dynamic "networks_advanced" { for_each = var.attach_to_br1 && local.effective_network_mode != "host" ? [1] : [] content { name = data.docker_network.main_host.id - ipv4_address = var.br1_ipv4_addr + ipv4_address = var.br1_ipv4_addr } } - dynamic "networks_advanced" { + dynamic "networks_advanced" { for_each = var.attach_to_br0 && local.effective_network_mode != "host" ? [1] : [] content { name = data.docker_network.secondary_host.id - ipv4_address = var.br0_ipv4_addr + ipv4_address = var.br0_ipv4_addr } } @@ -55,12 +55,12 @@ resource "docker_container" "container" { dynamic "volumes" { for_each = var.container_volumes - iterator = vol_iterator + iterator = vol_iterator content { container_path = vol_iterator.value.container_path read_only = vol_iterator.value.read_only - host_path = vol_iterator.value.host_path - volume_name = vol_iterator.value.volume_name != null ? ( + host_path = vol_iterator.value.host_path + volume_name = vol_iterator.value.volume_name != null ? ( coalesce(vol_iterator.value.manage_volume_lifecycle, true) ? docker_volume.managed_volumes[vol_iterator.key].name : # References module-created volume vol_iterator.value.volume_name # Uses pre-existing volume name directly From c96f095dc1570ed6186b51451e1490346a1007b0 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Fri, 20 Jun 2025 07:50:34 -0500 Subject: [PATCH 17/29] Update config and converted arrrs to proxy service stack --- config/services.yaml | 2 + config.yaml => config/stacks.yaml | 8 +-- config/system.yaml | 7 +++ main.tf | 4 +- .../proxy_service_stack}/ReadMe.md | 0 modules/proxy_service_stack/main.tf | 49 ++++++++++++++++ modules/proxy_service_stack/outputs.tf | 3 + modules/proxy_service_stack/variables.tf | 58 +++++++++++++++++++ services.tf | 8 ++- services/arrr/locals.tf | 6 -- services/arrr/main.tf | 49 ---------------- services/arrr/variables.tf | 38 ------------ 12 files changed, 129 insertions(+), 103 deletions(-) create mode 100644 config/services.yaml rename config.yaml => config/stacks.yaml (94%) create mode 100644 config/system.yaml rename {services/arrr => modules/proxy_service_stack}/ReadMe.md (100%) create mode 100644 modules/proxy_service_stack/main.tf create mode 100644 modules/proxy_service_stack/outputs.tf create mode 100644 modules/proxy_service_stack/variables.tf delete mode 100644 services/arrr/locals.tf delete mode 100644 services/arrr/main.tf delete mode 100644 services/arrr/variables.tf diff --git a/config/services.yaml b/config/services.yaml new file mode 100644 index 0000000..ef00236 --- /dev/null +++ b/config/services.yaml @@ -0,0 +1,2 @@ +authentik: + admin-user: "" \ No newline at end of file diff --git a/config.yaml b/config/stacks.yaml similarity index 94% rename from config.yaml rename to config/stacks.yaml index 3e9fbc0..7c16d0d 100644 --- a/config.yaml +++ b/config/stacks.yaml @@ -1,5 +1,3 @@ -system: -stacks: arr_services: prowlarr: service_name: "prowlarr" @@ -60,8 +58,4 @@ stacks: env: - "PUID=1000" - "PGID=1000" - - "TZ=America/Chicago" - -services: - authentik: - admin-user: "" \ No newline at end of file + - "TZ=America/Chicago" \ No newline at end of file diff --git a/config/system.yaml b/config/system.yaml new file mode 100644 index 0000000..1746e1f --- /dev/null +++ b/config/system.yaml @@ -0,0 +1,7 @@ +authentik: + ip_address: "192.168.4.55" + port: 9000 +proxy_ip: "192.168.4.2" +zones: + dcapi: "dcapi.app" + mallett: "mallett.family" \ No newline at end of file diff --git a/main.tf b/main.tf index ec76f42..a540afe 100644 --- a/main.tf +++ b/main.tf @@ -41,5 +41,7 @@ module "nginx_conf" { } locals { - config = yamldecode(file("${path.module}/config.yaml")) + system = yamldecode(file("${path.module}/config/system.yaml")) + stacks = yamldecode(file("${path.module}/config/stacks.yaml")) + services = yamldecode(file("${path.module}/config/services.yaml")) } \ No newline at end of file diff --git a/services/arrr/ReadMe.md b/modules/proxy_service_stack/ReadMe.md similarity index 100% rename from services/arrr/ReadMe.md rename to modules/proxy_service_stack/ReadMe.md diff --git a/modules/proxy_service_stack/main.tf b/modules/proxy_service_stack/main.tf new file mode 100644 index 0000000..547f462 --- /dev/null +++ b/modules/proxy_service_stack/main.tf @@ -0,0 +1,49 @@ +module "service_docker" { + for_each = var.services + source = "../docker" + + container_name = each.value.service_name + container_image = each.value.image_name + attach_to_br0 = false + attach_to_br1 = true + br1_ipv4_addr = each.value.ip_address + environment_vars = each.value.env +} + +module "service_dns" { + source = "../dns" + for_each = var.services + + internal_only = true + service_port = var.authentik_port + zone_name = var.zone_name + domain_name = each.value.domain_name + access_list_id = var.access_list_id + internal_host_ipv4 = var.proxy_ip + service_ipv4 = var.authentik_ip + admin_email = var.admin_email + dns_cloudflare_api_token = var.cloudflare_token + external_host_ipv4 = var.public_facing_ip +} + +//Note: This will still require manually navigating to the service and setting up the credentials on the first run + +module "authentication" { + source = "../proxy_auth" + for_each = var.services + + internal_host = "http://${each.value.ip_address}:${each.value.service_port}" + external_host = each.value.domain_name + name = each.value.service_name + username_attribute = "${each.value.service_name}_username" + password_attribute = "${each.value.service_name}_password" + create_access_group = true + access_group_name = "tf_${each.value.service_name}" + user_to_add_to_access_group = var.admin_username + access_group_attributes = jsonencode( + { + "${each.value.service_name}_username" : each.value.username, + "${each.value.service_name}_password" : each.value.password + } + ) +} \ No newline at end of file diff --git a/modules/proxy_service_stack/outputs.tf b/modules/proxy_service_stack/outputs.tf new file mode 100644 index 0000000..32f1867 --- /dev/null +++ b/modules/proxy_service_stack/outputs.tf @@ -0,0 +1,3 @@ +output "container_names" { + value = [for docker in module.service_docker : docker.name] +} \ No newline at end of file diff --git a/modules/proxy_service_stack/variables.tf b/modules/proxy_service_stack/variables.tf new file mode 100644 index 0000000..388e785 --- /dev/null +++ b/modules/proxy_service_stack/variables.tf @@ -0,0 +1,58 @@ +variable "cloudflare_token" { + type = string + description = "Cloudflare API token" +} + +variable "admin_email" { + type = string + description = "Network admin email address" +} + +variable "admin_username" { + type = string + description = "Network admin username" +} + +variable "access_list_id" { + type = string + description = "" +} + +variable "public_facing_ip" { + type = string + description = "" +} + +variable "services" { + type = map(object({ + service_name = string + image_name = string + username = string + password = string + domain_name = string + service_port = number + ip_address = string + env = list(string) + })) + description = "Map of services to be deployed with their configurations" +} + +variable "zone_name" { + type = string + description = "DNS zone name for the services" +} + +variable "proxy_ip" { + type = string + description = "IP address of the proxy server" +} + +variable "authentik_ip" { + type = string + description = "IP address of the Authentik service" +} + +variable "authentik_port" { + type = number + description = "Port on which the Authentik service is running" +} \ No newline at end of file diff --git a/services.tf b/services.tf index 516e5ee..0a76dfc 100644 --- a/services.tf +++ b/services.tf @@ -1,12 +1,16 @@ module "arr_services" { - source = "./services/arrr" + source = "./modules/proxy_service_stack" admin_email = var.network_admin_email admin_username = var.network_admin_username cloudflare_token = var.cloudflare_api_token access_list_id = module.nginx_conf.internal_access_list_id public_facing_ip = var.public_facing_ip - services = local.config.stacks.arr_services + services = local.stacks.arr_services + zone_name = local.system.zones.mallett + proxy_ip = local.system.proxy_ip + authentik_ip = local.system.authentik.ip_address + authentik_port = local.system.authentik.port } /* diff --git a/services/arrr/locals.tf b/services/arrr/locals.tf deleted file mode 100644 index 2cda7ed..0000000 --- a/services/arrr/locals.tf +++ /dev/null @@ -1,6 +0,0 @@ -locals { - zone_name = "mallett.family" - proxy_ip = "192.168.4.2" - authentik_ip = "192.168.4.55" - authentik_port = 9000 -} \ No newline at end of file diff --git a/services/arrr/main.tf b/services/arrr/main.tf deleted file mode 100644 index 53fa805..0000000 --- a/services/arrr/main.tf +++ /dev/null @@ -1,49 +0,0 @@ -module "service_docker" { - for_each = var.services - source = "../../modules/docker" - - container_name = each.value.service_name - container_image = each.value.image_name - attach_to_br0 = false - attach_to_br1 = true - br1_ipv4_addr = each.value.ip_address - environment_vars = each.value.env -} - -module "service_dns" { - source = "../../modules/dns" - for_each = var.services - - internal_only = true - service_port = local.authentik_port - zone_name = local.zone_name - domain_name = each.value.domain_name - access_list_id = var.access_list_id - internal_host_ipv4 = local.proxy_ip - service_ipv4 = local.authentik_ip - admin_email = var.admin_email - dns_cloudflare_api_token = var.cloudflare_token - external_host_ipv4 = var.public_facing_ip -} - -//Note: This will still require manually navigating to the service and setting up the credentials on the first run - -module "authentication" { - source = "../../modules/proxy_auth" - for_each = var.services - - internal_host = "http://${each.value.ip_address}:${each.value.service_port}" - external_host = each.value.domain_name - name = each.value.service_name - username_attribute = "${each.value.service_name}_username" - password_attribute = "${each.value.service_name}_password" - create_access_group = true - access_group_name = "tf_${each.value.service_name}" - user_to_add_to_access_group = var.admin_username - access_group_attributes = jsonencode( - { - "${each.value.service_name}_username": each.value.username, - "${each.value.service_name}_password": each.value.password - } - ) -} \ No newline at end of file diff --git a/services/arrr/variables.tf b/services/arrr/variables.tf deleted file mode 100644 index 129120b..0000000 --- a/services/arrr/variables.tf +++ /dev/null @@ -1,38 +0,0 @@ -variable "cloudflare_token" { - type = string - description = "Cloudflare API token" -} - -variable "admin_email" { - type = string - description = "Network admin email address" -} - -variable "admin_username" { - type = string - description = "Network admin username" -} - -variable "access_list_id" { - type = string - description = "" -} - -variable "public_facing_ip" { - type = string - description = "" -} - -variable "services" { - type = map(object({ - service_name = string - image_name = string - username = string - password = string - domain_name = string - service_port = number - ip_address = string - env = list(string) - })) - description = "Map of services to be deployed with their configurations" -} \ No newline at end of file From 4329b32eed47c4952a8ac8252f16170bb706e667 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Fri, 20 Jun 2025 07:54:25 -0500 Subject: [PATCH 18/29] Moved all TF into terraform directory --- {services => terraform}/grafana/locals.tf | 0 {services => terraform}/grafana/main.tf | 0 {services => terraform}/grafana/variables.tf | 0 main.tf => terraform/main.tf | 6 +++--- {modules => terraform/modules}/authentik/directory.tf | 0 {modules => terraform/modules}/authentik/main.tf | 0 {modules => terraform/modules}/authentik/providers.tf | 0 {modules => terraform/modules}/authentik/variables.tf | 0 {modules => terraform/modules}/dns/cloudflare.tf | 0 {modules => terraform/modules}/dns/nginxproxy.tf | 0 {modules => terraform/modules}/dns/outputs.tf | 0 {modules => terraform/modules}/dns/providers.tf | 0 {modules => terraform/modules}/dns/technitium.tf | 0 {modules => terraform/modules}/dns/variables.tf | 0 {modules => terraform/modules}/docker/data.tf | 0 {modules => terraform/modules}/docker/locals.tf | 0 {modules => terraform/modules}/docker/main.tf | 0 {modules => terraform/modules}/docker/outputs.tf | 0 {modules => terraform/modules}/docker/variables.tf | 0 {modules => terraform/modules}/nginx_config/main.tf | 0 {modules => terraform/modules}/nginx_config/outputs.tf | 0 {modules => terraform/modules}/nginx_config/providers.tf | 0 {modules => terraform/modules}/oauth_auth/locals.tf | 0 {modules => terraform/modules}/oauth_auth/main.tf | 0 {modules => terraform/modules}/oauth_auth/outputs.tf | 0 {modules => terraform/modules}/oauth_auth/policy.tf | 0 {modules => terraform/modules}/oauth_auth/service.tf | 0 {modules => terraform/modules}/oauth_auth/variables.tf | 0 {modules => terraform/modules}/proxy_auth/main.tf | 0 {modules => terraform/modules}/proxy_auth/outputs.tf | 0 {modules => terraform/modules}/proxy_auth/policy.tf | 0 {modules => terraform/modules}/proxy_auth/service.tf | 0 {modules => terraform/modules}/proxy_auth/variables.tf | 0 .../modules}/proxy_service_stack/ReadMe.md | 0 {modules => terraform/modules}/proxy_service_stack/main.tf | 0 .../modules}/proxy_service_stack/outputs.tf | 0 .../modules}/proxy_service_stack/variables.tf | 0 providers.tf => terraform/providers.tf | 0 services.tf => terraform/services.tf | 0 variables.tf => terraform/variables.tf | 0 40 files changed, 3 insertions(+), 3 deletions(-) rename {services => terraform}/grafana/locals.tf (100%) rename {services => terraform}/grafana/main.tf (100%) rename {services => terraform}/grafana/variables.tf (100%) rename main.tf => terraform/main.tf (75%) rename {modules => terraform/modules}/authentik/directory.tf (100%) rename {modules => terraform/modules}/authentik/main.tf (100%) rename {modules => terraform/modules}/authentik/providers.tf (100%) rename {modules => terraform/modules}/authentik/variables.tf (100%) rename {modules => terraform/modules}/dns/cloudflare.tf (100%) rename {modules => terraform/modules}/dns/nginxproxy.tf (100%) rename {modules => terraform/modules}/dns/outputs.tf (100%) rename {modules => terraform/modules}/dns/providers.tf (100%) rename {modules => terraform/modules}/dns/technitium.tf (100%) rename {modules => terraform/modules}/dns/variables.tf (100%) rename {modules => terraform/modules}/docker/data.tf (100%) rename {modules => terraform/modules}/docker/locals.tf (100%) rename {modules => terraform/modules}/docker/main.tf (100%) rename {modules => terraform/modules}/docker/outputs.tf (100%) rename {modules => terraform/modules}/docker/variables.tf (100%) rename {modules => terraform/modules}/nginx_config/main.tf (100%) rename {modules => terraform/modules}/nginx_config/outputs.tf (100%) rename {modules => terraform/modules}/nginx_config/providers.tf (100%) rename {modules => terraform/modules}/oauth_auth/locals.tf (100%) rename {modules => terraform/modules}/oauth_auth/main.tf (100%) rename {modules => terraform/modules}/oauth_auth/outputs.tf (100%) rename {modules => terraform/modules}/oauth_auth/policy.tf (100%) rename {modules => terraform/modules}/oauth_auth/service.tf (100%) rename {modules => terraform/modules}/oauth_auth/variables.tf (100%) rename {modules => terraform/modules}/proxy_auth/main.tf (100%) rename {modules => terraform/modules}/proxy_auth/outputs.tf (100%) rename {modules => terraform/modules}/proxy_auth/policy.tf (100%) rename {modules => terraform/modules}/proxy_auth/service.tf (100%) rename {modules => terraform/modules}/proxy_auth/variables.tf (100%) rename {modules => terraform/modules}/proxy_service_stack/ReadMe.md (100%) rename {modules => terraform/modules}/proxy_service_stack/main.tf (100%) rename {modules => terraform/modules}/proxy_service_stack/outputs.tf (100%) rename {modules => terraform/modules}/proxy_service_stack/variables.tf (100%) rename providers.tf => terraform/providers.tf (100%) rename services.tf => terraform/services.tf (100%) rename variables.tf => terraform/variables.tf (100%) diff --git a/services/grafana/locals.tf b/terraform/grafana/locals.tf similarity index 100% rename from services/grafana/locals.tf rename to terraform/grafana/locals.tf diff --git a/services/grafana/main.tf b/terraform/grafana/main.tf similarity index 100% rename from services/grafana/main.tf rename to terraform/grafana/main.tf diff --git a/services/grafana/variables.tf b/terraform/grafana/variables.tf similarity index 100% rename from services/grafana/variables.tf rename to terraform/grafana/variables.tf diff --git a/main.tf b/terraform/main.tf similarity index 75% rename from main.tf rename to terraform/main.tf index a540afe..1034fc7 100644 --- a/main.tf +++ b/terraform/main.tf @@ -41,7 +41,7 @@ module "nginx_conf" { } locals { - system = yamldecode(file("${path.module}/config/system.yaml")) - stacks = yamldecode(file("${path.module}/config/stacks.yaml")) - services = yamldecode(file("${path.module}/config/services.yaml")) + system = yamldecode(file("${path.module}/../config/system.yaml")) + stacks = yamldecode(file("${path.module}/../config/stacks.yaml")) + services = yamldecode(file("${path.module}/../config/services.yaml")) } \ No newline at end of file diff --git a/modules/authentik/directory.tf b/terraform/modules/authentik/directory.tf similarity index 100% rename from modules/authentik/directory.tf rename to terraform/modules/authentik/directory.tf diff --git a/modules/authentik/main.tf b/terraform/modules/authentik/main.tf similarity index 100% rename from modules/authentik/main.tf rename to terraform/modules/authentik/main.tf diff --git a/modules/authentik/providers.tf b/terraform/modules/authentik/providers.tf similarity index 100% rename from modules/authentik/providers.tf rename to terraform/modules/authentik/providers.tf diff --git a/modules/authentik/variables.tf b/terraform/modules/authentik/variables.tf similarity index 100% rename from modules/authentik/variables.tf rename to terraform/modules/authentik/variables.tf diff --git a/modules/dns/cloudflare.tf b/terraform/modules/dns/cloudflare.tf similarity index 100% rename from modules/dns/cloudflare.tf rename to terraform/modules/dns/cloudflare.tf diff --git a/modules/dns/nginxproxy.tf b/terraform/modules/dns/nginxproxy.tf similarity index 100% rename from modules/dns/nginxproxy.tf rename to terraform/modules/dns/nginxproxy.tf diff --git a/modules/dns/outputs.tf b/terraform/modules/dns/outputs.tf similarity index 100% rename from modules/dns/outputs.tf rename to terraform/modules/dns/outputs.tf diff --git a/modules/dns/providers.tf b/terraform/modules/dns/providers.tf similarity index 100% rename from modules/dns/providers.tf rename to terraform/modules/dns/providers.tf diff --git a/modules/dns/technitium.tf b/terraform/modules/dns/technitium.tf similarity index 100% rename from modules/dns/technitium.tf rename to terraform/modules/dns/technitium.tf diff --git a/modules/dns/variables.tf b/terraform/modules/dns/variables.tf similarity index 100% rename from modules/dns/variables.tf rename to terraform/modules/dns/variables.tf diff --git a/modules/docker/data.tf b/terraform/modules/docker/data.tf similarity index 100% rename from modules/docker/data.tf rename to terraform/modules/docker/data.tf diff --git a/modules/docker/locals.tf b/terraform/modules/docker/locals.tf similarity index 100% rename from modules/docker/locals.tf rename to terraform/modules/docker/locals.tf diff --git a/modules/docker/main.tf b/terraform/modules/docker/main.tf similarity index 100% rename from modules/docker/main.tf rename to terraform/modules/docker/main.tf diff --git a/modules/docker/outputs.tf b/terraform/modules/docker/outputs.tf similarity index 100% rename from modules/docker/outputs.tf rename to terraform/modules/docker/outputs.tf diff --git a/modules/docker/variables.tf b/terraform/modules/docker/variables.tf similarity index 100% rename from modules/docker/variables.tf rename to terraform/modules/docker/variables.tf diff --git a/modules/nginx_config/main.tf b/terraform/modules/nginx_config/main.tf similarity index 100% rename from modules/nginx_config/main.tf rename to terraform/modules/nginx_config/main.tf diff --git a/modules/nginx_config/outputs.tf b/terraform/modules/nginx_config/outputs.tf similarity index 100% rename from modules/nginx_config/outputs.tf rename to terraform/modules/nginx_config/outputs.tf diff --git a/modules/nginx_config/providers.tf b/terraform/modules/nginx_config/providers.tf similarity index 100% rename from modules/nginx_config/providers.tf rename to terraform/modules/nginx_config/providers.tf diff --git a/modules/oauth_auth/locals.tf b/terraform/modules/oauth_auth/locals.tf similarity index 100% rename from modules/oauth_auth/locals.tf rename to terraform/modules/oauth_auth/locals.tf diff --git a/modules/oauth_auth/main.tf b/terraform/modules/oauth_auth/main.tf similarity index 100% rename from modules/oauth_auth/main.tf rename to terraform/modules/oauth_auth/main.tf diff --git a/modules/oauth_auth/outputs.tf b/terraform/modules/oauth_auth/outputs.tf similarity index 100% rename from modules/oauth_auth/outputs.tf rename to terraform/modules/oauth_auth/outputs.tf diff --git a/modules/oauth_auth/policy.tf b/terraform/modules/oauth_auth/policy.tf similarity index 100% rename from modules/oauth_auth/policy.tf rename to terraform/modules/oauth_auth/policy.tf diff --git a/modules/oauth_auth/service.tf b/terraform/modules/oauth_auth/service.tf similarity index 100% rename from modules/oauth_auth/service.tf rename to terraform/modules/oauth_auth/service.tf diff --git a/modules/oauth_auth/variables.tf b/terraform/modules/oauth_auth/variables.tf similarity index 100% rename from modules/oauth_auth/variables.tf rename to terraform/modules/oauth_auth/variables.tf diff --git a/modules/proxy_auth/main.tf b/terraform/modules/proxy_auth/main.tf similarity index 100% rename from modules/proxy_auth/main.tf rename to terraform/modules/proxy_auth/main.tf diff --git a/modules/proxy_auth/outputs.tf b/terraform/modules/proxy_auth/outputs.tf similarity index 100% rename from modules/proxy_auth/outputs.tf rename to terraform/modules/proxy_auth/outputs.tf diff --git a/modules/proxy_auth/policy.tf b/terraform/modules/proxy_auth/policy.tf similarity index 100% rename from modules/proxy_auth/policy.tf rename to terraform/modules/proxy_auth/policy.tf diff --git a/modules/proxy_auth/service.tf b/terraform/modules/proxy_auth/service.tf similarity index 100% rename from modules/proxy_auth/service.tf rename to terraform/modules/proxy_auth/service.tf diff --git a/modules/proxy_auth/variables.tf b/terraform/modules/proxy_auth/variables.tf similarity index 100% rename from modules/proxy_auth/variables.tf rename to terraform/modules/proxy_auth/variables.tf diff --git a/modules/proxy_service_stack/ReadMe.md b/terraform/modules/proxy_service_stack/ReadMe.md similarity index 100% rename from modules/proxy_service_stack/ReadMe.md rename to terraform/modules/proxy_service_stack/ReadMe.md diff --git a/modules/proxy_service_stack/main.tf b/terraform/modules/proxy_service_stack/main.tf similarity index 100% rename from modules/proxy_service_stack/main.tf rename to terraform/modules/proxy_service_stack/main.tf diff --git a/modules/proxy_service_stack/outputs.tf b/terraform/modules/proxy_service_stack/outputs.tf similarity index 100% rename from modules/proxy_service_stack/outputs.tf rename to terraform/modules/proxy_service_stack/outputs.tf diff --git a/modules/proxy_service_stack/variables.tf b/terraform/modules/proxy_service_stack/variables.tf similarity index 100% rename from modules/proxy_service_stack/variables.tf rename to terraform/modules/proxy_service_stack/variables.tf diff --git a/providers.tf b/terraform/providers.tf similarity index 100% rename from providers.tf rename to terraform/providers.tf diff --git a/services.tf b/terraform/services.tf similarity index 100% rename from services.tf rename to terraform/services.tf diff --git a/variables.tf b/terraform/variables.tf similarity index 100% rename from variables.tf rename to terraform/variables.tf From e23d0386c7259c6d558e90da5df639668cce3fc0 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Fri, 20 Jun 2025 08:05:08 -0500 Subject: [PATCH 19/29] Add TF version file for use with tfenv --- terraform/.terraform-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 terraform/.terraform-version diff --git a/terraform/.terraform-version b/terraform/.terraform-version new file mode 100644 index 0000000..32bd932 --- /dev/null +++ b/terraform/.terraform-version @@ -0,0 +1 @@ +1.12.0 \ No newline at end of file From 5f36732f4a85051c5edbe5d8df63564d3783fbed Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 06:50:12 -0500 Subject: [PATCH 20/29] Update stacks and fix issues --- config/services.yaml | 8 +- config/stacks.yaml | 109 ++++++++---------- terraform/modules/docker/main.tf | 12 ++ terraform/modules/docker/variables.tf | 6 + terraform/modules/proxy_service_stack/main.tf | 9 +- .../modules/proxy_service_stack/variables.tf | 28 +++-- terraform/providers.tf | 10 ++ terraform/services.tf | 14 ++- 8 files changed, 118 insertions(+), 78 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index ef00236..12fdc47 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -1,2 +1,8 @@ authentik: - admin-user: "" \ No newline at end of file + admin-user: "" +flaresolverr: + service_name: "flaresolverr" + ip_address: "192.168.5.27" + image_name: "flaresolverr/flaresolverr:v3.3.25" + env: + - "TZ=America/Chicago" \ No newline at end of file diff --git a/config/stacks.yaml b/config/stacks.yaml index 7c16d0d..65f0697 100644 --- a/config/stacks.yaml +++ b/config/stacks.yaml @@ -1,61 +1,50 @@ arr_services: - prowlarr: - service_name: "prowlarr" - service_port: 9696 - image_name: "linuxserver/prowlarr:1.37.0" - username: "default_username" - password: "ProwlarrPassword1!" - domain_name: "prowlarr.dcapi.app" - ip_address: "192.168.5.13" - env: - - "PUID=1000" - - "PGID=1000" - - "TZ=America/Chicago" - sonarr: - service_name: "sonarr" - service_port: 8989 - image_name: "linuxserver/sonarr:4.0.14.2939-ls281" - username: "default_username" - password: "SonarrPassword1!" - domain_name: "sonarr.dcapi.app" - ip_address: "" - env: - - "PUID=1000" - - "PGID=1000" - - "TZ=America/Chicago" - radarr: - service_name: "radarr" - service_port: 7878 - image_name: "linuxserver/radarr:5.26.2" - username: "default_username" - password: "RadarrPassword1!" - domain_name: "radarr.dcapi.app" - ip_address: "" - env: - - "PUID=1000" - - "PGID=1000" - - "TZ=America/Chicago" - lidarr: #Need to fix - service_name: "lidarr" - service_port: 8686 - image_name: "linuxserver/lidarr:1.0.2.1495-ls108" - username: "default_username" - password: "LidarrPassword1!" - domain_name: "lidarr.dcapi.app" - ip_address: "" - env: - - "PUID=1000" - - "PGID=1000" - - "TZ=America/Chicago" - readarr: #Need to fix - service_name: "readarr" - service_port: 8787 - image_name: "linuxserver/readarr:1.0.2.1495-ls108" - username: "default_username" - password: "ReadarrPassword1!" - domain_name: "readarr.dcapi.app" - ip_address: "" - env: - - "PUID=1000" - - "PGID=1000" - - "TZ=America/Chicago" \ No newline at end of file + env: + - "PUID=1000" + - "PGID=100" + - "TZ=America/Chicago" + mounts: + - "/etc/localtime:/etc/localtime:ro" + services: + prowlarr: + service_name: "prowlarr" + service_port: 9696 + image_name: "linuxserver/prowlarr:1.37.0" + username: "default_username" + password: "ProwlarrPassword1!" + domain_name: "prowlarr.dcapi.app" + ip_address: "192.168.5.22" + mounts: + - "/var/run/docker.sock:/var/run/docker.sock" + sonarr: + service_name: "sonarr" + service_port: 8989 + image_name: "linuxserver/sonarr:4.0.14.2939-ls281" + username: "default_username" + password: "SonarrPassword1!" + domain_name: "sonarr.dcapi.app" + ip_address: "192.168.5.23" + radarr: + service_name: "radarr" + service_port: 7878 + image_name: "linuxserver/radarr:5.26.2" + username: "default_username" + password: "RadarrPassword1!" + domain_name: "radarr.dcapi.app" + ip_address: "192.168.5.24" + lidarr: + service_name: "lidarr" + service_port: 8686 + image_name: "linuxserver/lidarr:1.0.2.1495-ls108" + username: "default_username" + password: "LidarrPassword1!" + domain_name: "lidarr.dcapi.app" + ip_address: "192.168.5.25" + readarr: + service_name: "readarr" + service_port: 8787 + image_name: "linuxserver/readarr:1.0.2.1495-ls108" + username: "default_username" + password: "ReadarrPassword1!" + domain_name: "readarr.dcapi.app" + ip_address: "192.168.5.26" \ No newline at end of file diff --git a/terraform/modules/docker/main.tf b/terraform/modules/docker/main.tf index 72d6b06..e76cb69 100644 --- a/terraform/modules/docker/main.tf +++ b/terraform/modules/docker/main.tf @@ -53,6 +53,18 @@ resource "docker_container" "container" { } } + dynamic "mounts" { + for_each = var.mounts + iterator = mount_iterator + + content { + target = split(":", mount_iterator.value)[1] + source = split(":", mount_iterator.value)[0] + read_only = strcontains(mount_iterator.value, ":ro") ? true : false + type = "bind" + } + } + dynamic "volumes" { for_each = var.container_volumes iterator = vol_iterator diff --git a/terraform/modules/docker/variables.tf b/terraform/modules/docker/variables.tf index 1c9eb36..909d064 100644 --- a/terraform/modules/docker/variables.tf +++ b/terraform/modules/docker/variables.tf @@ -129,4 +129,10 @@ variable "container_capabilities" { drop = optional(list(string)) }) default = {} # Defaults to an empty object, meaning 'add' and 'drop' will be null if not specified. +} + +variable "mounts"{ + description = "Specification for mounts to be added to containers created as part of the service." + type = list(string) + default = [] } \ No newline at end of file diff --git a/terraform/modules/proxy_service_stack/main.tf b/terraform/modules/proxy_service_stack/main.tf index 547f462..4647100 100644 --- a/terraform/modules/proxy_service_stack/main.tf +++ b/terraform/modules/proxy_service_stack/main.tf @@ -1,5 +1,5 @@ module "service_docker" { - for_each = var.services + for_each = var.stack.services source = "../docker" container_name = each.value.service_name @@ -7,12 +7,13 @@ module "service_docker" { attach_to_br0 = false attach_to_br1 = true br1_ipv4_addr = each.value.ip_address - environment_vars = each.value.env + environment_vars = concat(coalesce(var.stack.env, []), coalesce(each.value.env, [])) + mounts = concat(coalesce(var.stack.mounts, []), coalesce(each.value.mounts, [])) } module "service_dns" { source = "../dns" - for_each = var.services + for_each = var.stack.services internal_only = true service_port = var.authentik_port @@ -30,7 +31,7 @@ module "service_dns" { module "authentication" { source = "../proxy_auth" - for_each = var.services + for_each = var.stack.services internal_host = "http://${each.value.ip_address}:${each.value.service_port}" external_host = each.value.domain_name diff --git a/terraform/modules/proxy_service_stack/variables.tf b/terraform/modules/proxy_service_stack/variables.tf index 388e785..cb3ab31 100644 --- a/terraform/modules/proxy_service_stack/variables.tf +++ b/terraform/modules/proxy_service_stack/variables.tf @@ -23,17 +23,23 @@ variable "public_facing_ip" { description = "" } -variable "services" { - type = map(object({ - service_name = string - image_name = string - username = string - password = string - domain_name = string - service_port = number - ip_address = string - env = list(string) - })) +variable "stack" { + type = object({ + env = optional(list(string)) + mounts = optional(list(string)) + volumes = optional(list(string)) + services = map(object({ + service_name = string + image_name = string + username = optional(string, "") + password = optional(string, "") + domain_name = string + service_port = number + ip_address = string + env = optional(list(string)) + mounts = optional(list(string)) + })) + }) description = "Map of services to be deployed with their configurations" } diff --git a/terraform/providers.tf b/terraform/providers.tf index 13c6e45..2e23c2e 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -4,10 +4,17 @@ provider "nginxproxymanager" { password = var.nginx_proxy_pass } + provider "docker" { host = "unix:///var/run/docker.sock" } +/* +provider "docker" { + host = "ssh://root@192.168.1.41:22" + ssh_opts = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] +}*/ + provider "technitium" { url = var.technitium_host token = var.technitium_api_token @@ -15,4 +22,7 @@ provider "technitium" { provider "cloudflare" { api_token = var.cloudflare_api_token +} + +provider "authentik" { } \ No newline at end of file diff --git a/terraform/services.tf b/terraform/services.tf index 0a76dfc..46decd1 100644 --- a/terraform/services.tf +++ b/terraform/services.tf @@ -6,15 +6,25 @@ module "arr_services" { cloudflare_token = var.cloudflare_api_token access_list_id = module.nginx_conf.internal_access_list_id public_facing_ip = var.public_facing_ip - services = local.stacks.arr_services + stack = local.stacks.arr_services zone_name = local.system.zones.mallett proxy_ip = local.system.proxy_ip authentik_ip = local.system.authentik.ip_address authentik_port = local.system.authentik.port } -/* +module "flaresolverr_service" { + source = "./modules/docker" + + container_name = local.services.flaresolverr.service_name + container_image = local.services.flaresolverr.image_name + attach_to_br0 = false + attach_to_br1 = true + br1_ipv4_addr = local.services.flaresolverr.ip_address + environment_vars = local.services.flaresolverr.env +} +/* module "grafana_service" { source = "./services/grafana" From 554323706d777bca2d9a7872b3c64efa3ff34d2d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 08:14:37 -0500 Subject: [PATCH 21/29] Huge update to stack module and docker module, includes icons for unraid --- config/services.yaml | 33 ++++++++++++++++++- config/stacks.yaml | 14 ++++++-- terraform/modules/docker/locals.tf | 12 +++++++ terraform/modules/docker/main.tf | 11 ++++++- terraform/modules/docker/variables.tf | 19 +++++++++++ terraform/modules/proxy_service_stack/main.tf | 2 ++ .../modules/proxy_service_stack/variables.tf | 1 + terraform/services.tf | 12 +++++++ terraform/services/deluge-vpn/main.tf | 16 +++++++++ terraform/services/deluge-vpn/variables.tf | 27 +++++++++++++++ terraform/variables.tf | 12 +++++++ 11 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 terraform/services/deluge-vpn/main.tf create mode 100644 terraform/services/deluge-vpn/variables.tf diff --git a/config/services.yaml b/config/services.yaml index 12fdc47..613532c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -5,4 +5,35 @@ flaresolverr: ip_address: "192.168.5.27" image_name: "flaresolverr/flaresolverr:v3.3.25" env: - - "TZ=America/Chicago" \ No newline at end of file + - "TZ=America/Chicago" +deluge-vpn: + icon: "https://vectorified.com/images/deluge-icon-3.jpg" + service_name: "deluge-vpn" + ip_address: "192.168.5.28" + image_name: "binhex/arch-delugevpn:2.2" + service_port: 8112 + env: + - "TZ=America/Chicago" + - "ENABLE_SOCKS=no" + - "STRICT_PORT_FORWARD=yes" + - "DEBUG=false" + - "ENABLE_PRIVOXY=no" + - "PGID=100" + - "PUID=1000" + - "VPN_ENABLED=yes" + - "VPN_PROV=custom" + - "VPN_CLIENT=openvpn" + - "LAN_NETWORK=192.168.1.0/24" + capabilities: + add: + - "NET_ADMIN" + mounts: + - "/etc/localtime:/etc/localtime:ro" +requestrr: + service_name: "requestrr" + service_port: 4545 + image_name: "linuxserver/requestrr:2.1.2" + ip_address: "192.168.5.27" + mounts: + - "/mnt/user/Arr/requestrr-data:/config" + - "/etc/localtime:/etc/localtime:ro" \ No newline at end of file diff --git a/config/stacks.yaml b/config/stacks.yaml index 65f0697..556f19b 100644 --- a/config/stacks.yaml +++ b/config/stacks.yaml @@ -5,6 +5,8 @@ - "TZ=America/Chicago" mounts: - "/etc/localtime:/etc/localtime:ro" + - "/mnt/user/Media:/media" + - "/mnt/user/Downloads:/downloads" services: prowlarr: service_name: "prowlarr" @@ -15,7 +17,7 @@ domain_name: "prowlarr.dcapi.app" ip_address: "192.168.5.22" mounts: - - "/var/run/docker.sock:/var/run/docker.sock" + - "/mnt/user/Arr/prowlarr-data:/config" sonarr: service_name: "sonarr" service_port: 8989 @@ -24,6 +26,8 @@ password: "SonarrPassword1!" domain_name: "sonarr.dcapi.app" ip_address: "192.168.5.23" + mounts: + - "/mnt/user/Arr/sonarr-data:/config" radarr: service_name: "radarr" service_port: 7878 @@ -32,6 +36,8 @@ password: "RadarrPassword1!" domain_name: "radarr.dcapi.app" ip_address: "192.168.5.24" + mounts: + - "/mnt/user/Arr/radarr-data:/config" lidarr: service_name: "lidarr" service_port: 8686 @@ -40,6 +46,8 @@ password: "LidarrPassword1!" domain_name: "lidarr.dcapi.app" ip_address: "192.168.5.25" + mounts: + - "/mnt/user/Arr/lidarr-data:/config" readarr: service_name: "readarr" service_port: 8787 @@ -47,4 +55,6 @@ username: "default_username" password: "ReadarrPassword1!" domain_name: "readarr.dcapi.app" - ip_address: "192.168.5.26" \ No newline at end of file + ip_address: "192.168.5.26" + mounts: + - "/mnt/user/Arr/readarr-data:/config" \ No newline at end of file diff --git a/terraform/modules/docker/locals.tf b/terraform/modules/docker/locals.tf index 66a4ee7..782a85a 100644 --- a/terraform/modules/docker/locals.tf +++ b/terraform/modules/docker/locals.tf @@ -13,4 +13,16 @@ locals { ) ) ) + + all_labels = toset(concat( + tolist(coalesce(var.labels, [])), + var.icon != null ? [{ + label = "net.unraid.docker.icon", + value = var.icon + }] : [], + var.web_ui != null ? [{ + label = "net.unraid.docker.webui", + value = var.web_ui + }] : [] + )) } \ No newline at end of file diff --git a/terraform/modules/docker/main.tf b/terraform/modules/docker/main.tf index e76cb69..82211f8 100644 --- a/terraform/modules/docker/main.tf +++ b/terraform/modules/docker/main.tf @@ -54,7 +54,7 @@ resource "docker_container" "container" { } dynamic "mounts" { - for_each = var.mounts + for_each = toset(coalesce(var.mounts, [])) iterator = mount_iterator content { @@ -88,6 +88,15 @@ resource "docker_container" "container" { drop = var.container_capabilities.drop } } + + dynamic "labels" { + for_each = local.all_labels + + content { + label = labels.value.label + value = labels.value.value + } + } } resource "docker_volume" "managed_volumes" { diff --git a/terraform/modules/docker/variables.tf b/terraform/modules/docker/variables.tf index 909d064..ecd816d 100644 --- a/terraform/modules/docker/variables.tf +++ b/terraform/modules/docker/variables.tf @@ -12,6 +12,25 @@ variable "environment_vars" { type = set(string) description = "Environment variables to set in the form of KEY=VALUE" default = null + sensitive = true +} + +variable "icon"{ + type = string + default = null +} + +variable "web_ui"{ + type = string + default = null +} + +variable "labels"{ + type = set(object({ + label = string + value = string + })) + default = null } variable "attach_to_br1" { diff --git a/terraform/modules/proxy_service_stack/main.tf b/terraform/modules/proxy_service_stack/main.tf index 4647100..cefad71 100644 --- a/terraform/modules/proxy_service_stack/main.tf +++ b/terraform/modules/proxy_service_stack/main.tf @@ -2,6 +2,8 @@ module "service_docker" { for_each = var.stack.services source = "../docker" + icon = each.value.icon + web_ui = "http://${each.value.ip_address}:${each.value.service_port}" container_name = each.value.service_name container_image = each.value.image_name attach_to_br0 = false diff --git a/terraform/modules/proxy_service_stack/variables.tf b/terraform/modules/proxy_service_stack/variables.tf index cb3ab31..315c931 100644 --- a/terraform/modules/proxy_service_stack/variables.tf +++ b/terraform/modules/proxy_service_stack/variables.tf @@ -29,6 +29,7 @@ variable "stack" { mounts = optional(list(string)) volumes = optional(list(string)) services = map(object({ + icon = optional(string) service_name = string image_name = string username = optional(string, "") diff --git a/terraform/services.tf b/terraform/services.tf index 46decd1..176b68d 100644 --- a/terraform/services.tf +++ b/terraform/services.tf @@ -24,6 +24,18 @@ module "flaresolverr_service" { environment_vars = local.services.flaresolverr.env } +/* +These env variables have to be set: +TF_VAR_VPN_USER +TF_VAR_VPN_PASS +*/ +module "delugevpn_service" { + source = "./services/deluge-vpn" + service = local.services.deluge-vpn + vpn_pass = var.vpn_pass + vpn_user = var.vpn_user +} + /* module "grafana_service" { source = "./services/grafana" diff --git a/terraform/services/deluge-vpn/main.tf b/terraform/services/deluge-vpn/main.tf new file mode 100644 index 0000000..ad00280 --- /dev/null +++ b/terraform/services/deluge-vpn/main.tf @@ -0,0 +1,16 @@ +module "delugevpn_service" { + source = "../../modules/docker" + + icon = var.service.icon + web_ui = "http://${var.service.ip_address}:${var.service.service_port}" + container_name = var.service.service_name + container_image = var.service.image_name + attach_to_br0 = false + attach_to_br1 = true + br1_ipv4_addr = var.service.ip_address + environment_vars = concat(var.service.env, [ + "VPN_USER=${var.vpn_user}", + "VPN_PASS=${var.vpn_pass}" + ]) + mounts = var.service.mounts +} \ No newline at end of file diff --git a/terraform/services/deluge-vpn/variables.tf b/terraform/services/deluge-vpn/variables.tf new file mode 100644 index 0000000..3e6e441 --- /dev/null +++ b/terraform/services/deluge-vpn/variables.tf @@ -0,0 +1,27 @@ +variable "vpn_user"{ + type = string + description = "The username for the VPN connection." + sensitive = true +} + +variable "vpn_pass"{ + type = string + description = "The password for the VPN connection." + sensitive = true +} + +variable "service" { + type = object({ + icon = optional(string) + volumes = optional(list(string)) + service_name = string + image_name = string + username = optional(string, "") + password = optional(string, "") + service_port = number + ip_address = string + env = optional(list(string)) + mounts = optional(list(string)) + }) + description = "Service and config" +} \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf index fd70127..458a097 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -38,4 +38,16 @@ variable "public_facing_ip" { variable "network_admin_username" { type = string description = "Admin username" +} + +variable "vpn_user" { + type = string + description = "The username for the VPN connection." + sensitive = true +} + +variable "vpn_pass" { + type = string + description = "The password for the VPN connection." + sensitive = true } \ No newline at end of file From 8cfa5de63e41b9b87b8a25d0ee51dcd6069ae95d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 08:18:01 -0500 Subject: [PATCH 22/29] Add capabilities to deluge vpn service --- terraform/services/deluge-vpn/main.tf | 1 + terraform/services/deluge-vpn/variables.tf | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/terraform/services/deluge-vpn/main.tf b/terraform/services/deluge-vpn/main.tf index ad00280..95299ab 100644 --- a/terraform/services/deluge-vpn/main.tf +++ b/terraform/services/deluge-vpn/main.tf @@ -13,4 +13,5 @@ module "delugevpn_service" { "VPN_PASS=${var.vpn_pass}" ]) mounts = var.service.mounts + container_capabilities = var.service.capabilities } \ No newline at end of file diff --git a/terraform/services/deluge-vpn/variables.tf b/terraform/services/deluge-vpn/variables.tf index 3e6e441..c79822d 100644 --- a/terraform/services/deluge-vpn/variables.tf +++ b/terraform/services/deluge-vpn/variables.tf @@ -22,6 +22,10 @@ variable "service" { ip_address = string env = optional(list(string)) mounts = optional(list(string)) + capabilities = optional(object({ + add = optional(list(string)) + drop = optional(list(string)) + }), {}) }) description = "Service and config" } \ No newline at end of file From e52af8aa356cf6d40354bd464adad902ece8943d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 08:20:22 -0500 Subject: [PATCH 23/29] Update and correct deluge config --- config/services.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/services.yaml b/config/services.yaml index 613532c..00f80ea 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -29,6 +29,9 @@ deluge-vpn: - "NET_ADMIN" mounts: - "/etc/localtime:/etc/localtime:ro" + - "/mnt/user/Arr/deluge-data:/config" + - "/mnt/user/Downloads:/data/downloads" + - "/mnt/user/appdata/binhex-delugevpn/openvpn:/config/openvpn" requestrr: service_name: "requestrr" service_port: 4545 From a8340b8e59ab77d661c67004b3c5cc0a2c70e5b6 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 08:53:43 -0500 Subject: [PATCH 24/29] Update icons for some services in arr stack --- config/stacks.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/stacks.yaml b/config/stacks.yaml index 556f19b..74b3167 100644 --- a/config/stacks.yaml +++ b/config/stacks.yaml @@ -9,6 +9,7 @@ - "/mnt/user/Downloads:/downloads" services: prowlarr: + icon: "https://static-00.iconduck.com/assets.00/prowlarr-icon-512x512-v9ekdjxx.png" service_name: "prowlarr" service_port: 9696 image_name: "linuxserver/prowlarr:1.37.0" @@ -19,6 +20,7 @@ mounts: - "/mnt/user/Arr/prowlarr-data:/config" sonarr: + icon: "https://static-00.iconduck.com/assets.00/sonarr-icon-1024x1024-wkay604k.png" service_name: "sonarr" service_port: 8989 image_name: "linuxserver/sonarr:4.0.14.2939-ls281" @@ -29,6 +31,7 @@ mounts: - "/mnt/user/Arr/sonarr-data:/config" radarr: + icon: "https://static-00.iconduck.com/assets.00/radarr-icon-462x512-bydv4e4f.png" service_name: "radarr" service_port: 7878 image_name: "linuxserver/radarr:5.26.2" From aa54977d76ec987b920979f28d0efb90acf2ffa1 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 09:04:27 -0500 Subject: [PATCH 25/29] Added VPN creds to readme --- ReadMe.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ReadMe.md b/ReadMe.md index 2f2e320..b3daa81 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -59,6 +59,9 @@ This is run on a self-hosted agent on the Unraid Server. This is invoked using |AUTHENTIK_INSECURE|env|N| |AUTHENTIK_TOKEN|env|Y| |AUTHENTIK_URL|env|N| +|vpn_pass|terraform|Y| +|vpn_user|terraform|Y| + You may be wondering why some things such as "public facing API" and "network admin email" are set to sensitive. Well, I honestly I don't want the world knowing those details. Aside from that, there's no reason for them to be marked sensitive. From 4dc4f0ec96436581fed1fda9cb1d0ef184aaf23d Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 09:07:58 -0500 Subject: [PATCH 26/29] Updated proxy stack to accept internal --- config/stacks.yaml | 5 +++++ terraform/modules/dns/variables.tf | 1 + terraform/modules/proxy_service_stack/main.tf | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config/stacks.yaml b/config/stacks.yaml index 74b3167..2c5c006 100644 --- a/config/stacks.yaml +++ b/config/stacks.yaml @@ -9,6 +9,7 @@ - "/mnt/user/Downloads:/downloads" services: prowlarr: + internal: true icon: "https://static-00.iconduck.com/assets.00/prowlarr-icon-512x512-v9ekdjxx.png" service_name: "prowlarr" service_port: 9696 @@ -20,6 +21,7 @@ mounts: - "/mnt/user/Arr/prowlarr-data:/config" sonarr: + internal: true icon: "https://static-00.iconduck.com/assets.00/sonarr-icon-1024x1024-wkay604k.png" service_name: "sonarr" service_port: 8989 @@ -31,6 +33,7 @@ mounts: - "/mnt/user/Arr/sonarr-data:/config" radarr: + internal: true icon: "https://static-00.iconduck.com/assets.00/radarr-icon-462x512-bydv4e4f.png" service_name: "radarr" service_port: 7878 @@ -42,6 +45,7 @@ mounts: - "/mnt/user/Arr/radarr-data:/config" lidarr: + internal: true service_name: "lidarr" service_port: 8686 image_name: "linuxserver/lidarr:1.0.2.1495-ls108" @@ -52,6 +56,7 @@ mounts: - "/mnt/user/Arr/lidarr-data:/config" readarr: + internal: true service_name: "readarr" service_port: 8787 image_name: "linuxserver/readarr:1.0.2.1495-ls108" diff --git a/terraform/modules/dns/variables.tf b/terraform/modules/dns/variables.tf index f215c8a..66c18c9 100644 --- a/terraform/modules/dns/variables.tf +++ b/terraform/modules/dns/variables.tf @@ -37,6 +37,7 @@ variable "dns_cloudflare_api_token" { variable "admin_email" { description = "Email address for the admin user" type = string + sensitive = true } variable "external_host_ipv4" { diff --git a/terraform/modules/proxy_service_stack/main.tf b/terraform/modules/proxy_service_stack/main.tf index cefad71..bdb5f30 100644 --- a/terraform/modules/proxy_service_stack/main.tf +++ b/terraform/modules/proxy_service_stack/main.tf @@ -17,7 +17,7 @@ module "service_dns" { source = "../dns" for_each = var.stack.services - internal_only = true + internal_only = each.value.internal service_port = var.authentik_port zone_name = var.zone_name domain_name = each.value.domain_name From f0743458424220d10367bf72447d59d570568d16 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 09:13:30 -0500 Subject: [PATCH 27/29] Oops, forgot to add it here --- terraform/modules/proxy_service_stack/variables.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/modules/proxy_service_stack/variables.tf b/terraform/modules/proxy_service_stack/variables.tf index 315c931..a0c1781 100644 --- a/terraform/modules/proxy_service_stack/variables.tf +++ b/terraform/modules/proxy_service_stack/variables.tf @@ -29,6 +29,7 @@ variable "stack" { mounts = optional(list(string)) volumes = optional(list(string)) services = map(object({ + internal = optional(bool, false) icon = optional(string) service_name = string image_name = string From 51a7306480c05cf9f86e1dc228815f676ff1d6c2 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 09:25:44 -0500 Subject: [PATCH 28/29] Update plan workflow --- .github/workflows/run-plan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 24cffcf..63405d8 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -40,7 +40,7 @@ jobs: hostname: app.terraform.io organization: ${{ secrets.TF_ORGANIZATION }} workspace: ${{ secrets.TF_WORKSPACE }} - message: "Triggered by PR push: ${{ github.event.pull_request.head.ref }}" + message: "PR: ${{ github.event.pull_request.title }} | Commit: ${{ github.event.head_commit.message }}" plan_only: true - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.3.2 From 85ac0d2fd98fa5d0fd749ca857d9187473127b03 Mon Sep 17 00:00:00 2001 From: Ernest Mallett Date: Sat, 21 Jun 2025 09:26:53 -0500 Subject: [PATCH 29/29] Update plan workflow --- .github/workflows/run-plan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-plan.yml b/.github/workflows/run-plan.yml index 63405d8..24cffcf 100644 --- a/.github/workflows/run-plan.yml +++ b/.github/workflows/run-plan.yml @@ -40,7 +40,7 @@ jobs: hostname: app.terraform.io organization: ${{ secrets.TF_ORGANIZATION }} workspace: ${{ secrets.TF_WORKSPACE }} - message: "PR: ${{ github.event.pull_request.title }} | Commit: ${{ github.event.head_commit.message }}" + message: "Triggered by PR push: ${{ github.event.pull_request.head.ref }}" plan_only: true - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.3.2