Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 121 additions & 38 deletions configurations/terraform/proxmox-vm/README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,147 @@
# stuttgart-things/crossplane/proxmox-vm

## USAGE
Crossplane Configuration for provisioning Proxmox VMs using the OpenTofu provider.

<details><summary><b>RESOURCE</b></summary>
## Prerequisites

### Create OpenTofu ClusterProviderConfig

```bash
# GET ALL VMS
kubectl get proxmoxvm -A
kubectl apply -f examples/provider-config.yaml
```

# TRACE RESORUCE CREATION
crossplane beta trace proxmoxvm test-vm
### Create TFVars Secret

# EXAMPLE - ID VIA TRACE
kubectl describe Workspace/test-vm-hg4c8-tbqvw
The OpenTofu provider requires Proxmox credentials stored as a Kubernetes secret.
The secret must exist in the **same namespace as the ProxmoxVM XR** (typically `default`):

# DELETE VM
kubectl delete proxmoxvm test-vm -n default
```bash
kubectl create secret generic proxmox-tfvars \
-n default \
--from-literal=terraform.tfvars="$(cat <<EOF
pve_api_url = "<proxmox-api-url>"
pve_api_user = "<user>@<realm>"
pve_api_password = "<password>"
vm_ssh_user = "<ssh-user>"
vm_ssh_password = "<ssh-password>"
EOF
)"
```

</details>
If using SOPS-encrypted secrets:

```bash
kubectl create secret generic proxmox-tfvars \
-n default \
--from-literal=terraform.tfvars="$(sops --decrypt /path/to/pve-labul.tfvars.enc.json | \
python3 -c 'import json,sys; [print(f"{k} = \"{v}\"") for k,v in json.load(sys.stdin).items() if k != "sops"]')"
```

## CONFIGURATION
### Grant RBAC for OpenTofu Provider

<details><summary><b>CONFIGURE IN-CLUSTER PROVIDER</b></summary>
The OpenTofu provider service account needs permission to read secrets.
Adjust the service account name to match your cluster's provider revision.

```bash
kubectl apply -f - <<EOF
---
apiVersion: tf.upbound.io/v1beta1
kind: ProviderConfig
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: proxmox-vm
spec:
configuration: |
terraform {
backend "kubernetes" {
secret_suffix = "proxmox-vm-tfstate" # pragma: allowlist secret
namespace = "crossplane-system"
in_cluster_config = true
}
}
name: provider-opentofu-secrets
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: provider-opentofu-secrets
subjects:
- kind: ServiceAccount
name: <provider-opentofu-service-account>
namespace: crossplane-system
roleRef:
kind: ClusterRole
name: provider-opentofu-secrets
apiGroup: rbac.authorization.k8s.io
EOF
```

</details>
To find the correct service account name:

```bash
kubectl get sa -n crossplane-system | grep opentofu
```

## Claim Parameters

<details><summary><b>CREATE TFVARS AS SECRET</b></summary>
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `vm.name` | string | yes | - | VM name |
| `vm.count` | string | no | `"1"` | Number of VMs to create |
| `vm.cpu` | string | no | `"4"` | Number of vCPUs |
| `vm.ram` | string | no | `"4096"` | Memory in MB |
| `vm.disk` | string | no | `"32G"` | Disk size |
| `vm.firmware` | string | no | `"seabios"` | Firmware type |
| `vm.template` | string | yes | - | VM template name |
| `vm.annotation` | string | no | `PROXMOX-VM BUILD...` | VM notes |
| `proxmox.node` | string | yes | - | Proxmox cluster node |
| `proxmox.datastore` | string | yes | - | Proxmox datastore |
| `proxmox.folderPath` | string | no | - | VM folder path |
| `proxmox.network` | string | yes | - | Proxmox network bridge |
| `tfvars.secretName` | string | yes | - | Name of tfvars secret |
| `tfvars.secretKey` | string | no | `terraform.tfvars` | Key in the secret |
| `connectionSecret.name` | string | yes | - | Output connection secret name |
| `providerRef.name` | string | yes | - | Provider config reference |
| `providerRef.kind` | string | no | `ClusterProviderConfig` | `ProviderConfig` or `ClusterProviderConfig` |

## Usage Example

```yaml
apiVersion: resources.stuttgart-things.com/v1alpha1
kind: ProxmoxVM
metadata:
name: my-vm
namespace: default
spec:
providerRef:
name: default
kind: ClusterProviderConfig
vm:
name: my-vm
cpu: "4"
ram: "8192"
disk: "64G"
template: ubuntu24
proxmox:
node: sthings-pve1
datastore: v3700
folderPath: stuttgart-things
network: vmbr101
tfvars:
secretName: proxmox-tfvars
secretKey: terraform.tfvars
connectionSecret:
name: my-vm
```

## Development

### Render Composition Locally

```bash
# CREATE SECRET
kubectl create secret generic proxmox-tfvars --from-literal=terraform.tfvars="$(cat <<EOF
pve_api_url=""
pve_api_user="terraform@pve"
pve_api_password=""
pve_api_tls_verify = true
vm_ssh_user="sthings"
vm_ssh_password=""
EOF
)"
crossplane render examples/proxmox-vm.yaml compositions/proxmox-vm.yaml examples/functions.yaml \
--include-function-results
```

</details>
### Trace Resource

```bash
crossplane beta trace proxmoxvm my-vm -n default
```

## License

Apache-2.0
133 changes: 0 additions & 133 deletions configurations/terraform/proxmox-vm/apis/composition.yaml

This file was deleted.

Loading