TF: Modularization Roadmap
Terraform Modularization Roadmap
Why Modularize
- Blast radius — Today
tofu applytouches all 25 platform resources. A typo in MinIO config could accidentally recreate Harbor. Modules let you-targetby module. - Readability — 828 lines in one file. New developers can't find anything.
- Reuse — The namespace → helm → funnel pattern repeats 6 times. A module eliminates copy-paste.
- Testing — Modules can be tested independently with
tofu planand mock inputs. - Parallel work — Two developers changing different modules don't conflict as much.
Our Insight: Don't Over-Module
The industry best practice of "module everything" leads to abstraction layers that slow you down. Our
for_each in services.tf is better than a module for the service onboarding pattern — it's declarative, flat, and the tfvars file IS the interface. We should modularize where we have repeated structure (platform components) but NOT wrap the for_each pattern in a module.Proposed Module Extraction (pal-e-platform)
Module: platform-component
Encapsulates the repeating pattern: namespace → helm_release → tailscale_funnel
Used by: Forgejo, Woodpecker, Harbor, MinIO. NOT monitoring (it has extra resources like configmaps and separate Loki chart).
Module: monitoring
Special case — kube-prometheus-stack + loki-stack + datasource configmap + grafana funnel
Module: minio
Special case — helm release + 2 funnels (console + API) + bucket/IAM management
Target File Tree (pal-e-platform)
pal-e-services: Keep Flat
The
services.tf for_each pattern should NOT be modularized. It's already declarative and flat. The interface IS the tfvars map. A module would add a layer of indirection for no benefit. If anything, split main.tf into:
Migration Strategy
- Extract monitoring module first — most complex, highest value
- Use
movedblocks — avoids destroy/recreate.moved { from = helm_release.forgejo; to = module.forgejo.helm_release.this } - One module per PR — keep blast radius small
- Validate with
tofu plan— must show 0 changes after migration
When To Do This
Modularization is a prerequisite for environments (same modules, different vars). Do it before the environment split, after the CI pipeline is in place (so you can validate the refactor in CI).