├── .gitignore ├── .idea ├── modules.xml ├── runConfigurations │ ├── Apply.xml │ ├── Init.xml │ └── Init__Upgrade.xml ├── terraform.iml ├── terraform.xml └── vcs.xml ├── LICENSE ├── README.md ├── environment └── sample-container │ ├── main.tf │ ├── variables.tf │ ├── versions.tf │ └── vm-configuration.bu.tftpl ├── module └── ignition-vm │ ├── main.tf │ ├── providers.tf │ └── variables.tf └── scripts ├── descrption-to-ignition └── make_template.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't put any local credentials into git 2 | credentials.auto.tfvars 3 | 4 | # Default ignored files 5 | /shelf/ 6 | /workspace.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | 10 | 11 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 12 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 13 | 14 | # User-specific stuff 15 | .idea/**/workspace.xml 16 | .idea/**/tasks.xml 17 | .idea/**/usage.statistics.xml 18 | .idea/**/dictionaries 19 | .idea/**/shelf 20 | 21 | # AWS User-specific 22 | .idea/**/aws.xml 23 | 24 | # Generated files 25 | .idea/**/contentModel.xml 26 | 27 | 28 | # 29 | # Terraform 30 | # ========= 31 | 32 | # Directories 33 | # Local .terraform directories 34 | **/.terraform/* 35 | .vagrant/ 36 | 37 | # Compiled files 38 | *.tfstate 39 | *.tfstate.backup 40 | *.tfstate.lock.info 41 | .terraform.lock.hcl 42 | 43 | # logs 44 | *.log 45 | 46 | 47 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Apply.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Init.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Init__Upgrade.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 19 | -------------------------------------------------------------------------------- /.idea/terraform.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/terraform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Lucid Solutions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **DEPRECATED:** see [flatcar-ignition-proxmox](https://github.com/lucidsolns/terraform-flatcar-ignition-proxmox) which 2 | uses *cloud-init* rather than the *VM description* to transport the ignition file through the proxmox API. 3 | 4 | # A sample Proxmox Terraform Flatcar provisioning script 5 | 6 | This repository has a script to provision Flatcar VM images using 7 | Butane and Ignition from a preconfigured template VM. 8 | 9 | A helper script is provided to manually provision a Proxmox VM template for 10 | Flatcar. This is a versioned resource so that new versions of Flatcar can be 11 | used for new VM's. 12 | 13 | ## Usage 14 | 15 | To use the sample, set a `credentials.auto.tfvars` with the root username/password: 16 | 17 | ```terraform 18 | pm_user = "root@pam" 19 | pm_password = "password" 20 | ``` 21 | 22 | ## Template creation 23 | 24 | The `make_template.sh` script is provided to create a Proxmox template VM using the 25 | Flatcar linux production qemu image. 26 | 27 | Download the images (assuming a stable release is used): 28 | 29 | ```shell 30 | wget https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_qemu_uefi_efi_code.fd 31 | wget https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_qemu_uefi_efi_vars.fd 32 | wget https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_qemu_uefi_image.img.bz2 33 | bunzip2 flatcar_production_qemu_uefi_image.img.bz2 34 | ``` 35 | Create the template VM using the images. 36 | ```shell 37 | ./make_template.sh -n flatcar-production-qemu-3510.2.6 900 38 | ``` 39 | The *vars* image is used as the UEFI read-write variables device and the *code* image is a 40 | read-only UEFI code device. 41 | 42 | # Links 43 | 44 | - https://austinsnerdythings.com/2021/09/01/how-to-deploy-vms-in-proxmox-with-terraform/ 45 | 46 | ### Flatcar 47 | 48 | - https://www.flatcar.org/ 49 | - Flatcar releases (https://www.flatcar.org/releases) 50 | - https://www.flatcar.org/docs/latest/installing/vms/libvirt/ 51 | - https://github.com/flatcar/Flatcar/issues/430 52 | - https://github.com/flatcar/ignition 53 | 54 | ### Ignition 55 | - https://coreos.github.io/ignition 56 | - https://github.com/flatcar/ignition 57 | - https://www.iana.org/assignments/media-types/application/vnd.coreos.ignition+json 58 | 59 | ### Terraform 60 | 61 | - https://registry.terraform.io/providers/Telmate/proxmox/latest 62 | - https://registry.terraform.io/providers/Telmate/proxmox/latest/docs/resources/vm_qemu 63 | - https://github.com/poseidon/terraform-provider-ct 64 | - https://registry.terraform.io/providers/poseidon/ct/latest/docs 65 | - https://www.flatcar.org/docs/latest/provisioning/config-transpiler/ 66 | 67 | 68 | ### Proxmox 69 | - https://pve.proxmox.com/wiki/Manual:_qm.conf 70 | 71 | ### Qemu 72 | - https://github.com/qemu/qemu/blob/master/docs/specs/fw_cfg.rst 73 | - https://www.qemu.org/docs/master/specs/fw_cfg.html 74 | 75 | ### UEFI 76 | - https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/#uefi-is-not-bios 77 | 78 | ## Known Limitations 79 | 80 | As of August 2023, the following limitation and residuals have been observed: 81 | 82 | 1. The Proxmox API requires the 'root@pam' user to provision 'args'. Using 83 | an API key doesn't work, and using an API key for root also doesn't work, 84 | as the username 'root@pam!terraform' doesn't match the required identity of 'root@pam'. 85 | 86 | 2. The Qemu command line parsing requires to the Ignition configuration to have all 87 | comma's escaped with another comma (i.e. a double comma). 88 | 89 | 3. Ignition support in Flatcar Linux **Stable** is limited to Butane version 1.0.0 with the generated 90 | ignition files being v3.3.0 using ct provider v0.12 (**important**: not latest ct provider) 91 | 92 | 4. When creating a Proxmox UEFI VM with a pre-made image, the special `file=:0` 93 | syntax must be used. e.g. if the node local disk is called 'local' then the syntax would be: 94 | ``` 95 | --efidisk0 "file=local:0,import-from=flatcar_production_qemu_image.img,efitype=4m,format=raw,pre-enrolled-keys=1" 96 | ``` 97 | 98 | 5. Although the flatcar linux qemu image has a `.img` extension, it is 99 | a [qcow2](https://en.wikipedia.org/wiki/Qcow) formatted file. The image has multiple partitions. 100 | 101 | 6. The documentation isn't clear as to the correct way to mount UEFI code partitions as 102 | a read-only volume. It is unclear how to specify a pflash drive for the UEFI code. To see 103 | the Qemu configuration run `qm showcmd --pretty`, which shows the two EFI 104 | pflash drives. 105 | 106 | 7. Terraform Telmate/Proxmox provider doesn't support setting the hookscript upon create, thus 107 | the hookscript must be set in the template and inherited to child VM's. 108 | 109 | 8. The proxmox hook script locks the vm configuration - thus stopping the hook script 110 | from modifying/mutating the configuration. Even if the configuration is changed the 111 | Proxmox *start* code will not reload the changes after the hookscript runs. 112 | 113 | 9. There appears to be limitation on the length of the ignition file to about 8k. 114 | It is unclear where this limitation is imposed, as the internal code seems to limit 115 | the description field to 64kbytes. This renders the strategy of hijacking the description 116 | field as ineffective for all but trivial VM's. -------------------------------------------------------------------------------- /environment/sample-container/main.tf: -------------------------------------------------------------------------------- 1 | module "ignition-vm" { 2 | source = "../../module/ignition-vm" 3 | pm_api_url = var.pm_api_url 4 | pm_api_token_id = var.pm_api_token_id 5 | pm_api_token_secret = var.pm_api_token_secret 6 | pm_user = var.pm_user 7 | pm_password = var.pm_password 8 | target_node = "raisin" 9 | template_name = "flatcar-production-qemu-3510.2.6" 10 | butane_conf = "${path.module}/vm-configuration.bu.tftpl" 11 | name = "flatcar-sample-container" 12 | vm_id = 500 13 | network_tag = 109 14 | tags = ["sample", "flatcar"] 15 | vm_count = 1 16 | } 17 | -------------------------------------------------------------------------------- /environment/sample-container/variables.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * The API requires credentials. Use an API key (c.f. username/password), by going to the 3 | * web UI 'Datacenter' -> 'Permissions' -> 'API Tokens' and create a new set of credentials. 4 | * 5 | */ 6 | variable "pm_api_url" { 7 | description = "The proxmox api endpoint" 8 | default = "https://proxmox:8006/api2/json" 9 | } 10 | 11 | 12 | variable "pm_user" { 13 | description = "A username for password based authentication of the Proxmox API" 14 | type = string 15 | default = "root@pam" 16 | } 17 | 18 | variable "pm_password" { 19 | description = "A password for password based authentication of the Proxmox API" 20 | type = string 21 | sensitive = true 22 | default = "" 23 | } 24 | 25 | variable "pm_api_token_id" { 26 | default = "root@pam!terraform" 27 | } 28 | 29 | variable "pm_api_token_secret" { 30 | description = "Provide an API secret in a *.auto.tfvars file (or via some other mechanism)" 31 | default = "" 32 | sensitive = true 33 | } 34 | -------------------------------------------------------------------------------- /environment/sample-container/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = "~> 1.5.0" 4 | } -------------------------------------------------------------------------------- /environment/sample-container/vm-configuration.bu.tftpl: -------------------------------------------------------------------------------- 1 | # A butane configuration for the Flatcar VM, with support for terraform template substitution. 2 | # 3 | # see 4 | # - https://coreos.github.io/butane/config-flatcar-v1_1/ 5 | # - https://coreos.github.io/butane/ 6 | # 7 | version: 1.0.0 8 | variant: flatcar 9 | passwd: 10 | users: 11 | - name: core 12 | ssh_authorized_keys: 13 | - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFkyaM9D4TtCOSdIR8JvH5DCt0UHbfPGx7VlSJrP593N greg-ed25519 14 | storage: 15 | files: 16 | - path: /etc/systemd/network/static.network 17 | contents: 18 | inline: | 19 | [Match] 20 | Name=eth0 21 | 22 | [Network] 23 | Address=10.20.9.${30 + vm_count_index}/24 24 | Gateway=10.20.9.1 25 | -------------------------------------------------------------------------------- /module/ignition-vm/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | Provision a VM with an ignition configuration file. 3 | 4 | see 5 | - https://registry.terraform.io/providers/Telmate/proxmox/latest/docs/resources/vm_qemu 6 | - https://pve.proxmox.com/wiki/Manual:_qm.conf 7 | - https://github.com/qemu/qemu/blob/master/docs/specs/fw_cfg.rst 8 | - https://www.flatcar.org/docs/latest/installing/vms/libvirt/ 9 | - https://austinsnerdythings.com/2021/09/01/how-to-deploy-vms-in-proxmox-with-terraform/ 10 | - https://www.flatcar.org/ 11 | - https://github.com/flatcar/ignition 12 | - https://www.qemu.org/docs/master/specs/fw_cfg.html 13 | */ 14 | resource "proxmox_vm_qemu" "test_server" { 15 | count = var.vm_count # just want 1 for now, set to 0 and apply to destroy VM 16 | vmid = var.vm_count > 1 ? var.vm_id + count.index : var.vm_id 17 | name = var.vm_count > 1 ? "${var.name}-${count.index + 1}" : var.name 18 | target_node = var.target_node 19 | 20 | # Create a VM using the flatcar qemu image, and give it a version. This will mean 21 | # a linked clone can be used to reduce the storage requirements when a large number 22 | # of clones are created. 23 | # 24 | # Download the flatcar_qemu image from https://www.flatcar.org/releases and 25 | clone = var.template_name 26 | full_clone = false 27 | clone_wait = 0 28 | 29 | /* 30 | The 'arguments' parameter for QEMU is parsed in such a way that the string is not 31 | treated opaquely. The options are split at comma's (',') causing ambiguity with 32 | options without a value. 33 | 34 | The doubling up of comma's in the fw_cfg configuration overcomes this limitation. 35 | This is documented in the Qemu documentation in the 'blockdev drive -file' section. 36 | 37 | Setting this args parameter when creating a VM requires local root access 38 | with password authentication. 39 | */ 40 | args = "-fw_cfg name=opt/org.flatcar-linux/config,file=/etc/pve/local/ignition/${var.vm_count > 1 ? var.vm_id + count.index : var.vm_id}.ign" 41 | desc = "data:application/vnd.coreos.ignition+json;charset=UTF-8;base64,${base64encode(data.ct_config.ignition_json[count.index].rendered)}" 42 | 43 | # The qemu agent must be running in the flatcar instance so that Proxmox can 44 | # identify when the VM is up (see https://github.com/flatcar/Flatcar/issues/737) 45 | agent = 1 46 | timeouts { 47 | # use terraform timeouts instead of 'guest_agent_ready_timeout' 48 | create = "60s" 49 | update = "60s" 50 | default = "120s" 51 | } 52 | 53 | define_connection_info = false # ssh connection info is defined in the ignition configuration 54 | 55 | # 56 | bios = "ovmf" # UEFI boot 57 | 58 | # There seems to be an issue here with duplication 59 | os_type = var.os_type # qemu identifier 60 | qemu_os = var.os_type # qemu identifier 61 | 62 | cores = var.cores 63 | sockets = 1 64 | cpu = var.cpu 65 | memory = var.memory 66 | tags = join(";", sort(var.tags)) # Proxmox sorts the tags, so sort them here to stop change thrash 67 | onboot = true 68 | scsihw = "virtio-scsi-single" 69 | 70 | # if you want two NICs, just copy this whole network section and duplicate it 71 | network { 72 | model = "virtio" 73 | bridge = var.network_bridge 74 | tag = var.network_tag 75 | } 76 | 77 | lifecycle { 78 | prevent_destroy = false # this resource should be immutable **and** disposable 79 | create_before_destroy = false 80 | ignore_changes = [ 81 | disk # the disk is provisioned in the template and inherited (but not defined here] 82 | ] 83 | replace_triggered_by = [ 84 | null_resource.node_replace_trigger[count.index].id 85 | ] 86 | } 87 | } 88 | 89 | /** 90 | Convert a butane configuration to an ignition JSON configuration. The template supports 91 | multiple instances (a count) so that each configuration can be slightly changed. 92 | 93 | see 94 | - https://github.com/poseidon/terraform-provider-ct 95 | - https://registry.terraform.io/providers/poseidon/ct/latest 96 | - https://registry.terraform.io/providers/poseidon/ct/latest/docs 97 | - https://www.flatcar.org/docs/latest/provisioning/config-transpiler/ 98 | - https://developer.hashicorp.com/terraform/language/functions/templatefile 99 | */ 100 | data "ct_config" "ignition_json" { 101 | count = var.vm_count 102 | content = templatefile(var.butane_conf, { 103 | "vm_id" = var.vm_count > 1 ? var.vm_id + count.index : var.vm_id 104 | "vm_name" = var.vm_count > 1 ? "${var.name}-${count.index + 1}" : var.name 105 | "vm_count" = var.vm_count, 106 | "vm_count_index" = count.index, 107 | }) 108 | strict = false 109 | pretty_print = true 110 | 111 | snippets = [ 112 | for snippet in var.butane_conf_snippets : templatefile(var.butane_conf, { 113 | "vm_id" = var.vm_count > 1 ? var.vm_id + count.index : var.vm_id 114 | "vm_name" = var.vm_count > 1 ? "${var.name}-${count.index + 1}" : var.name 115 | "vm_count" = var.vm_count, 116 | "vm_count_index" = count.index, 117 | }) 118 | ] 119 | } 120 | 121 | /** 122 | A null resource to track changes, so that the immutable VM is recreated 123 | */ 124 | resource "null_resource" "node_replace_trigger" { 125 | count = var.vm_count 126 | # Changes to any instance of the cluster requires re-provisioning 127 | triggers = { 128 | "ignition" = "${data.ct_config.ignition_json[count.index].rendered}" 129 | } 130 | } -------------------------------------------------------------------------------- /module/ignition-vm/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5.0" 3 | 4 | required_providers { 5 | /* 6 | API provisioning support for Proxmox 7 | see 8 | - https://registry.terraform.io/providers/Telmate/proxmox/latest 9 | */ 10 | proxmox = { 11 | source = "telmate/proxmox" 12 | version = "2.9.14" 13 | } 14 | 15 | /* 16 | Convert a butane configuration to an ignition JSON configuration 17 | 18 | WARNING: The current flatcar stable release requires ignition v3.3.0 configurations, which 19 | are supported by the v0.12 provider. The v0.13 CT provider generated v3.4.0 ignition 20 | configurations which are not supported with Flatcar v3510.2.6. This is all clearly documented in 21 | the git [README.md](https://github.com/poseidon/terraform-provider-ct) 22 | 23 | see 24 | - https://github.com/poseidon/terraform-provider-ct 25 | - https://registry.terraform.io/providers/poseidon/ct/latest 26 | - https://registry.terraform.io/providers/poseidon/ct/latest/docs 27 | - https://www.flatcar.org/docs/latest/provisioning/config-transpiler/ 28 | */ 29 | ct = { 30 | source = "poseidon/ct" 31 | version = "0.12.0" 32 | } 33 | 34 | /* 35 | see 36 | - https://registry.terraform.io/providers/hashicorp/null 37 | */ 38 | null = { 39 | source = "hashicorp/null" 40 | version = "3.2.1" 41 | } 42 | } 43 | } 44 | 45 | provider "proxmox" { 46 | pm_api_url = var.pm_api_url 47 | pm_tls_insecure = var.pm_tls_insecure 48 | 49 | pm_user = var.pm_user 50 | pm_password = var.pm_password 51 | pm_api_token_id = var.pm_api_token_id 52 | pm_api_token_secret = var.pm_api_token_secret 53 | } -------------------------------------------------------------------------------- /module/ignition-vm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | default = "Flatcar-Linux" 4 | description = "The base name of the VM" 5 | } 6 | 7 | variable "butane_conf" { 8 | type = string 9 | description = "YAML Butane configuration for the VM" 10 | } 11 | variable "butane_conf_snippets" { 12 | type = list(string) 13 | default = [] 14 | description = "Additional YAML Butane configuration(s) for the VM" 15 | } 16 | variable "target_node" { 17 | description = "The name of the target proxmox node" 18 | type = string 19 | } 20 | variable "storage" { 21 | description = "The name of the storage used for storing VM images" 22 | type = string 23 | default = "local" 24 | } 25 | /* 26 | The name of a VM that has been converted to a template. The creation of this 27 | process is manual, and has not been automated. The flatcar qemu image is a qcow2 28 | image, and not a raw disk image as the name implies. 29 | 30 | Steps: 31 | 1. Download the latest flatcar qemu image (noting the version number) 32 | > wget https://stable.release.flatcar-linux.net/amd64-usr/3510.2.6/flatcar_production_qemu_image.img.bz2 33 | 2. Create a new VM with the name 'flatcar-production-qemu-' (e.g. flatcar-production-qemu-3510.2.6) 34 | - UEFI boot 35 | - with no network 36 | - with no disks 37 | - delete the CDROM 38 | 3. Decompress the flatcar qcow2 image 39 | > bunzip2 flatcar_production_qemu_image.img.bz2 40 | 4. Add the qcow2 image to the VM 41 | > qm importdisk 900 flatcar_production_qemu_image.img vmroot --format qcow2 42 | 5. Adopt the new disk into the VM 43 | > qm set 900 -efidisk0 vm-900-disk-0.qcow2:0,format=qcow2,efitype=4m,pre-enrolled-keys=1 44 | 6. Convert the VM to a Template 45 | 46 | see 47 | - https://www.flatcar.org/releases 48 | */ 49 | variable "vm_count" { 50 | description = "The number of VMs to provision" 51 | type = number 52 | default = 1 53 | } 54 | variable "template_name" { 55 | description = "The name of the Proxmox Flatcar template VM" 56 | type = string 57 | default = "flatcar_qemu" 58 | } 59 | variable "vm_id" { 60 | type = number 61 | default = 0 62 | } 63 | variable "cores" { 64 | type = number 65 | default = 2 66 | } 67 | variable "cpu" { 68 | type = string 69 | default = "host" 70 | description = "e.g. x86-64-v2-AES" 71 | } 72 | variable "memory" { 73 | type = number 74 | default = 2048 75 | } 76 | variable "network_bridge" { 77 | type = string 78 | default = "vmbr0" 79 | } 80 | variable "network_tag" { 81 | type = number 82 | default = 9 83 | } 84 | variable "os_type" { 85 | type = string 86 | default = "l26" 87 | description = "The short OS name identifier" 88 | } 89 | 90 | variable "os_type_name" { 91 | type = string 92 | default = "Linux 2.6 - 6.X Kernel" 93 | description = "os_type_name='Linux 2.6 - 6.X Kernel'" 94 | } 95 | 96 | 97 | variable "pm_api_url" { 98 | description = "The FQDN and path to the API of the proxmox server e.g. https://example.com:8006/api2/json" 99 | type = string 100 | } 101 | 102 | variable "pm_api_token_id" { 103 | description = "user@pam!token_id" 104 | type = string 105 | default = "root@pam" 106 | } 107 | 108 | 109 | /* 110 | The API secret from the proxmox datacenter. 111 | 112 | The identity must have the permission 'PVEVMAdmin' to the correct path ('/'). Due to possible issues 113 | in the API and hoe authorisation is performed, the 114 | 115 | With the incorrect permissions, the following error is generated: 116 | Error: user greg@pam has valid credentials but cannot retrieve user list, check privilege 117 | separation of api token 118 | Which corresponds to the following GET from /var/log/pveproxy/access.log 119 | GET /api2/json/access/users?full=1 120 | 121 | Required Privileges 122 | =================== 123 | 124 | user must be 'root@pam' <=== ugly 125 | userid-group, Sys.Audit -> GET users 126 | 127 | see 128 | - https://forum.proxmox.com/threads/root-pam-token-api-restricted.83866/ 129 | - https://pve.proxmox.com/pve-docs/api-viewer/index.html#/access/users 130 | - https://github.com/Telmate/terraform-provider-proxmox/issues/385 131 | - https://bugzilla.proxmox.com/show_bug.cgi?id=4068 132 | */ 133 | variable "pm_api_token_secret" { 134 | description = "secret hash" 135 | type = string 136 | sensitive = true 137 | default = "" 138 | } 139 | 140 | variable "pm_user" { 141 | description = "A username for password based authentication of the Proxmox API" 142 | type = string 143 | default = "" 144 | } 145 | 146 | variable "pm_password" { 147 | description = "A password for password based authentication of the Proxmox API" 148 | type = string 149 | sensitive = true 150 | default = "" 151 | } 152 | 153 | variable "pm_tls_insecure" { 154 | description = "leave tls_insecure set to true unless you have a valid proxmox SSL certificate " 155 | default = true 156 | type = bool 157 | } 158 | variable "tags" { 159 | description = "Tags to apply to the VM" 160 | type = list(string) 161 | default = ["flatcar"] 162 | } 163 | -------------------------------------------------------------------------------- /scripts/descrption-to-ignition: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # A script to transition a data URL from a VM image into a file that is 4 | # deployed as part of a VM configuration. 5 | # 6 | # This strategy is really good, right up to the point that the size of the 7 | # description field is too large. This strategy works for small VM's 8 | # with a minimal ignition configuration. 9 | # 10 | # The script 'cloudinit-to-ignition' overcomes this limitation with a hack 11 | # that the cloudinit configuration is used to transport the ignition 12 | # configuration 13 | # 14 | # see: 15 | # - /usr/share/pve-docs/examples/guest-example-hookscript.pl 16 | # 17 | set -eu 18 | 19 | function configure() 20 | { 21 | local VM_ID=$1 22 | 23 | # Get the VM Proxmox config 24 | local VM_CONFIG="$(qm config ${VM_ID})" 25 | 26 | # Get the ignition configuration from the description field of the VM 27 | local DATA_URL_BASE64=$(printf "%s" "${VM_CONFIG}" | sed -ne 's/^description: data\(:\|%3A\)application\/vnd.coreos.ignition+json[^,]*;base64[^,]*,\(.*\)/\2/gp') 28 | if [ -n "${DATA_URL_BASE64}" ] ; then 29 | # Write ignition JSON configuration 30 | mkdir -p /etc/pve/local/ignition 31 | printf "%s" "${DATA_URL_BASE64}"| base64 -d > "/etc/pve/local/ignition/${VM_ID}.ign" 32 | 33 | 34 | # Check the fw_cfg argument is set (and points to the ignition configuration written above) 35 | local FW_CFG=$(printf "%s" "${VM_CONFIG}" | sed -ne 's/^args: .*\(-fw_cfg name=opt\/org.flatcar-linux\/config,file=[^\\ ]*\)/\1/gp') 36 | if [ -n "$FW_CFG" ] ; then 37 | return # fw_cfg set correctly 38 | else 39 | echo "Error: The VM configuration must contains additional Qemu arguments of -fw_cfg name=opt/org.flatcar-linux/config,file=/etc/pve/local/ignition/${VM_ID}.ign" 40 | exit 5 41 | fi 42 | 43 | exit 0 44 | else 45 | echo "Data URL in the VM configuration descrption field not present" 46 | exit 0 47 | fi 48 | } 49 | 50 | 51 | 52 | PHASE=$2 53 | if [ "${PHASE}" = 'pre-start' ]; then 54 | configure $1 55 | elif [ "${PHASE}" = 'post-start' ]; then 56 | exit 0 57 | elif [ "${PHASE}" = 'pre-stop' ]; then 58 | exit 0 59 | elif [ "${PHASE}" = 'post-stop' ]; then 60 | exit 0 61 | else 62 | echo "Invalid hook script phase ${PHASE}" 63 | exit 1 64 | fi 65 | -------------------------------------------------------------------------------- /scripts/make_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # A script to create a flatcar template VM 4 | # 5 | # (c) Lucid Solutions 2023 6 | # 7 | # example usage: 8 | # > make_template -i flatcar_production_qemu_uefi_image.img -n flatcar-production-qemu-3510.2.6 900 9 | # 10 | set -eu 11 | 12 | usage(){ 13 | >&2 cat <<-EOF 14 | Usage: $0 15 | [ -d | --datastore ] 16 | [ -v | --verbose ] 17 | [ -f | --force ] 18 | [ -n | --name ] 19 | [ -i | --osimage ] 20 | [ --varsimage ] 21 | [ --codeimage ] 22 | [ -h | --help ] 23 | 24 | EOF 25 | exit 1 26 | } 27 | 28 | OPTS=$(/usr/bin/getopt -o d:vhn:i: --long verbose,help,name:,osimage:,varsimage:,codeimage:,datastore -n 'make_template' -- "$@") 29 | if [ $? != 0 ] ; then usage >&2 ; exit 1 ; fi 30 | eval set -- "$OPTS" 31 | 32 | VERBOSE=false 33 | VARS_IMAGE=flatcar_production_qemu_uefi_efi_vars.fd 34 | CODE_IMAGE=flatcar_production_qemu_uefi_efi_code.fd 35 | OS_IMAGE=flatcar_production_qemu_uefi_image.img 36 | NAME=flatcar-linux 37 | DATASTORE=local 38 | 39 | while true; do 40 | case "$1" in 41 | -v | --verbose ) VERBOSE=true; shift ;; 42 | -h | --help ) usage; exit 1 ;; 43 | -n | --name ) NAME="$2"; shift 2 ;; 44 | --varsimage ) VARS_IMAGE="$2"; shift 2 ;; 45 | --codeimage ) CODE_IMAGE="$2"; shift 2 ;; 46 | -i | --image ) OS_IMAGE="$2"; shift 2 ;; 47 | -d | --datastore ) DATASTORE="$2"; shift 2 ;; 48 | -- ) shift; break ;; 49 | * ) break ;; 50 | esac 51 | done 52 | 53 | if [[ $# -ne 1 ]]; then 54 | usage 55 | fi 56 | VM_ID=$1 57 | 58 | [ -n "$VERBOSE" ] && echo "Create VM ${VM_ID} (${NAME} from ${OS_IMAGE})" 59 | qm create ${VM_ID} \ 60 | --name "${NAME}" \ 61 | --agent=1 \ 62 | --bios=ovmf \ 63 | --cores=1 \ 64 | --ostype l26 \ 65 | --scsihw virtio-scsi-single \ 66 | --boot order=scsi0 \ 67 | --hookscript ${DATASTORE}:snippets/description-to-ignition \ 68 | --efidisk0 "file=${DATASTORE}:0,import-from=$(readlink -e ${VARS_IMAGE}),format=raw,efitype=4m,pre-enrolled-keys=1" \ 69 | --scsi0 "file=${DATASTORE}:0,import-from=$(readlink -e ${OS_IMAGE}),format=qcow2" \ 70 | --description "Flatcar Template with Ignition configuration" \ 71 | --tag "flatcar,template" 72 | 73 | # Convert the VM to a template 74 | [ -n "$VERBOSE" ] && echo "Convert VM ${VM_ID} to a template" 75 | qm template ${VM_ID} 76 | 77 | [ -n "$VERBOSE" ] && echo "Template VM ${VM_ID} done" 78 | --------------------------------------------------------------------------------