├── .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 |
4 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Init.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Init__Upgrade.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/terraform.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/terraform.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
--------------------------------------------------------------------------------