Merge pull request #57 from beatz174-bit/codex/add-dynu-dns-terraform-documentation-layer

Add Dynu Terraform brownfield DNS documentation layer
This commit is contained in:
beatz174-bit
2026-05-13 03:36:03 +10:00
committed by GitHub
13 changed files with 317 additions and 0 deletions
+6
View File
@@ -50,6 +50,12 @@ Compose files define intended service runtime composition, networking, labels, a
Dynu write operations are intentionally blocked in this repository stage. Dynu write operations are intentionally blocked in this repository stage.
### 7) Terraform Dynu DNS layer
`infrastructure/terraform/dynu/` is the brownfield Terraform DNS mirror/reconciliation root for Dynu domain/record inventory outputs.
At this stage it is primarily documentation-oriented catalog output, ready for one-object-at-a-time imports.
## Output shaping expectations ## Output shaping expectations
When adding Terraform outputs for documentation/tooling: When adding Terraform outputs for documentation/tooling:
+4
View File
@@ -30,6 +30,10 @@ Use Terraform when documenting/reconciling existing:
Do **not** treat Terraform as a full replacement for Compose operations in this repo. Do **not** treat Terraform as a full replacement for Compose operations in this repo.
- Dynu public DNS records remain authoritative at Dynu.
- Terraform Dynu configuration mirrors/reconciles Dynu DNS state for documentation and controlled drift management.
- Imported Dynu Terraform state reflects actual provider-side DNS state at import time.
### Ansible bootstrap decisions ### Ansible bootstrap decisions
+15
View File
@@ -43,6 +43,21 @@ Use for existing Proxmox VMs and metadata reconciliation.
5. Keep lifecycle ignore rules narrow and explicit. 5. Keep lifecycle ignore rules narrow and explicit.
6. Iterate per VM until plan stabilizes. 6. Iterate per VM until plan stabilizes.
## Dynu DNS workflow
Directory: `infrastructure/terraform/dynu/`
Use for existing Dynu domains and DNS records.
1. Add or confirm the documentation catalog entry for one hostname.
2. Confirm the provider resource type and import ID format.
3. Import one existing domain or DNS record at a time.
4. Inspect state with `terraform state show`.
5. Reconcile only stable, meaningful attributes into hand-maintained `.tf`.
6. Keep record IDs, dynamic DNS targets, and provider-computed values out unless intentionally required.
7. Re-run plan until intended scope is clean.
## Physical host metadata workflow ## Physical host metadata workflow
Physical host metadata currently lives in Proxmox Terraform locals/outputs and is used as documentation inventory context. Physical host metadata currently lives in Proxmox Terraform locals/outputs and is used as documentation inventory context.
+4
View File
@@ -10,17 +10,21 @@ It does **not** replace Docker Compose as runtime deployment authority.
- Physical host metadata represented in Terraform locals/outputs. - Physical host metadata represented in Terraform locals/outputs.
- Select Docker container mirror resources for documentation-oriented tracking. - Select Docker container mirror resources for documentation-oriented tracking.
- Outputs that can support documentation and later downstream tooling. - Outputs that can support documentation and later downstream tooling.
- Dynu DNS domain/record import and documentation inventory.
## What Terraform is not used for (today) ## What Terraform is not used for (today)
- Replacing `services-up.sh` / Compose for day-to-day app runtime orchestration. - Replacing `services-up.sh` / Compose for day-to-day app runtime orchestration.
- Broad, immediate greenfield provisioning of the whole stack. - Broad, immediate greenfield provisioning of the whole stack.
- Casual `apply` operations across all infrastructure. - Casual `apply` operations across all infrastructure.
- Replacing Dynu as DNS authority.
- Blindly recreating production DNS records without import/reconciliation.
## Directory map ## Directory map
- `proxmox/` — imported/reconciled VM resources and host metadata outputs. - `proxmox/` — imported/reconciled VM resources and host metadata outputs.
- `docker/` — selective Docker container import/mirror resources. - `docker/` — selective Docker container import/mirror resources.
- `dynu/` — Dynu DNS brownfield import/reconciliation and DNS documentation outputs.
- `bootstrap/` — backend/provider bootstrap scaffolding. - `bootstrap/` — backend/provider bootstrap scaffolding.
- `modules/` — placeholder module directories for future stable abstractions. - `modules/` — placeholder module directories for future stable abstractions.
- `scripts/reconcile_from_plan.sh` — helper to convert generated plan config into reviewable draft files. - `scripts/reconcile_from_plan.sh` — helper to convert generated plan config into reviewable draft files.
+67
View File
@@ -0,0 +1,67 @@
# Dynu Terraform Layer (Brownfield DNS Reconciliation)
This Terraform root is for **Dynu DNS brownfield import/reconciliation** and documentation outputs.
Dynu remains the authoritative DNS provider for existing records. Terraform here is used to mirror and reconcile existing DNS state incrementally, not to casually recreate production DNS from scratch.
## Provider
- Source: `beatz174-bit/dynu`
- Version constraint: `>= 0.1.0`
Authentication is local-only and must not be committed.
## Credentials and auth
Use local `terraform.tfvars` (or environment variables if supported by the provider release you use).
Variables included:
- `dynu_api_key` (sensitive)
- `dynu_username` (optional, sensitive)
- `dynu_password` (optional, sensitive)
> Keep real values out of git and out of shared logs.
## Safety
- Do not commit `terraform.tfvars`, `.tfstate*`, or `.terraform/`.
- Import/reconcile one domain or record at a time.
- Treat generated config as draft input, not final truth.
## Safe validation commands
```bash
cd infrastructure/terraform/dynu
terraform fmt -check -recursive
terraform init -backend=false -input=false
terraform validate
```
## Local workflow
```bash
cp terraform.tfvars.example terraform.tfvars
$EDITOR terraform.tfvars
terraform init
terraform plan
```
## Import workflow (placeholder examples)
```bash
terraform import dynu_dns_domain.lan_ddnsgeek_com '<provider-specific-domain-import-id>'
terraform state show dynu_dns_domain.lan_ddnsgeek_com
terraform plan
```
Or with import blocks:
```bash
cp imports.tf.example imports.tf
$EDITOR imports.tf
terraform plan -generate-config-out=generated-dynu.tf
```
Confirm exact resource types and import ID formats from the provider docs before running imports.
+3
View File
@@ -0,0 +1,3 @@
locals {
dynu_domain = "lan.ddnsgeek.com"
}
@@ -0,0 +1,13 @@
# Copy this file to imports.tf and adjust values after confirming the
# published provider docs for resource type names and import ID formats.
# Example placeholder shape only:
# import {
# to = dynu_dns_domain.lan_ddnsgeek_com
# id = "REPLACE_WITH_DYNU_DOMAIN_IMPORT_ID"
# }
#
# import {
# to = dynu_dns_record.grafana_lan_ddnsgeek_com
# id = "REPLACE_WITH_DYNU_RECORD_IMPORT_ID"
# }
+19
View File
@@ -0,0 +1,19 @@
output "dynu_domain" {
description = "Primary Dynu domain represented by this Terraform root."
value = local.dynu_domain
}
output "dynu_dns_records_catalog" {
description = "Documentation catalog of expected Dynu DNS records discovered from repo service exposure."
value = local.dynu_dns_records_catalog
}
output "dynu_dns_inventory" {
description = "Documentation-friendly Dynu DNS inventory for export and merge into broader infrastructure docs."
value = {
provider = "dynu"
domain = local.dynu_domain
record_count = length(local.dynu_dns_records_catalog)
records = local.dynu_dns_records_catalog
}
}
@@ -0,0 +1,5 @@
provider "dynu" {
# Keep auth local-only; do not commit credentials.
# Provider schema must be confirmed against registry docs before changing fields.
api_key = var.dynu_api_key
}
+147
View File
@@ -0,0 +1,147 @@
locals {
dynu_dns_records_catalog = {
auth = {
fqdn = "auth.lan.ddnsgeek.com"
hostname = "auth"
service = "authelia"
source = "core/authelia/docker-compose.yml"
purpose = "Authentication portal"
record_type = null
ttl = null
target = null
proxied = null
}
gitea = {
fqdn = "gitea.lan.ddnsgeek.com"
hostname = "gitea"
service = "gitea"
source = "apps/gitea/docker-compose.yml"
purpose = "Gitea service endpoint"
record_type = null
ttl = null
target = null
proxied = null
}
gotify = {
fqdn = "gotify.lan.ddnsgeek.com"
hostname = "gotify"
service = "gotify"
source = "monitoring/gotify/docker-compose.yml"
purpose = "Gotify notifications"
record_type = null
ttl = null
target = null
proxied = null
}
grafana = {
fqdn = "grafana.lan.ddnsgeek.com"
hostname = "grafana"
service = "grafana"
source = "monitoring/grafana/docker-compose.yml"
purpose = "Grafana monitoring UI"
record_type = null
ttl = null
target = null
proxied = null
}
familytree = {
fqdn = "familytree.lan.ddnsgeek.com"
hostname = "familytree"
service = "gramps"
source = "apps/gramps/docker-compose.yml"
purpose = "Family tree application"
record_type = null
ttl = null
target = null
proxied = null
}
influxdb = {
fqdn = "influxdb.lan.ddnsgeek.com"
hostname = "influxdb"
service = "influxdb"
source = "monitoring/influxdb/docker-compose.yml"
purpose = "InfluxDB metrics endpoint"
record_type = null
ttl = null
target = null
proxied = null
}
monitor_kuma = {
fqdn = "monitor-kuma.lan.ddnsgeek.com"
hostname = "monitor-kuma"
service = "uptime-kuma"
source = "monitoring/uptime-kuma/docker-compose.yml"
purpose = "Uptime Kuma monitoring UI"
record_type = null
ttl = null
target = null
proxied = null
}
mtls_bridge = {
fqdn = "mtls-bridge.lan.ddnsgeek.com"
hostname = "mtls-bridge"
service = "mtls-bridge"
source = "monitoring/mtls-bridge/docker-compose.yml"
purpose = "mTLS bridge API"
record_type = null
ttl = null
target = null
proxied = null
}
nextcloud = {
fqdn = "nextcloud.lan.ddnsgeek.com"
hostname = "nextcloud"
service = "nextcloud-webapp"
source = "apps/nextcloud/docker-compose.yml"
purpose = "Nextcloud service endpoint"
record_type = null
ttl = null
target = null
proxied = null
}
node_red = {
fqdn = "node-red.lan.ddnsgeek.com"
hostname = "node-red"
service = "node-red"
source = "monitoring/node-red/docker-compose.yml"
purpose = "Node-RED automation UI/API"
record_type = null
ttl = null
target = null
proxied = null
}
passbolt = {
fqdn = "passbolt.lan.ddnsgeek.com"
hostname = "passbolt"
service = "passbolt-webapp"
source = "apps/passbolt/docker-compose.yml"
purpose = "Passbolt password management"
record_type = null
ttl = null
target = null
proxied = null
}
portainer = {
fqdn = "portainer.lan.ddnsgeek.com"
hostname = "portainer"
service = "portainer"
source = "monitoring/portainer/docker-compose.yml"
purpose = "Portainer admin endpoint"
record_type = null
ttl = null
target = null
proxied = null
}
prometheus = {
fqdn = "prometheus.lan.ddnsgeek.com"
hostname = "prometheus"
service = "prometheus"
source = "monitoring/prometheus/docker-compose.yml"
purpose = "Prometheus metrics endpoint"
record_type = null
ttl = null
target = null
proxied = null
}
}
}
@@ -0,0 +1,4 @@
# Local-only credentials. Do not commit real values.
dynu_api_key = "replace-with-dynu-api-key"
dynu_username = null
dynu_password = null
@@ -0,0 +1,20 @@
variable "dynu_api_key" {
description = "Dynu API key/token used by the Dynu Terraform provider."
type = string
sensitive = true
default = null
}
variable "dynu_username" {
description = "Optional Dynu username, only if required by the provider."
type = string
sensitive = true
default = null
}
variable "dynu_password" {
description = "Optional Dynu password, only if required by the provider."
type = string
sensitive = true
default = null
}
+10
View File
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.6.0"
required_providers {
dynu = {
source = "beatz174-bit/dynu"
version = ">= 0.1.0"
}
}
}