Personal Homelab
1locals {
2 // Add secrets into quadlets config
3 containers_config = merge(var.containers_config, {
4 proxmox_ip : var.proxmox_config.host,
5 truenas_ip : var.fcos_config.truenas_ip,
6 fcos_ip : var.fcos_config.ip,
7 })
8
9 # Get a list of all files in the specified directory
10 config_paths = fileset("${path.module}/configs", "**")
11 config_files = {
12 for cfgpath in local.config_paths :
13 replace(cfgpath, ".tftpl", "") => templatefile("${path.module}/configs/${cfgpath}", local.containers_config)
14 }
15 # Terraform hasn't directory alternative for fileset method
16 config_dirs = provider::homelab-helpers::dirset("${path.module}/configs", "**")
17
18 butane_config = merge(var.fcos_config, {
19 config_files : local.config_files,
20 config_dirs : local.config_dirs,
21 })
22
23 config_rendered_files = {
24 for path, content in local.config_files :
25 path => content
26 }
27
28 init_script_path = "${path.module}/scripts/init_fcos.sh.tftpl"
29 get_secret_script_path = "${path.module}/scripts/get_secret.sh"
30}
31
32output "directories_to_create" {
33 value = local.config_dirs
34}
35
36data "ct_config" "fcos_ignition" {
37 content = templatefile("${path.module}/butane/fcos.yml.tftpl", local.butane_config)
38 strict = true
39}
40
41resource "proxmox_virtual_environment_vm" "fcos" {
42 node_name = "pve"
43 name = "fcos"
44 description = "Managed by OpenTofu"
45
46 lifecycle {
47 ignore_changes = [
48 disk["file_id"],
49 kvm_arguments
50 ]
51 }
52
53 # Use modern platform
54 machine = "q35"
55 bios = "ovmf"
56 scsi_hardware = "virtio-scsi-single"
57
58 startup {
59 order = 20
60 }
61
62 cpu {
63 cores = 16
64 type = "Skylake-Client-v4"
65 }
66
67 memory {
68 dedicated = 32768
69 floating = 32768
70 }
71
72 efi_disk {
73 datastore_id = "local-zfs"
74 type = "4m"
75 pre_enrolled_keys = true
76 }
77
78 disk {
79 interface = "virtio0"
80 datastore_id = "local-zfs"
81 file_id = proxmox_virtual_environment_file.fcos_qcow2.id
82 size = 32
83 }
84
85 tpm_state {
86 datastore_id = "local-zfs"
87 }
88
89 network_device {
90 bridge = "vmbr0"
91 vlan_id = 100
92 mac_address = var.fcos_config.mac_address
93 }
94
95 # Linux 6.x
96 operating_system {
97 type = "l26"
98 }
99
100 # Intel Arc Pro B50 video
101 hostpci {
102 device = "hostpci0"
103 id = "0000:03:00.1"
104 pcie = true
105 rombar = true
106 }
107
108 agent {
109 enabled = true
110 }
111
112 kvm_arguments = "-fw_cfg 'name=opt/com.coreos/config,string=${replace(data.ct_config.fcos_ignition.rendered, ",", ",,")}' -smbios type=11,value=io.systemd.credential:bws-access-token=${var.bws_access_token}"
113}
114
115resource "null_resource" "fcos_provision_secrets" {
116 depends_on = [proxmox_virtual_environment_vm.fcos]
117
118 triggers = {
119 checksum = sha256(file(local.init_script_path))
120 }
121
122 connection {
123 type = "ssh"
124 user = "core"
125 private_key = file(pathexpand(var.fcos_config.ssh_private_key_path))
126 host = var.fcos_config.ip
127 }
128
129 provisioner "file" {
130 destination = "/var/tmp/init.sh"
131 content = templatefile(local.init_script_path, {
132 config_files : local.config_files
133 secret_uuids : var.containers_secret_config
134 })
135 }
136
137 provisioner "file" {
138 destination = "/home/core/.local/bin/get_secret.sh"
139 content = file(local.get_secret_script_path)
140 }
141
142 provisioner "remote-exec" {
143 inline = ["sh /var/tmp/init.sh"]
144 on_failure = fail
145 }
146}
147
148resource "null_resource" "sync_configs" {
149 depends_on = [proxmox_virtual_environment_vm.fcos]
150
151 triggers = {
152 configs_hash = provider::homelab-helpers::dirhash("${path.module}/configs", "**")
153 }
154
155 connection {
156 type = "ssh"
157 user = "core"
158 private_key = file(pathexpand(var.fcos_config.ssh_private_key_path))
159 host = var.fcos_config.ip
160 }
161
162 // Create directories if not exist
163 provisioner "remote-exec" {
164 inline = distinct([
165 for path, _ in local.config_rendered_files :
166 "mkdir -p /var/home/core/.config/${replace(dirname(path), "\\", "/")}"
167 ])
168 }
169
170 // Copy files, but remove the last byte to avoid double newline
171 provisioner "remote-exec" {
172 inline = [
173 for path, content in local.config_rendered_files : <<-EOT
174 cat <<'EOF' | head -c -1 > "/var/home/core/.config/${path}"
175 ${content}
176 EOF
177 EOT
178 ]
179 }
180
181 provisioner "remote-exec" {
182 inline = [
183 "systemctl --user daemon-reload"
184 ]
185 }
186}