Compare commits
10 Commits
48bdaf28ec
...
ab3427fd82
Author | SHA1 | Date | |
---|---|---|---|
ab3427fd82 | |||
0536e9252a | |||
e6ed17b127 | |||
fb2526154b | |||
b7a7daef3b | |||
c5559fbb38 | |||
30e36beb93 | |||
e65da68f2d | |||
6c1ac8d96c | |||
f35163b5cd |
20
.github/workflows/infra-build.yml
vendored
20
.github/workflows/infra-build.yml
vendored
@@ -4,23 +4,28 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
paths:
|
||||||
|
- '**.tf'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
opentofu:
|
opentofu:
|
||||||
|
if: github.repository == 'comprofix/opentofu-homelab'
|
||||||
name: Opentofu Build
|
name: Opentofu Build
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
container:
|
container:
|
||||||
image: node:20-bullseye
|
image: node:20-bullseye
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PG_CONN_STR: ${{ secrets.PG_CONN_STR }} # available to all steps
|
PG_CONN_STR: ${{ secrets.PG_CONN_STR }} # PostgreSQL backend connection string
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
# 1. Checkout code
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# 2. Generate dynamic Terraform/Opentofu vars from secrets
|
||||||
- name: Generate Dynamic Vars (Secrets)
|
- name: Generate Dynamic Vars (Secrets)
|
||||||
run: |
|
run: |
|
||||||
cat <<EOF > terraform.auto.tfvars
|
cat <<EOF > terraform.auto.tfvars
|
||||||
@@ -32,27 +37,34 @@ jobs:
|
|||||||
ssh_key = "${{ secrets.SSH_PRIVATE_KEY }}"
|
ssh_key = "${{ secrets.SSH_PRIVATE_KEY }}"
|
||||||
passphrase = "${{ secrets.SSH_PASSPHRASE }}"
|
passphrase = "${{ secrets.SSH_PASSPHRASE }}"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# 3. Setup Opentofu CLI
|
||||||
- name: Setup Opentofu
|
- name: Setup Opentofu
|
||||||
uses: opentofu/setup-opentofu@v1
|
uses: opentofu/setup-opentofu@v1
|
||||||
|
|
||||||
|
# 4. Format the secrets/vars file (required by tofu fmt)
|
||||||
- name: Format vars file
|
- name: Format vars file
|
||||||
run: tofu fmt terraform.auto.tfvars
|
run: tofu fmt terraform.auto.tfvars
|
||||||
|
|
||||||
|
# 5. Initialize Opentofu backend and providers
|
||||||
- name: Opentofu Init
|
- name: Opentofu Init
|
||||||
run: tofu init
|
run: tofu init
|
||||||
|
|
||||||
|
# 6. Full formatting/lint check for all files
|
||||||
- name: Opentofu Format Check
|
- name: Opentofu Format Check
|
||||||
run: tofu fmt -check -recursive
|
run: tofu fmt -check -recursive
|
||||||
|
|
||||||
|
# 7. Validate configuration
|
||||||
- name: Opentofu Validate
|
- name: Opentofu Validate
|
||||||
run: tofu validate
|
run: tofu validate
|
||||||
|
|
||||||
|
# 8. Plan changes
|
||||||
- name: Opentofu Plan
|
- name: Opentofu Plan
|
||||||
id: plan
|
id: plan
|
||||||
run: |
|
run: |
|
||||||
tofu plan -out=tfplan -detailed-exitcode
|
tofu plan -out=tfplan -detailed-exitcode
|
||||||
|
|
||||||
|
# 9. Apply changes only if previous steps succeed
|
||||||
- name: Opentofu Apply
|
- name: Opentofu Apply
|
||||||
if: success()
|
if: success()
|
||||||
run: tofu apply -auto-approve tfplan
|
run: tofu apply -auto-approve tfplan
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,9 @@
|
|||||||
*.tfstate
|
*.tfstate
|
||||||
*.tfstate.*
|
*.tfstate.*
|
||||||
|
|
||||||
|
# var files
|
||||||
|
*.tfvars
|
||||||
|
|
||||||
# Crash log files
|
# Crash log files
|
||||||
crash.log
|
crash.log
|
||||||
crash.*.log
|
crash.*.log
|
||||||
|
@@ -1,17 +1,16 @@
|
|||||||
resource "proxmox_lxc" "omada" {
|
resource "proxmox_lxc" "omada" {
|
||||||
depends_on = [
|
|
||||||
proxmox_vm_qemu.dev-docker
|
|
||||||
]
|
|
||||||
target_node = "pve"
|
target_node = "pve"
|
||||||
vmid = "200"
|
vmid = "201"
|
||||||
hostname = "omada"
|
hostname = "omada"
|
||||||
ostemplate = "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
ostemplate = "local:vztmpl/debian-13-standard_13.1-1_amd64.tar.zst"
|
||||||
password = var.ci_password
|
password = var.ci_password
|
||||||
unprivileged = false
|
unprivileged = false
|
||||||
ostype = "debian"
|
ostype = "debian"
|
||||||
onboot = true
|
onboot = true
|
||||||
start = true
|
start = true
|
||||||
startup = "order=1000"
|
startup = "order=1000"
|
||||||
|
tags = "docker;container;appliance"
|
||||||
|
|
||||||
|
|
||||||
ssh_public_keys = <<EOF
|
ssh_public_keys = <<EOF
|
||||||
@@ -32,11 +31,29 @@ resource "proxmox_lxc" "omada" {
|
|||||||
mount = "nfs;cifs"
|
mount = "nfs;cifs"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind Mount Point
|
||||||
|
mountpoint {
|
||||||
|
key = "1"
|
||||||
|
slot = 1
|
||||||
|
//storage = "/mnt/lxc/omada"
|
||||||
|
volume = "/mnt/lxc/omada"
|
||||||
|
size = "1G"
|
||||||
|
mp = "/data"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
network {
|
network {
|
||||||
name = "eth0"
|
name = "eth0"
|
||||||
bridge = "vmbr0"
|
bridge = "vmbr0"
|
||||||
ip = "10.10.40.2/24"
|
ip = "10.10.40.2/24"
|
||||||
gw = "10.10.40.1"
|
gw = "10.10.40.1"
|
||||||
tag = 40
|
tag = 40
|
||||||
|
ip6 = "auto"
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
ignore_changes = [
|
||||||
|
mountpoint,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,14 +4,14 @@ resource "proxmox_vm_qemu" "dev-docker" {
|
|||||||
target_node = "pve"
|
target_node = "pve"
|
||||||
vmid = "400"
|
vmid = "400"
|
||||||
name = "dev-docker"
|
name = "dev-docker"
|
||||||
tags = null
|
tags = "linux;vm;docker"
|
||||||
|
|
||||||
# VM Advanced General Settings
|
# VM Advanced General Settings
|
||||||
onboot = true
|
onboot = true
|
||||||
scsihw = "virtio-scsi-single"
|
scsihw = "virtio-scsi-single"
|
||||||
|
|
||||||
# VM OS Settings
|
# VM OS Settings
|
||||||
clone = "debian-12-generic-amd64"
|
clone = "debian-13-generic-amd64"
|
||||||
clone_wait = 120
|
clone_wait = 120
|
||||||
timeouts {
|
timeouts {
|
||||||
create = "1h"
|
create = "1h"
|
34
CHANGELOG.md
Normal file
34
CHANGELOG.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial setup of OpenTofu project structure
|
||||||
|
- Providers for Proxmox, Omada, GitHub
|
||||||
|
- PostgreSQL backend support
|
||||||
|
- GitHub Actions CI/CD workflow with `init`, `fmt`, `validate`, `plan`, and `apply`
|
||||||
|
- Secure secrets handling via `terraform.auto.tfvars`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-09-27
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- First working pipeline applying infrastructure automatically on `main`
|
||||||
|
- Docker VM definition (`docker.tf`)
|
||||||
|
- GitHub repo/org configuration (`github.tf`)
|
||||||
|
- Omada networking definitions (`omada.tf`)
|
||||||
|
- Provider and backend config (`provider.tf`)
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Matthew McKinnon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
80
README.md
Normal file
80
README.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|

|
||||||
|
|
||||||
|
[](https://github.com/comprofix/opentofu-homelab/actions)
|
||||||
|
|
||||||
|
## 📖 Overview
|
||||||
|
|
||||||
|
Infrastructure as Code (IaC) for the Comprofix homelab using [OpenTofu](https://opentofu.org/).
|
||||||
|
|
||||||
|
This repository provisions and manages resources such as the Proxmox VMs and LXC containers used in the Comprofix Homelab
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- Declarative infrastructure management with OpenTofu
|
||||||
|
- Remote state stored in PostgreSQL backend
|
||||||
|
- Automated formatting, validation, and applies via GitHub Actions
|
||||||
|
- Secure injection of secrets into `terraform.auto.tfvars`
|
||||||
|
- Supports Proxmox VM provisioning and Omada configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Repository Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
├── dev-docker.tf # Docker VM definitions
|
||||||
|
├── github.tf # GitHub repo/org configuration
|
||||||
|
├── omada.tf # Omada network definitions
|
||||||
|
├── provider.tf # Provider setup and backend configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Requirements
|
||||||
|
|
||||||
|
- **OpenTofu** (installed automatically in GitHub Actions via [`opentofu/setup-opentofu`](https://github.com/opentofu/setup-opentofu))
|
||||||
|
- **PostgreSQL** database for remote state
|
||||||
|
Connection string provided via secret: `PG_CONN_STR`
|
||||||
|
- **GitHub Actions self-hosted runner** with access to Proxmox and Omada APIs
|
||||||
|
- Configured repository secrets:
|
||||||
|
- `PG_CONN_STR`
|
||||||
|
- `CI_USER`, `CI_PASSWORD`
|
||||||
|
- `PVE_API_URL`, `PVE_API_TOKEN_ID`, `PVE_API_TOKEN_SECRET`
|
||||||
|
- `SSH_PRIVATE_KEY`, `SSH_PASSPHRASE`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Workflow
|
||||||
|
|
||||||
|
Infrastructure is applied automatically on pushes to the `main` branch.
|
||||||
|
|
||||||
|
1. Checkout repo
|
||||||
|
2. Generate `terraform.auto.tfvars` from GitHub secrets
|
||||||
|
3. Run `tofu init`, `tofu fmt`, `tofu validate`
|
||||||
|
4. Execute `tofu plan`
|
||||||
|
5. If successful, run `tofu apply`
|
||||||
|
|
||||||
|
> 🔒 PRs and forks do not run workflows. Only code merged into `main` will trigger an apply.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Usage
|
||||||
|
|
||||||
|
Local testing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Initialize
|
||||||
|
tofu init
|
||||||
|
|
||||||
|
# Format configs
|
||||||
|
tofu fmt -recursive
|
||||||
|
|
||||||
|
# Validate configs
|
||||||
|
tofu validate
|
||||||
|
|
||||||
|
# Plan changes
|
||||||
|
PG_CONN_STR="postgres://..." tofu plan
|
||||||
|
|
||||||
|
# Apply changes
|
||||||
|
PG_CONN_STR="postgres://..." tofu apply
|
44
github.tf
44
github.tf
@@ -1,44 +0,0 @@
|
|||||||
resource "proxmox_lxc" "ghshr" {
|
|
||||||
|
|
||||||
depends_on = [
|
|
||||||
proxmox_vm_qemu.dev-docker
|
|
||||||
]
|
|
||||||
|
|
||||||
target_node = "pve"
|
|
||||||
vmid = "201"
|
|
||||||
hostname = "ghshr"
|
|
||||||
ostemplate = "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
|
||||||
password = var.ci_password
|
|
||||||
unprivileged = false
|
|
||||||
ostype = "debian"
|
|
||||||
onboot = true
|
|
||||||
start = true
|
|
||||||
startup = "order=1000"
|
|
||||||
|
|
||||||
|
|
||||||
ssh_public_keys = <<EOF
|
|
||||||
${var.ssh_key}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
memory = "4096"
|
|
||||||
swap = "512"
|
|
||||||
|
|
||||||
rootfs {
|
|
||||||
storage = "local"
|
|
||||||
size = "8G"
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
fuse = true
|
|
||||||
nesting = true
|
|
||||||
mount = "nfs;cifs"
|
|
||||||
}
|
|
||||||
|
|
||||||
network {
|
|
||||||
name = "eth0"
|
|
||||||
bridge = "vmbr0"
|
|
||||||
ip = "10.10.10.8/24"
|
|
||||||
gw = "10.10.10.1"
|
|
||||||
tag = 10
|
|
||||||
}
|
|
||||||
}
|
|
29
prepareEnvs.sh
Normal file
29
prepareEnvs.sh
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
bw config server https://vault.comprofix.com
|
||||||
|
bw login
|
||||||
|
export BW_SESSION=$(bw unlock --raw)
|
||||||
|
bw sync
|
||||||
|
|
||||||
|
echo "Please wait while we prepare terraform.auto.tfvars"
|
||||||
|
|
||||||
|
proxmox_api_url=$(bw get --session $BW_SESSION uri proxmox_api)
|
||||||
|
proxmox_api_token_id=$(bw get --session $BW_SESSION username f295a859-154a-482d-8129-c6ec6e06131e)
|
||||||
|
proxmox_api_token_secret=$(bw get --session $BW_SESSION password f295a859-154a-482d-8129-c6ec6e06131e)
|
||||||
|
ci_user=$(bw get --session $BW_SESSION username ci_details)
|
||||||
|
ci_password=$(bw get --session $BW_SESSION password ci_details)
|
||||||
|
ssh_key=$(bw get --session $BW_SESSION notes ssh_public_key_main)
|
||||||
|
passphrase=$(bw get --session $BW_SESSION password state_passphrase)
|
||||||
|
|
||||||
|
echo 'proxmox_api_url = "'$proxmox_api_url'"' > terraform.auto.tfvars
|
||||||
|
echo 'proxmox_api_token_id = "'$proxmox_api_token_id'"' >> terraform.auto.tfvars
|
||||||
|
echo 'proxmox_api_token_secret = "'$proxmox_api_token_secret'"' >> terraform.auto.tfvars
|
||||||
|
echo 'ci_user = "'$ci_user'"' >> terraform.auto.tfvars
|
||||||
|
echo 'ci_password = "'$ci_password'"' >> terraform.auto.tfvars
|
||||||
|
echo 'ssh_key = "'$ssh_key'"' >> terraform.auto.tfvars
|
||||||
|
echo 'passphrase = "'$passphrase'"' >> terraform.auto.tfvars
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -11,7 +11,9 @@ terraform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
backend "pg" {}
|
backend "pg" {
|
||||||
|
schema_name = "homelab-infra"
|
||||||
|
}
|
||||||
encryption {
|
encryption {
|
||||||
key_provider "pbkdf2" "mykey" {
|
key_provider "pbkdf2" "mykey" {
|
||||||
passphrase = var.passphrase
|
passphrase = var.passphrase
|
||||||
|
Reference in New Issue
Block a user