feat: Add SOPS state management and fix Terraform deployment issues

SOPS State Management:
- Implemented Git + SOPS + age encryption for Terraform state files
- Added .gitignore files to prevent committing unencrypted secrets
- Created .sops.yaml.example template for age encryption configuration
- Created helper scripts for automated encryption/decryption workflow:
  - scripts/tf: Wrapper script with auto-encrypt/decrypt
  - scripts/tf-encrypt: Manual encryption of state files
  - scripts/tf-decrypt: Manual decryption of state files
- Added comprehensive STATE_MANAGEMENT.md documentation covering:
  - Installation of age and SOPS
  - Initial setup and key generation
  - Daily workflow examples
  - Security best practices
  - Troubleshooting common issues
  - Multi-user key management
  - Backup strategies

Terraform Deployment Fixes:
- Added snippets_storage variable for cloud-init snippet storage
- Fixed datastore error: "local" does not support snippets
- Updated README with solutions for datastore and SSH issues
- Added troubleshooting for:
  - Enabling snippets on existing storage (pvesm set)
  - Creating dedicated directory storage for snippets
  - SSH authentication setup with ssh-agent
  - Manual cloud-init snippet creation workaround

Files modified:
- terraform/proxmox-examples/docker-host/main.tf
- terraform/proxmox-examples/docker-host/variables.tf
- terraform/proxmox-examples/docker-host/terraform.tfvars.example
- terraform/proxmox-examples/docker-host/README.md

Files added:
- .gitignore (root level)
- terraform/proxmox-examples/docker-host/.gitignore
- terraform/proxmox-examples/docker-host/.sops.yaml.example
- terraform/proxmox-examples/docker-host/STATE_MANAGEMENT.md
- terraform/proxmox-examples/docker-host/scripts/tf
- terraform/proxmox-examples/docker-host/scripts/tf-encrypt
- terraform/proxmox-examples/docker-host/scripts/tf-decrypt
This commit is contained in:
Claude 2025-11-11 05:55:07 +00:00
parent 21d373a513
commit 9109712b59
No known key found for this signature in database
11 changed files with 843 additions and 2 deletions

52
.gitignore vendored Normal file
View file

@ -0,0 +1,52 @@
# Terraform / OpenTofu
**/.terraform/
**/.terraform.lock.hcl
**/*.tfstate
**/*.tfstate.backup
**/*.tfstate.*.backup
**/crash.log
**/crash.*.log
**/.terraformrc
**/terraform.rc
**/override.tf
**/override.tf.json
**/*_override.tf
**/*_override.tf.json
**/.terraform.tfstate.lock.info
# Keep encrypted state files
!**/*.tfstate.enc
# SOPS
.sops/key.txt
**/.decrypted~*
# Environment files with secrets
**/.env
!**/.env.example
# Sensitive data directories
**/data/
**/config/
!**/config/*.example
!**/config/.gitkeep
# Logs
**/logs/
**/*.log
# OS files
.DS_Store
Thumbs.db
*.swp
*.swo
*~
# Editor files
.vscode/
.idea/
*.sublime-*
# Temporary files
*.tmp
*.temp

View file

@ -0,0 +1,34 @@
# Terraform state files (unencrypted)
*.tfstate
*.tfstate.backup
*.tfstate.*.backup
# Keep encrypted state files
!*.tfstate.enc
# Terraform directory
.terraform/
.terraform.lock.hcl
# SOPS configuration with your private key
.sops.yaml
# Actual terraform.tfvars (may contain secrets)
terraform.tfvars
# Keep encrypted version
!terraform.tfvars.enc
# Crash logs
crash.log
crash.*.log
# Override files
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Terraform RC files
.terraformrc
terraform.rc

View file

@ -0,0 +1,34 @@
# SOPS Configuration for Terraform State Encryption
#
# Setup Instructions:
# 1. Install age and sops:
# - Debian/Ubuntu: sudo apt install age
# - macOS: brew install age sops
# - Manual: https://github.com/FiloSottile/age/releases
# https://github.com/getsops/sops/releases
#
# 2. Generate an age key:
# mkdir -p ~/.sops
# age-keygen -o ~/.sops/homelab-terraform.txt
#
# 3. Copy this file:
# cp .sops.yaml.example .sops.yaml
#
# 4. Replace YOUR_AGE_PUBLIC_KEY_HERE with the public key from step 2
# (the line starting with "age1...")
#
# 5. DO NOT commit .sops.yaml to git (it's in .gitignore)
# Keep your private key (~/.sops/homelab-terraform.txt) secure!
creation_rules:
# Encrypt all .tfstate files with age
- path_regex: \.tfstate$
age: YOUR_AGE_PUBLIC_KEY_HERE
# Encrypt any .secret files
- path_regex: \.secret$
age: YOUR_AGE_PUBLIC_KEY_HERE
# Encrypt terraform.tfvars (contains API tokens)
- path_regex: terraform\.tfvars$
age: YOUR_AGE_PUBLIC_KEY_HERE

View file

@ -474,6 +474,80 @@ Type `yes` to confirm deletion.
## Troubleshooting ## Troubleshooting
### Datastore Does Not Support Snippets
Error: `the datastore "local" does not support content type "snippets"`
**Cause:** The storage you specified doesn't have snippets enabled
**Solution 1 - Enable snippets on existing storage:**
```bash
# On Proxmox host, check current content types
pvesm status
# Enable snippets on local storage
pvesm set local --content backup,iso,vztmpl,snippets
# Verify
pvesm status | grep local
```
**Solution 2 - Create dedicated directory storage:**
```bash
# On Proxmox host
# Create directory for snippets
mkdir -p /var/lib/vz/snippets
# Add directory storage via Proxmox UI:
# Datacenter → Storage → Add → Directory
# ID: local-snippets
# Directory: /var/lib/vz/snippets
# Content: Snippets
# Or via CLI:
pvesm add dir local-snippets --path /var/lib/vz/snippets --content snippets
# Update terraform.tfvars:
# snippets_storage = "local-snippets"
```
### SSH Authentication Failed
Error: `failed to open SSH client: unable to authenticate`
**Cause:** The Proxmox provider needs SSH access to upload cloud-init files
**Solution 1 - Add SSH key to Proxmox (Recommended):**
```bash
# On your workstation, generate SSH key if you don't have one
ssh-keygen -t ed25519 -C "terraform@homelab"
# Copy to Proxmox host
ssh-copy-id root@proxmox.local
# Add key to ssh-agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Verify
ssh-add -L
ssh root@proxmox.local "echo 'SSH works!'"
```
**Solution 2 - Use API token only (workaround):**
If SSH is problematic, you can create the cloud-init snippet manually:
```bash
# On Proxmox host, create the snippet
nano /var/lib/vz/snippets/cloud-init-docker-host.yaml
# Paste the cloud-init content from main.tf
# Then remove the proxmox_virtual_environment_file resource from main.tf
# and reference the file directly in the VM resource:
# user_data_file_id = "local:snippets/cloud-init-docker-host.yaml"
```
### Template Not Found ### Template Not Found
Error: `template with ID 9000 not found` Error: `template with ID 9000 not found`

View file

@ -0,0 +1,378 @@
# Terraform State Management with SOPS
This project uses [SOPS](https://github.com/getsops/sops) (Secrets OPerationS) with [age](https://github.com/FiloSottile/age) encryption to securely store Terraform state files in Git.
## Why SOPS + age?
**Encrypted at rest** - State files contain sensitive data (IPs, tokens)
**Version controlled** - Track infrastructure changes over time
**No infrastructure required** - No need for S3, PostgreSQL, or other backends
**Perfect for homelabs** - Simple, secure, self-contained
**FOSS** - Fully open source tools
## Prerequisites
### 1. Install age
**Debian/Ubuntu:**
```bash
sudo apt update
sudo apt install age
```
**macOS:**
```bash
brew install age
```
**Manual installation:**
```bash
# Download from https://github.com/FiloSottile/age/releases
wget https://github.com/FiloSottile/age/releases/download/v1.1.1/age-v1.1.1-linux-amd64.tar.gz
tar xzf age-v1.1.1-linux-amd64.tar.gz
sudo mv age/age age/age-keygen /usr/local/bin/
```
### 2. Install SOPS
**Debian/Ubuntu:**
```bash
# Download from https://github.com/getsops/sops/releases
wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops
```
**macOS:**
```bash
brew install sops
```
Verify installation:
```bash
age --version
sops --version
```
## Initial Setup
### 1. Generate Age Encryption Key
```bash
# Create SOPS directory
mkdir -p ~/.sops
# Generate a new age key pair
age-keygen -o ~/.sops/homelab-terraform.txt
# View the key (you'll need the public key)
cat ~/.sops/homelab-terraform.txt
```
Output will look like:
```
# created: 2025-11-11T12:34:56Z
# public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AGE-SECRET-KEY-1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
```
**⚠️ IMPORTANT:**
- The line starting with `AGE-SECRET-KEY-1` is your **private key** - keep it secret!
- The line starting with `age1` is your **public key** - you'll use this in .sops.yaml
- **Backup this file** to a secure location (password manager, encrypted backup, etc.)
- If you lose this key, you **cannot decrypt** your state files!
### 2. Configure SOPS
```bash
cd terraform/proxmox-examples/docker-host
# Copy the example config
cp .sops.yaml.example .sops.yaml
# Edit and replace YOUR_AGE_PUBLIC_KEY_HERE with your public key from step 1
nano .sops.yaml
```
Your `.sops.yaml` should look like:
```yaml
creation_rules:
- path_regex: \.tfstate$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- path_regex: \.secret$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- path_regex: terraform\.tfvars$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### 3. Set Environment Variable (Optional but Recommended)
```bash
# Add to your ~/.bashrc or ~/.zshrc
echo 'export SOPS_AGE_KEY_FILE=~/.sops/homelab-terraform.txt' >> ~/.bashrc
source ~/.bashrc
```
This tells SOPS where to find your private key for decryption.
## Usage
### Option A: Automatic Wrapper Script (Recommended)
Use the `./scripts/tf` wrapper that handles encryption/decryption automatically:
```bash
# Initialize (first time)
./scripts/tf init
# Plan changes
./scripts/tf plan
# Apply changes (automatically encrypts after)
./scripts/tf apply
# Destroy infrastructure (automatically encrypts after)
./scripts/tf destroy
# View state
./scripts/tf show
```
The wrapper script:
1. Decrypts state files before running
2. Runs your terraform/tofu command
3. Encrypts state files after (if state was modified)
### Option B: Manual Encryption/Decryption
If you prefer manual control:
```bash
# 1. Decrypt state files
./scripts/tf-decrypt
# 2. Run terraform commands
tofu init
tofu plan
tofu apply
# 3. Encrypt state files
./scripts/tf-encrypt
# 4. Commit encrypted files to Git
git add *.enc
git commit -m "Update infrastructure"
git push
```
## Workflow Examples
### First Time Setup
```bash
cd terraform/proxmox-examples/docker-host
# 1. Configure your variables
cp terraform.tfvars.example terraform.tfvars
nano terraform.tfvars # Add your API tokens, SSH keys, etc.
# 2. Initialize Terraform
./scripts/tf init
# 3. Plan infrastructure
./scripts/tf plan
# 4. Apply infrastructure
./scripts/tf apply
# 5. Encrypted state files are automatically created
# terraform.tfstate.enc now exists
# 6. Commit encrypted state to Git
git add terraform.tfstate.enc .sops.yaml.example
git commit -m "Add encrypted Terraform state"
git push
```
### Making Infrastructure Changes
```bash
# 1. Decrypt, apply changes, re-encrypt (all automatic)
./scripts/tf apply
# 2. Commit updated encrypted state
git add terraform.tfstate.enc
git commit -m "Update VM configuration"
git push
```
### Cloning on a New Machine
```bash
# 1. Clone the repository
git clone https://github.com/efigueroa/homelab.git
cd homelab/terraform/proxmox-examples/docker-host
# 2. Copy your age private key to the new machine
# (Securely transfer ~/.sops/homelab-terraform.txt)
mkdir -p ~/.sops
# Copy the key file here
# 3. Set up SOPS config
cp .sops.yaml.example .sops.yaml
# Edit with your public key
# 4. Decrypt state
./scripts/tf-decrypt
# 5. Now you can run terraform commands
./scripts/tf plan
```
## Security Best Practices
### DO ✅
- **Backup your age private key** to multiple secure locations
- **Use different keys** for different projects/environments
- **Commit `.sops.yaml.example`** to Git (without your actual key)
- **Commit encrypted `*.enc` files** to Git
- **Use the wrapper script** to avoid forgetting to encrypt
### DON'T ❌
- **Never commit `.sops.yaml`** with your actual key (it's in .gitignore)
- **Never commit unencrypted `.tfstate`** files (they're in .gitignore)
- **Never commit unencrypted `terraform.tfvars`** with secrets
- **Never share your private age key** publicly
- **Don't lose your private key** - you can't decrypt without it!
## File Structure
```
terraform/proxmox-examples/docker-host/
├── .gitignore # Ignores unencrypted files
├── .sops.yaml # Your SOPS config (NOT in Git)
├── .sops.yaml.example # Template (in Git)
├── terraform.tfstate # Unencrypted state (NOT in Git)
├── terraform.tfstate.enc # Encrypted state (in Git) ✅
├── terraform.tfvars # Your config with secrets (NOT in Git)
├── terraform.tfvars.enc # Encrypted config (in Git) ✅
├── terraform.tfvars.example # Template without secrets (in Git)
├── scripts/
│ ├── tf # Wrapper script
│ ├── tf-encrypt # Manual encrypt
│ └── tf-decrypt # Manual decrypt
└── STATE_MANAGEMENT.md # This file
```
## Troubleshooting
### Error: "no key could decrypt the data"
**Cause:** SOPS can't find your private key
**Solution:**
```bash
# Set the key file location
export SOPS_AGE_KEY_FILE=~/.sops/homelab-terraform.txt
# Or add to ~/.bashrc permanently
echo 'export SOPS_AGE_KEY_FILE=~/.sops/homelab-terraform.txt' >> ~/.bashrc
```
### Error: "YOUR_AGE_PUBLIC_KEY_HERE"
**Cause:** You didn't replace the placeholder in `.sops.yaml`
**Solution:**
```bash
# Edit .sops.yaml and replace with your actual public key
nano .sops.yaml
```
### Error: "failed to get the data key"
**Cause:** The file was encrypted with a different key
**Solution:**
- Ensure you're using the same age key that encrypted the file
- If you lost the original key, you'll need to re-create the state by running `tofu import`
### Accidentally Committed Unencrypted State
**Solution:**
```bash
# Remove from Git history (DANGEROUS - coordinate with team if not solo)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch terraform.tfstate' \
--prune-empty --tag-name-filter cat -- --all
# Force push (only if solo or coordinated)
git push origin --force --all
```
### Lost Private Key
**Solution:**
- Restore from your backup (you made a backup, right?)
- If truly lost, you'll need to:
1. Manually recreate infrastructure or import existing resources
2. Generate a new age key
3. Re-encrypt everything with the new key
## Advanced: Multiple Keys (Team Access)
If multiple people need access:
```yaml
# .sops.yaml
creation_rules:
- path_regex: \.tfstate$
age: >-
age1person1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
age1person2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
age1person3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
Each person's private key can decrypt the files.
## Backup Strategy
### Recommended Backup Locations:
1. **Password Manager** (1Password, Bitwarden, etc.)
```bash
# Copy the contents
cat ~/.sops/homelab-terraform.txt
# Store as a secure note in your password manager
```
2. **Encrypted USB Drive**
```bash
# Copy to encrypted drive
cp ~/.sops/homelab-terraform.txt /media/encrypted-usb/
```
3. **Encrypted Cloud Storage**
```bash
# Encrypt with gpg before uploading
gpg -c ~/.sops/homelab-terraform.txt
# Upload homelab-terraform.txt.gpg to cloud
```
## Resources
- [SOPS Documentation](https://github.com/getsops/sops)
- [age Documentation](https://github.com/FiloSottile/age)
- [Terraform State Security](https://developer.hashicorp.com/terraform/language/state/sensitive-data)
- [OpenTofu Documentation](https://opentofu.org/docs/)
## Questions?
Common questions answered in this document:
- ✅ How do I set up SOPS? → See [Initial Setup](#initial-setup)
- ✅ How do I use it daily? → See [Option A: Automatic Wrapper](#option-a-automatic-wrapper-script-recommended)
- ✅ What if I lose my key? → See [Lost Private Key](#lost-private-key)
- ✅ How do I backup my key? → See [Backup Strategy](#backup-strategy)
- ✅ Can multiple people access? → See [Advanced: Multiple Keys](#advanced-multiple-keys-team-access)

View file

@ -114,7 +114,7 @@ resource "proxmox_virtual_environment_vm" "docker_host" {
# Cloud-init user data for Docker installation # Cloud-init user data for Docker installation
resource "proxmox_virtual_environment_file" "cloud_init_user_data" { resource "proxmox_virtual_environment_file" "cloud_init_user_data" {
content_type = "snippets" content_type = "snippets"
datastore_id = "local" datastore_id = var.snippets_storage
node_name = var.proxmox_node node_name = var.proxmox_node
source_raw { source_raw {

View file

@ -0,0 +1,76 @@
#!/usr/bin/env bash
#
# tf - Wrapper for OpenTofu/Terraform with automatic SOPS encryption/decryption
#
# Usage:
# ./scripts/tf init
# ./scripts/tf plan
# ./scripts/tf apply
# ./scripts/tf destroy
#
# This script automatically:
# 1. Decrypts state before running tofu commands
# 2. Runs your tofu command
# 3. Encrypts state after running tofu commands
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TF_DIR="$(dirname "$SCRIPT_DIR")"
cd "$TF_DIR"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
error() {
echo -e "${RED}ERROR: $1${NC}" >&2
exit 1
}
success() {
echo -e "${GREEN}✓ $1${NC}"
}
info() {
echo -e "${BLUE} $1${NC}"
}
# Check if tofu or terraform is installed
if command -v tofu &> /dev/null; then
TF_CMD="tofu"
elif command -v terraform &> /dev/null; then
TF_CMD="terraform"
else
error "Neither tofu nor terraform is installed"
fi
# Decrypt state if encrypted files exist
if [[ -f terraform.tfstate.enc || -f terraform.tfvars.enc ]]; then
info "Decrypting state files..."
"$SCRIPT_DIR/tf-decrypt"
echo
fi
# Run the terraform/tofu command
echo -e "${BLUE}Running: $TF_CMD $*${NC}"
echo
$TF_CMD "$@"
TF_EXIT_CODE=$?
# If the command succeeded and modified state, encrypt it
if [[ $TF_EXIT_CODE -eq 0 ]]; then
# Commands that modify state
if [[ "$1" =~ ^(apply|destroy|import|refresh|state)$ ]]; then
echo
info "Encrypting state files..."
"$SCRIPT_DIR/tf-encrypt"
fi
fi
exit $TF_EXIT_CODE

View file

@ -0,0 +1,87 @@
#!/usr/bin/env bash
#
# tf-decrypt - Decrypt Terraform state and tfvars files with SOPS
#
# Usage: ./scripts/tf-decrypt
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TF_DIR="$(dirname "$SCRIPT_DIR")"
cd "$TF_DIR"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
error() {
echo -e "${RED}ERROR: $1${NC}" >&2
exit 1
}
success() {
echo -e "${GREEN}✓ $1${NC}"
}
warn() {
echo -e "${YELLOW}⚠ $1${NC}"
}
# Check if SOPS is installed
if ! command -v sops &> /dev/null; then
error "sops is not installed. Install it from: https://github.com/getsops/sops/releases"
fi
# Check if .sops.yaml exists
if [[ ! -f .sops.yaml ]]; then
error ".sops.yaml not found. Copy .sops.yaml.example and configure your age key."
fi
# Check if SOPS_AGE_KEY_FILE is set or exists in default location
if [[ -z "${SOPS_AGE_KEY_FILE:-}" ]]; then
if [[ -f ~/.sops/homelab-terraform.txt ]]; then
export SOPS_AGE_KEY_FILE=~/.sops/homelab-terraform.txt
else
warn "SOPS_AGE_KEY_FILE not set. Trying default age identities..."
fi
fi
echo "🔓 Decrypting Terraform files..."
echo
# Decrypt terraform.tfstate.enc if it exists
if [[ -f terraform.tfstate.enc ]]; then
echo "Decrypting terraform.tfstate.enc..."
sops -d terraform.tfstate.enc > terraform.tfstate
success "terraform.tfstate.enc → terraform.tfstate"
else
warn "terraform.tfstate.enc not found (this is normal for first-time setup)"
fi
# Decrypt terraform.tfvars.enc if it exists
if [[ -f terraform.tfvars.enc ]]; then
echo "Decrypting terraform.tfvars.enc..."
sops -d terraform.tfvars.enc > terraform.tfvars
success "terraform.tfvars.enc → terraform.tfvars"
else
warn "terraform.tfvars.enc not found"
fi
# Decrypt backup state files if they exist
for backup_enc in terraform.tfstate.backup.enc terraform.tfstate.*.backup.enc; do
if [[ -f "$backup_enc" ]]; then
backup="${backup_enc%.enc}"
echo "Decrypting $backup_enc..."
sops -d "$backup_enc" > "$backup"
success "$backup_enc → $backup"
fi
done
echo
success "All Terraform files decrypted successfully!"
echo
warn "Remember to encrypt files after making changes: ./scripts/tf-encrypt"

View file

@ -0,0 +1,94 @@
#!/usr/bin/env bash
#
# tf-encrypt - Encrypt Terraform state and tfvars files with SOPS
#
# Usage: ./scripts/tf-encrypt
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TF_DIR="$(dirname "$SCRIPT_DIR")"
cd "$TF_DIR"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
error() {
echo -e "${RED}ERROR: $1${NC}" >&2
exit 1
}
success() {
echo -e "${GREEN}✓ $1${NC}"
}
warn() {
echo -e "${YELLOW}⚠ $1${NC}"
}
# Check if SOPS is installed
if ! command -v sops &> /dev/null; then
error "sops is not installed. Install it from: https://github.com/getsops/sops/releases"
fi
# Check if .sops.yaml exists
if [[ ! -f .sops.yaml ]]; then
error ".sops.yaml not found. Copy .sops.yaml.example and configure your age key."
fi
# Check if age key placeholder is still present
if grep -q "YOUR_AGE_PUBLIC_KEY_HERE" .sops.yaml; then
error ".sops.yaml contains placeholder. Replace YOUR_AGE_PUBLIC_KEY_HERE with your actual age public key."
fi
echo "🔐 Encrypting Terraform files..."
echo
# Encrypt terraform.tfstate if it exists
if [[ -f terraform.tfstate ]]; then
echo "Encrypting terraform.tfstate..."
sops -e terraform.tfstate > terraform.tfstate.enc
success "terraform.tfstate → terraform.tfstate.enc"
# Securely delete unencrypted state
shred -u terraform.tfstate 2>/dev/null || rm -f terraform.tfstate
success "Deleted unencrypted terraform.tfstate"
else
warn "terraform.tfstate not found (this is normal if you haven't run 'tofu apply' yet)"
fi
# Encrypt terraform.tfvars if it exists
if [[ -f terraform.tfvars ]]; then
echo "Encrypting terraform.tfvars..."
sops -e terraform.tfvars > terraform.tfvars.enc
success "terraform.tfvars → terraform.tfvars.enc"
# Keep original tfvars (don't delete, just warn)
warn "Remember to not commit unencrypted terraform.tfvars to Git"
else
warn "terraform.tfvars not found"
fi
# Encrypt backup state files if they exist
for backup in terraform.tfstate.backup terraform.tfstate.*.backup; do
if [[ -f "$backup" ]]; then
echo "Encrypting $backup..."
sops -e "$backup" > "${backup}.enc"
success "$backup → ${backup}.enc"
shred -u "$backup" 2>/dev/null || rm -f "$backup"
success "Deleted unencrypted $backup"
fi
done
echo
success "All Terraform files encrypted successfully!"
echo
echo "Next steps:"
echo " 1. git add *.enc"
echo " 2. git commit -m 'Update encrypted Terraform state'"
echo " 3. git push"

View file

@ -8,6 +8,12 @@ pm_tls_insecure = true
proxmox_node = "pve" proxmox_node = "pve"
template_vm_id = 9000 template_vm_id = 9000
storage = "local-lvm" storage = "local-lvm"
# Storage for cloud-init snippets (must support 'snippets' content type)
# Common options: "local" (directory storage), or create a directory storage
# See README.md for setup instructions if you get datastore errors
snippets_storage = "local"
network_bridge = "vmbr0" network_bridge = "vmbr0"
# VM Configuration # VM Configuration

View file

@ -59,11 +59,17 @@ variable "disk_size" {
} }
variable "storage" { variable "storage" {
description = "Storage pool name" description = "Storage pool name for VM disks"
type = string type = string
default = "local-lvm" default = "local-lvm"
} }
variable "snippets_storage" {
description = "Storage pool name for cloud-init snippets (must support 'snippets' content type)"
type = string
default = "local"
}
variable "network_bridge" { variable "network_bridge" {
description = "Network bridge" description = "Network bridge"
type = string type = string