Skip to content

Convert to Terraform multi-cloud IaC + DigitalOcean WireGuard VPN - #6

Open
grimwm wants to merge 3 commits into
mainfrom
feat/terraform-multicloud-do-vpn
Open

grimwm wants to merge 3 commits into
mainfrom
feat/terraform-multicloud-do-vpn

Conversation

@grimwm

@grimwm grimwm commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Repurposes this repo from Ansible into Terraform infrastructure-as-code for multiple cloud providers. The Ansible roles that lived here have been consolidated into the ansible repo (separate PR).

  • Layout: environments/<provider>/<project>/ Terraform roots (each its own state) + reusable modules/<provider>-<thing>. Starts with environments/do/vpn and a blank environments/gcp.
  • modules/digitalocean-wireguard: provisions the cheapest DigitalOcean droplet (~$4/mo), a firewall (SSH + WireGuard UDP), and a dedicated DigitalOcean Project ("space") holding only the VPN. WireGuard is installed/configured via cloud-init, and a ready-to-import Mac client config is generated (keys via the OJFord/wireguard provider).
  • Makefile: drives Terraform per environment, e.g. make tf-init tf-plan tf-apply env=do/vpn (with a vpn alias). Self-documenting help target.
  • CLAUDE.md: conventions for adding new environments and infra.
  • scripts/vpn.sh: connect/disconnect helper using wg-quick.

Usage

cd environments/do/vpn
cp terraform.tfvars.example terraform.tfvars
export DIGITALOCEAN_TOKEN=...
terraform init && terraform apply

Test plan

  • terraform fmt -recursive -check clean
  • terraform init -backend=false + terraform validate pass in environments/do/vpn
  • Makefile env resolution, target chaining, and guards verified
  • terraform apply against a real DO account to confirm the VPN comes up

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added a Terraform-driven workflow with environment-scoped commands (init/plan/apply/destroy/validate/output/refresh/show/clean) plus formatting and environment listing.
    • Added DigitalOcean WireGuard VPN provisioning, including endpoint/server public key, project ID, and generated client config output.
    • Added a macOS helper script to bring the VPN up/down and show status.
  • Documentation

    • Replaced repo documentation with Terraform/WireGuard setup and connection/teardown guidance; added environment variables and example tfvars.
  • Breaking Changes

    • Removed Ansible-based setup, configs, and Windows documentation from the repository.
  • Chores

    • Updated ignore rules and added repository guidance for AI agent usage.

…eGuard VPN

Replace the old Ansible content (now consolidated in the ansible repo) with
a Terraform layout for multiple cloud providers.

- environments/<provider>/<project> roots (start with do/vpn; blank gcp)
- modules/digitalocean-wireguard: cheapest droplet + firewall + a dedicated
  DO Project ("space") holding only the VPN; WireGuard configured via
  cloud-init; generates a ready-to-import Mac client config
- Makefile to drive terraform per env (make tf-* env=do/vpn, alias vpn)
- CLAUDE.md documenting repo conventions for adding envs/infra
- scripts/vpn.sh helper to connect/disconnect via wg-quick

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4214d157-4f0c-4e6f-b2f9-aecc48d08801

📥 Commits

Reviewing files that changed from the base of the PR and between 934d6a8 and fbe2d75.

📒 Files selected for processing (2)
  • Makefile
  • README.md
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile

📝 Walkthrough

Walkthrough

The repository is migrated from Ansible to Terraform. It adds a DigitalOcean WireGuard module and environment, a Makefile workflow, a VPN helper script, and updated repository guidance and documentation while removing the Ansible configuration and playbooks.

Changes

Ansible-to-Terraform Repository Migration

Layer / File(s) Summary
do-wireguard module variables and versions
modules/do-wireguard/variables.tf, modules/do-wireguard/versions.tf
Defines module inputs for droplet, SSH, WireGuard, client routing, and DigitalOcean metadata, with provider/version constraints and project_environment validation.
do-wireguard module implementation and outputs
modules/do-wireguard/main.tf, modules/do-wireguard/templates/cloud-init.yaml.tftpl, modules/do-wireguard/outputs.tf
Creates WireGuard keys, a DigitalOcean droplet, firewall, project, cloud-init configuration, client config assembly, and outputs for endpoint, keys, config, and project ID.
do/vpn environment wiring
environments/do/vpn/versions.tf, environments/do/vpn/variables.tf, environments/do/vpn/providers.tf, environments/do/vpn/main.tf, environments/do/vpn/outputs.tf, environments/do/vpn/terraform.tfvars.example, environments/do/vpn/.terraform.lock.hcl
Wires the module into the VPN environment with provider blocks, environment variables, a local sensitive file for the client config, outputs, an example tfvars file, and provider locks.
Makefile Terraform workflow CLI
Makefile
Adds env selection with alias expansion, environment validation, Terraform workflow targets, cleanup, and formatting targets.
VPN connection helper script
scripts/vpn.sh
Adds a Bash helper that runs wg-quick up/down/status against the generated WireGuard config after checking prerequisites.
Repo config, docs, and Ansible removal
.gitignore, CLAUDE.md, README.md, ansible.cfg, inventories/local.yml, playbooks/..., requirements.yml, group_vars/..., roles/...
Updates ignore and guidance files for Terraform, rewrites the README for the VPN workflow, and removes the Ansible inventory, playbooks, roles, defaults, handlers, metadata, and config files.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I hopped from playbooks to Terraform land,
With WireGuard keys and tunnels planned.
A make command now clears the way,
For VPN hops by night and day.
My carrots are stateful, neat, and grand.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the repo’s shift from Ansible to Terraform IaC and highlights the new DigitalOcean WireGuard VPN environment.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/terraform-multicloud-do-vpn

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.gitignore:
- Around line 17-20: The .gitignore entry for broad `*.conf` files is too
general and can hide legitimate configs; remove that pattern and keep the more
specific `**/generated/` ignore that already covers the generated WireGuard
client config. Update the ignore rules in `.gitignore` so only generated
artifacts are excluded, preserving real `.conf` files elsewhere.

In `@CLAUDE.md`:
- Around line 13-17: Add a language hint to the Markdown fence in CLAUDE.md for
the layout section so MD040 is satisfied; update the fenced block around the
repository layout examples to use a text language tag while keeping the existing
content unchanged. Locate the fix in the layout snippet near the
modules/environments/scripts examples.

In `@Makefile`:
- Around line 53-63: The guard-env target currently only validates that ENV is
set and that ENV_DIR exists, so placeholder environments like gcp still pass and
fail later in Terraform. Update guard-env to also verify the selected
environment directory is not a blank placeholder by checking for the expected
Terraform content/files before proceeding, using the ENV and ENV_DIR variables
in the Makefile logic. Keep the existing messaging pattern, but add a clear
failure path for empty placeholder roots so make targets stop early when the
environment is not usable.

In `@modules/digitalocean-wireguard/main.tf`:
- Around line 10-25: The digitalocean_droplet resource is generating a
full-tunnel client config with a default IPv6 route, but the droplet is not
provisioned for IPv6 by default. Update the droplet configuration in
digitalocean_droplet.this to either enable IPv6 at creation or remove the ::/0
client route from the cloud-init/template-driven WireGuard config so the client
and server settings stay consistent.

In `@modules/digitalocean-wireguard/templates/cloud-init.yaml.tftpl`:
- Around line 19-29: The WireGuard cloud-init template hardcodes the server
address prefix, so vpn_subnet_cidr is ignored. Update the flow from main.tf to
pass the configured CIDR prefix into cloud-init.yaml.tftpl, and change the
Address line in the template to use that variable instead of always rendering
/24. Use the existing symbols vpn_subnet_cidr, main.tf, and the server Address
block in the cloud-init template to locate the change.

In `@modules/digitalocean-wireguard/variables.tf`:
- Around line 1-5: The module naming/layout breaks the repo’s standard provider
slug convention, so update the DigitalOcean module to use the short `do` prefix
in its module identity and any related references. Adjust the module’s
`name`/branding and any caller source paths or documentation so they follow the
expected `modules/<provider>-<thing>` pattern with `do` rather than
`digitalocean`, keeping the DigitalOcean-specific code aligned with the repo’s
`do` convention.
- Around line 30-34: The `ssh_allowed_cidrs` variable in `variables.tf`
currently defaults to globally open CIDRs, which `main.tf` passes directly into
the firewall and exposes SSH on every new apply. Update the default in
`ssh_allowed_cidrs` to a safer, non-public value or remove the default so
callers must explicitly choose allowed sources, and make sure the firewall rule
in `main.tf` continues to read from that variable without widening access.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e50d01c-cfa7-4346-9b5b-a6be8ab96508

📥 Commits

Reviewing files that changed from the base of the PR and between 67b5075 and e1f3b97.

📒 Files selected for processing (59)
  • .gitignore
  • CLAUDE.md
  • Makefile
  • README-Windows.md
  • README.md
  • ansible.cfg
  • environments/do/vpn/.terraform.lock.hcl
  • environments/do/vpn/main.tf
  • environments/do/vpn/outputs.tf
  • environments/do/vpn/providers.tf
  • environments/do/vpn/terraform.tfvars.example
  • environments/do/vpn/variables.tf
  • environments/do/vpn/versions.tf
  • environments/gcp/.gitkeep
  • group_vars/linux_dev.yml
  • group_vars/windows_dev.yml
  • inventories/local.yml
  • modules/digitalocean-wireguard/main.tf
  • modules/digitalocean-wireguard/outputs.tf
  • modules/digitalocean-wireguard/templates/cloud-init.yaml.tftpl
  • modules/digitalocean-wireguard/variables.tf
  • modules/digitalocean-wireguard/versions.tf
  • playbooks/dev-setup.yml
  • playbooks/development.yml
  • playbooks/gaming-setup.yml
  • playbooks/production.yml
  • playbooks/site.yml
  • playbooks/windows-only.yml
  • playbooks/windows-wsl2.yml
  • requirements.yml
  • roles/common/defaults/main.yml
  • roles/common/handlers/main.yml
  • roles/common/meta/main.yml
  • roles/common/tasks/main.yml
  • roles/common/templates/tmux.conf.j2
  • roles/git-config/defaults/main.yml
  • roles/git-config/handlers/main.yml
  • roles/git-config/meta/main.yml
  • roles/git-config/tasks/main.yml
  • roles/linux-dev/defaults/main.yml
  • roles/linux-dev/meta/main.yml
  • roles/linux-dev/tasks/main.yml
  • roles/mednafen-gaming/defaults/main.yml
  • roles/mednafen-gaming/meta/main.yml
  • roles/mednafen-gaming/tasks/main.yml
  • roles/mednafen-gaming/templates/select-controller.sh.j2
  • roles/nvim-config/defaults/main.yml
  • roles/nvim-config/handlers/main.yml
  • roles/nvim-config/meta/main.yml
  • roles/nvim-config/tasks/main.yml
  • roles/nvim-config/templates/init.vim.j2
  • roles/windows-dev/defaults/main.yml
  • roles/windows-dev/meta/main.yml
  • roles/windows-dev/tasks/main.yml
  • roles/windows-wsl2-setup/defaults/main.yml
  • roles/windows-wsl2-setup/handlers/main.yml
  • roles/windows-wsl2-setup/meta/main.yml
  • roles/windows-wsl2-setup/tasks/main.yml
  • scripts/vpn.sh
💤 Files with no reviewable changes (41)
  • roles/windows-wsl2-setup/handlers/main.yml
  • roles/nvim-config/templates/init.vim.j2
  • roles/common/tasks/main.yml
  • roles/mednafen-gaming/meta/main.yml
  • roles/linux-dev/tasks/main.yml
  • playbooks/gaming-setup.yml
  • roles/common/handlers/main.yml
  • README-Windows.md
  • roles/windows-dev/defaults/main.yml
  • roles/windows-dev/meta/main.yml
  • playbooks/development.yml
  • roles/mednafen-gaming/templates/select-controller.sh.j2
  • playbooks/site.yml
  • roles/linux-dev/meta/main.yml
  • roles/git-config/meta/main.yml
  • ansible.cfg
  • group_vars/windows_dev.yml
  • roles/nvim-config/handlers/main.yml
  • roles/common/defaults/main.yml
  • roles/nvim-config/tasks/main.yml
  • roles/linux-dev/defaults/main.yml
  • playbooks/production.yml
  • playbooks/windows-only.yml
  • roles/windows-dev/tasks/main.yml
  • roles/windows-wsl2-setup/meta/main.yml
  • roles/mednafen-gaming/defaults/main.yml
  • group_vars/linux_dev.yml
  • roles/git-config/handlers/main.yml
  • roles/common/meta/main.yml
  • roles/git-config/defaults/main.yml
  • roles/nvim-config/defaults/main.yml
  • playbooks/windows-wsl2.yml
  • roles/windows-wsl2-setup/defaults/main.yml
  • roles/git-config/tasks/main.yml
  • requirements.yml
  • roles/nvim-config/meta/main.yml
  • roles/windows-wsl2-setup/tasks/main.yml
  • inventories/local.yml
  • playbooks/dev-setup.yml
  • roles/mednafen-gaming/tasks/main.yml
  • roles/common/templates/tmux.conf.j2

Comment thread .gitignore
Comment thread CLAUDE.md Outdated
Comment thread Makefile
Comment thread modules/do-wireguard/main.tf
Comment thread modules/do-wireguard/templates/cloud-init.yaml.tftpl
Comment thread modules/do-wireguard/variables.tf
Comment thread modules/do-wireguard/variables.tf
grimwm and others added 2 commits June 27, 2026 22:15
- rename modules/digitalocean-wireguard -> modules/do-wireguard to follow the
  repo's modules/<provider>-<thing> convention (update env source + docs)
- security: ssh_allowed_cidrs now defaults to closed; the SSH firewall rule is
  only created when CIDRs are provided (module + env root)
- droplet: enable ipv6 so the dual-stack full-tunnel client config works
- cloud-init: derive the server Address prefix from vpn_subnet_cidr instead of
  hardcoding /24
- Makefile: guard-env now rejects placeholder envs with no .tf files
- gitignore: drop the broad *.conf ignore (generated/ already covers it)
- CLAUDE.md: add text language hint to the layout fence (MD040)

Co-authored-by: Cursor <cursoragent@cursor.com>
Aligns with foundry's `list` target naming. Updates the target, .PHONY,
the help invocation, guard-env hints, and the README reference.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant