Skip to content

Commit aa2a54f

Browse files
committed
Squashed Changes to Merge Change for Windows 2022 support for Proxmox provider
1 parent 61997b7 commit aa2a54f

20 files changed

Lines changed: 1238 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ The table below shows the currently provided operating systems for each provider
3636
| Ubuntu 22.04 || 💙 || 💙 |||||||| 💙 |||||||
3737
| Ubuntu 24.04 || 💙 || 💙 |||||||| 💙 |||||||
3838
| Windows 2019 || 💙 |||||||||||||||||
39-
| Windows 2022 || 💙 |||||||||||| |||||
39+
| Windows 2022 || 💙 |||||||||||| |||||
4040
| Windows 2025 || 💙 |||||||||||||||||
4141
| Windows Annual |||||||||||||||||||
4242

docs/book/src/capi/providers/proxmox.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ The image build process expects a few things to be in place before the build pro
1313
1. DHCP must be available to assign the packer VM an IP
1414
* The packer proxmox integration currently does not support the ability to assign static IPs, thus DHCP is required.
1515
* Access to internet hosts is optional, but the VM will not be able to apply any current updates and will need to be manually rebooted to get a clean cloud-init status.
16-
2. The build VM must be accessible via SSH from the host running `make build-proxmox...`
16+
2. The build VM must be accessible via SSH or Winrm from the host running `make build-proxmox...`
17+
* Linux builds use SSH.
18+
* Windows builds use WinRM.
1719
3. The build VM must have DHCP, DNS, HTTP, HTTPS and NTP accessibility to successfully update the OS packages.
1820

1921
## Building Images
@@ -38,6 +40,7 @@ the different operating systems.
3840
|--------------------|-----------------------------------------|
3941
| `ubuntu-2204.json` | The settings for the Ubuntu 22.04 image |
4042
| `ubuntu-2404.json` | The settings for the Ubuntu 24.04 image |
43+
| `windows-2022.json` | The settings for the Windows Server 2022 image |
4144

4245
The full list of available environment vars can be found in the `variables` section of `images/capi/packer/proxmox/packer.json`.
4346

@@ -84,6 +87,45 @@ export PACKER_FLAGS="--var 'oem_id=qemu'"
8487
make build-proxmox-flatcar
8588
```
8689

90+
### Building images for Windows
91+
92+
Windows builds require a Windows installation ISO and a Windows administrator
93+
password in addition to the standard Proxmox credentials.
94+
95+
Set the following environment variables before running the build:
96+
97+
```bash
98+
export PROXMOX_URL="https://pve.example.com:8006/api2/json"
99+
export PROXMOX_USERNAME=<USERNAME>
100+
export PROXMOX_TOKEN=<TOKEN_ID>
101+
export PROXMOX_NODE="pve"
102+
export PROXMOX_ISO_POOL="local"
103+
export PROXMOX_BRIDGE="vmbr0"
104+
export PROXMOX_STORAGE_POOL="local-lvm"
105+
export ISO_FILE="local:iso/en-us_windows_server_2022_x64.iso"
106+
export WINDOWS_ADMIN_PASSWORD='<PASSWORD>'
107+
```
108+
109+
The Proxmox Windows build also expects a VirtIO driver ISO to be available in the
110+
selected ISO storage. By default the build uses:
111+
112+
```bash
113+
local:iso/virtio-win-0.1.285.iso
114+
```
115+
116+
If your environment uses a different VirtIO image name or location, override it
117+
with `PACKER_FLAGS`:
118+
119+
```bash
120+
export PACKER_FLAGS="--var 'iso_virtio=local:iso/virtio-win-0.1.285.iso'"
121+
```
122+
123+
Build the Windows Server 2022 template with:
124+
125+
```bash
126+
make build-proxmox-windows-2022
127+
```
128+
87129
### Example
88130

89131
Prior to building images you need to ensure you have set the required environment variables:
@@ -104,6 +146,12 @@ export PROXMOX_STORAGE_POOL="local-lvm"
104146
make build-proxmox-ubuntu-2204
105147
```
106148

149+
- Build Windows Server 2022 template:
150+
151+
```bash
152+
make build-proxmox-windows-2022
153+
```
154+
107155
### Note on disk formats
108156

109157
Depending on what storage type you are using, you will need to change the default disk

images/capi/Makefile

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ deps-huaweicloud: deps-common
173173
.PHONY: deps-proxmox
174174
deps-proxmox: ## Installs/checks dependencies for Proxmox builds
175175
deps-proxmox: deps-common
176+
hack/ensure-ansible-windows.sh
176177
$(PACKER) init packer/config.pkr.hcl
177178
$(PACKER) init packer/proxmox/config.pkr.hcl
178179

@@ -403,7 +404,7 @@ NUTANIX_BUILD_NAMES ?= nutanix-ubuntu-2204 nutanix-ubuntu-2404 nutanix-rhel-9 nu
403404

404405
HCLOUD_BUILD_NAMES ?= hcloud-ubuntu-2204 hcloud-ubuntu-2404 hcloud-rockylinux-9 hcloud-flatcar hcloud-flatcar-arm64
405406

406-
PROXMOX_BUILD_NAMES ?= proxmox-ubuntu-2204 proxmox-ubuntu-2404 proxmox-ubuntu-2404-efi proxmox-rockylinux-9 proxmox-flatcar
407+
PROXMOX_BUILD_NAMES ?= proxmox-ubuntu-2204 proxmox-ubuntu-2404 proxmox-ubuntu-2404-efi proxmox-rockylinux-9 proxmox-flatcar proxmox-windows-2022
407408

408409
VULTR_BUILD_NAMES ?= vultr-ubuntu-2204 vultr-ubuntu-2404
409410

@@ -644,7 +645,11 @@ $(HCLOUD_VALIDATE_TARGETS): deps-hcloud
644645

645646
.PHONY: $(PROXMOX_BUILD_TARGETS)
646647
$(PROXMOX_BUILD_TARGETS): deps-proxmox set-ssh-password
647-
$(PACKER) build $(PACKER_NODE_FLAGS) -var-file="$(abspath packer/proxmox/$(subst build-proxmox-,,$@).json)" $(ABSOLUTE_PACKER_VAR_FILES) packer/proxmox/packer.json
648+
# This uses a packer file builder to input unattend variables into a JSON file to be consumed by the python script before running the vsphere provisioner
649+
$(if $(findstring windows,$@),$(PACKER) build $(PACKER_WINDOWS_NODE_FLAGS) -var-file="$(abspath packer/proxmox/$(subst build-proxmox-,,$@).json)" -var-file="$(abspath packer/proxmox/$(subst build-proxmox-,,$@).json)" -only=file $(ABSOLUTE_PACKER_VAR_FILES) packer/proxmox/packer-windows.json,)
650+
$(if $(findstring windows,$@),hack/windows-unattend.py --unattend-file='./packer/proxmox/windows/$(subst build-proxmox-,,$@)/autounattend.xml',)
651+
$(PACKER) build $(if $(findstring windows,$@),$(PACKER_WINDOWS_NODE_FLAGS),$(PACKER_NODE_FLAGS)) -var-file="$(abspath packer/proxmox/$(subst build-proxmox-,,$@).json)" $(ABSOLUTE_PACKER_VAR_FILES) packer/proxmox/packer$(if $(findstring windows,$@),-windows,).json
652+
648653

649654
.PHONY: $(PROXMOX_VALIDATE_TARGETS)
650655
$(PROXMOX_VALIDATE_TARGETS): deps-proxmox set-ssh-password
@@ -859,6 +864,11 @@ build-proxmox-ubuntu-2404: ## Builds Ubuntu 24.04 Proxmox image
859864
build-proxmox-ubuntu-2404-efi: ## Builds Ubuntu 24.04 Proxmox image that EFI boots
860865
build-proxmox-rockylinux-9: ## Builds Rocky Linux 9 Proxmox image
861866
build-proxmox-flatcar: ## Builds Flatcar Proxmox image
867+
build-proxmox-ubuntu-2204: ## Builds the Proxmox ubuntu-2204 image
868+
build-proxmox-ubuntu-2404: ## Builds the Proxmox ubuntu-2404 image
869+
build-proxmox-ubuntu-2404-efi: ## Builds the Proxmox ubuntu-2404-efi image that EFI boots
870+
build-proxmox-rockylinux-9: ## Builds the Proxmox rockylinux-9 image
871+
build-proxmox-windows-2022: ## Builds the Proxmox Windows 2022 image
862872
build-proxmox-all: $(PROXMOX_BUILD_TARGETS) ## Builds all Proxmox images
863873

864874
build-vultr-ubuntu-2204: ## Builds Ubuntu 22.04 Vultr Snapshot

images/capi/ansible/windows/roles/cloudbase-init/templates/cloudbase-init.conf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,12 @@ netbios_host_name_compatibility={{ netbios_host_name_compatibility }}
3434

3535
metadata_services={{ cloudbase_metadata_services }}
3636
plugins={{ cloudbase_plugins }}
37+
{% if (cloudbase_nocloud_metadata_file | default('') | length > 0) or (cloudbase_nocloud_networkdata_file | default('') | length > 0) or (cloudbase_nocloud_userdata_file | default('') | length > 0) %}
38+
39+
[nocloud]
40+
metadata_file={{ cloudbase_nocloud_metadata_file | default('meta-data', true) }}
41+
{% if cloudbase_nocloud_networkdata_file | default('') | length > 0 %}
42+
networkdata_file={{ cloudbase_nocloud_networkdata_file }}
43+
{% endif %}
44+
userdata_file={{ cloudbase_nocloud_userdata_file | default('user-data', true) }}
45+
{% endif %}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Copyright 2026 The Kubernetes Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Apply Proxmox NoCloud network data from a config-drive file."""
16+
17+
import logging
18+
import os
19+
import string
20+
import sys
21+
22+
try:
23+
from oslo_log import log as oslo_logging
24+
25+
LOG = oslo_logging.getLogger(__name__)
26+
except Exception: # pragma: no cover - fallback when oslo logging is unavailable
27+
logging.basicConfig(level=logging.INFO)
28+
LOG = logging.getLogger(__name__)
29+
30+
31+
DEFAULT_NETWORK_DATA_FILENAMES = ("NETWORK_CONFIG", "network-config")
32+
33+
34+
def _iter_search_roots():
35+
search_roots = os.environ.get("PROXMOX_NETWORK_DATA_SEARCH_ROOTS")
36+
if search_roots:
37+
for root in search_roots.split(os.pathsep):
38+
root = root.strip()
39+
if root:
40+
yield root
41+
return
42+
43+
for drive_letter in string.ascii_uppercase:
44+
yield "%s:\\" % drive_letter
45+
46+
47+
def _iter_candidate_paths():
48+
override_path = os.environ.get("PROXMOX_NETWORK_DATA_PATH", "").strip()
49+
if override_path:
50+
yield override_path
51+
return
52+
53+
for root in _iter_search_roots():
54+
normalized_root = root.rstrip("\\/")
55+
for filename in DEFAULT_NETWORK_DATA_FILENAMES:
56+
yield "%s\\%s" % (normalized_root, filename)
57+
58+
59+
def find_network_data_path(path_exists=os.path.exists):
60+
for candidate_path in _iter_candidate_paths():
61+
if path_exists(candidate_path):
62+
return candidate_path
63+
return None
64+
65+
66+
def load_network_data(network_data_path, open_file=open, parser=None):
67+
if parser is None:
68+
from cloudbaseinit.utils import serialization
69+
70+
parser = serialization.parse_json_yaml
71+
72+
with open_file(network_data_path, "r", encoding="utf-8") as network_data_file:
73+
raw_network_data = network_data_file.read()
74+
75+
network_data = parser(raw_network_data)
76+
if not isinstance(network_data, dict):
77+
raise ValueError(
78+
"Proxmox network data parsed into %r, expected dict" %
79+
type(network_data)
80+
)
81+
82+
return network_data
83+
84+
85+
def apply_network_data(network_data, network_parser=None, plugin_factory=None):
86+
if network_parser is None:
87+
from cloudbaseinit.metadata.services.nocloudservice import (
88+
NoCloudNetworkConfigParser,
89+
)
90+
91+
network_parser = NoCloudNetworkConfigParser.parse
92+
93+
if plugin_factory is None:
94+
from cloudbaseinit.plugins.common import networkconfig
95+
96+
plugin_factory = networkconfig.NetworkConfigPlugin
97+
98+
network_details = network_parser(network_data)
99+
if not network_details:
100+
LOG.warning("NoCloud network parser returned no interfaces")
101+
return False
102+
103+
plugin = plugin_factory()
104+
process_network_details = getattr(plugin, "_process_network_details_v2", None)
105+
if process_network_details is None:
106+
raise AttributeError(
107+
"Cloudbase-Init network plugin is missing _process_network_details_v2"
108+
)
109+
110+
process_network_details(network_details)
111+
return True
112+
113+
114+
def main():
115+
network_data_path = find_network_data_path()
116+
if not network_data_path:
117+
LOG.info(
118+
"No Proxmox network data found in candidate paths: %s",
119+
", ".join(_iter_candidate_paths()),
120+
)
121+
return 0
122+
123+
try:
124+
network_data = load_network_data(network_data_path)
125+
except Exception:
126+
LOG.exception(
127+
"Failed to load Proxmox network data from %s", network_data_path
128+
)
129+
return 0
130+
131+
try:
132+
LOG.info("Applying Proxmox network data from %s", network_data_path)
133+
applied = apply_network_data(network_data)
134+
except Exception:
135+
LOG.exception(
136+
"Failed to apply Proxmox network data from %s", network_data_path
137+
)
138+
return 0
139+
140+
if not applied:
141+
LOG.warning(
142+
"No network interfaces were applied from %s", network_data_path
143+
)
144+
145+
return 0
146+
147+
148+
if __name__ == "__main__":
149+
sys.exit(main())

images/capi/ansible/windows/roles/providers/tasks/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@
1212
- ansible.builtin.include_tasks: azure.yml
1313
when: packer_builder_type.startswith('azure')
1414

15+
- ansible.builtin.include_tasks: proxmox.yml
16+
when: packer_builder_type is search('proxmox')
17+
1518
- ansible.builtin.include_tasks: vmware.yml
1619
when: packer_builder_type is search('vmware') or packer_builder_type is search('vsphere')
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2026 The Kubernetes Authors.
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
---
15+
- name: Ensure Proxmox Cloudbase-Init LocalScripts directory exists
16+
ansible.windows.win_file:
17+
path: "{{ programfiles.stdout | trim }}\\Cloudbase Solutions\\Cloudbase-Init\\LocalScripts"
18+
state: directory
19+
20+
- name: Copy Proxmox Cloudbase-Init compatibility helper
21+
ansible.windows.win_copy:
22+
src: proxmox/cloudbase_helper.py
23+
dest: "{{ programfiles.stdout | trim }}\\Cloudbase Solutions\\Cloudbase-Init\\LocalScripts\\cloudbase_helper.py"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"ansible_common_vars": "runtime={{user `runtime`}} containerd_url={{user `containerd_url`}} containerd_sha256={{user `containerd_sha256_windows`}} containerd_version={{user `containerd_version`}} pause_image={{user `pause_image`}} additional_debug_files=\"{{user `additional_debug_files`}}\" containerd_additional_settings={{user `containerd_additional_settings`}} custom_role_names=\"{{user `custom_role_names`}}\" http_proxy={{user `http_proxy`}} https_proxy={{user `https_proxy`}} no_proxy={{user `no_proxy`}} kubernetes_base_url={{user `kubernetes_base_url`}} kubernetes_semver={{user `kubernetes_semver`}} kubernetes_install_path={{user `kubernetes_install_path`}} cloudbase_init_url=\"{{user `cloudbase_init_url`}}\" cloudbase_plugins=\"{{user `cloudbase_plugins`}}\" cloudbase_metadata_services=\"{{user `cloudbase_metadata_services`}}\" cloudbase_plugins_unattend=\"{{user `cloudbase_plugins_unattend`}}\" cloudbase_metadata_services_unattend=\"{{user `cloudbase_metadata_services_unattend`}}\" prepull={{user `prepull`}} windows_updates_kbs=\"{{user `windows_updates_kbs`}}\" windows_updates_categories=\"{{user `windows_updates_categories`}}\" windows_service_manager={{user `windows_service_manager`}} nssm_url={{user `nssm_url`}} distribution_version={{user `distribution_version`}} netbios_host_name_compatibility={{user `netbios_host_name_compatibility`}} disable_hypervisor={{ user `disable_hypervisor` }} cloudbase_logging_serial_port={{ user `cloudbase_logging_serial_port` }} cloudbase_real_time_clock_utc={{ user `cloudbase_real_time_clock_utc` }} load_additional_components={{ user `load_additional_components`}} ecr_credential_provider={{ user `ecr_credential_provider` }} additional_registry_images={{ user `additional_registry_images`}} additional_registry_images_list={{ user `additional_registry_images_list`}} additional_url_images={{ user `additional_url_images`}} additional_url_images_list={{ user `additional_url_images_list`}} additional_executables={{ user `additional_executables`}} additional_executables_list={{ user `additional_executables_list`}} additional_executables_destination_path={{ user `additional_executables_destination_path`}} ssh_source_url={{user `ssh_source_url` }} debug_tools={{user `debug_tools`}}"
2+
"ansible_common_vars": "runtime={{user `runtime`}} containerd_url={{user `containerd_url`}} containerd_sha256={{user `containerd_sha256_windows`}} containerd_version={{user `containerd_version`}} pause_image={{user `pause_image`}} additional_debug_files=\"{{user `additional_debug_files`}}\" containerd_additional_settings={{user `containerd_additional_settings`}} custom_role_names=\"{{user `custom_role_names`}}\" http_proxy={{user `http_proxy`}} https_proxy={{user `https_proxy`}} no_proxy={{user `no_proxy`}} kubernetes_base_url={{user `kubernetes_base_url`}} kubernetes_semver={{user `kubernetes_semver`}} kubernetes_install_path={{user `kubernetes_install_path`}} cloudbase_init_url=\"{{user `cloudbase_init_url`}}\" cloudbase_plugins=\"{{user `cloudbase_plugins`}}\" cloudbase_metadata_services=\"{{user `cloudbase_metadata_services`}}\" cloudbase_plugins_unattend=\"{{user `cloudbase_plugins_unattend`}}\" cloudbase_metadata_services_unattend=\"{{user `cloudbase_metadata_services_unattend`}}\" cloudbase_nocloud_metadata_file=\"{{user `cloudbase_nocloud_metadata_file`}}\" cloudbase_nocloud_networkdata_file=\"{{user `cloudbase_nocloud_networkdata_file`}}\" cloudbase_nocloud_userdata_file=\"{{user `cloudbase_nocloud_userdata_file`}}\" prepull={{user `prepull`}} windows_updates_kbs=\"{{user `windows_updates_kbs`}}\" windows_updates_categories=\"{{user `windows_updates_categories`}}\" windows_service_manager={{user `windows_service_manager`}} nssm_url={{user `nssm_url`}} distribution_version={{user `distribution_version`}} netbios_host_name_compatibility={{user `netbios_host_name_compatibility`}} disable_hypervisor={{ user `disable_hypervisor` }} cloudbase_logging_serial_port={{ user `cloudbase_logging_serial_port` }} cloudbase_real_time_clock_utc={{ user `cloudbase_real_time_clock_utc` }} load_additional_components={{ user `load_additional_components`}} ecr_credential_provider={{ user `ecr_credential_provider` }} additional_registry_images={{ user `additional_registry_images`}} additional_registry_images_list={{ user `additional_registry_images_list`}} additional_url_images={{ user `additional_url_images`}} additional_url_images_list={{ user `additional_url_images_list`}} additional_executables={{ user `additional_executables`}} additional_executables_list={{ user `additional_executables_list`}} additional_executables_destination_path={{ user `additional_executables_destination_path`}} ssh_source_url={{user `ssh_source_url` }} debug_tools={{user `debug_tools`}}"
33
}

images/capi/packer/config/windows/common.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"additional_debug_files": "",
3+
"cloudbase_nocloud_metadata_file": "",
4+
"cloudbase_nocloud_networkdata_file": "",
5+
"cloudbase_nocloud_userdata_file": "",
36
"debug_tools": "true",
47
"disable_hypervisor": "false",
58
"http_proxy": "",

images/capi/packer/goss/goss-vars.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,3 +694,32 @@ windows:
694694
filetype: file
695695
contains:
696696
- "metadata_services=cloudbaseinit.metadata.services.base.EmptyMetadataService"
697+
698+
proxmox:
699+
windows-service:
700+
701+
files:
702+
'c:/program files/Cloudbase Solutions/Cloudbase-init/conf/cloudbase-init.conf':
703+
exists: true
704+
filetype: file
705+
contains:
706+
- "!/logging_serial_port=COM1,115200,N,8/"
707+
- "metadata_services=cloudbaseinit.metadata.services.nocloudservice.NoCloudConfigDriveService"
708+
- "cloudbaseinit.plugins.common.ephemeraldisk.EphemeralDiskPlugin"
709+
- "cloudbaseinit.plugins.common.mtu.MTUPlugin"
710+
- "cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin"
711+
- "cloudbaseinit.plugins.common.networkconfig.NetworkConfigPlugin"
712+
- "cloudbaseinit.plugins.common.sshpublickeys.SetUserSSHPublicKeysPlugin"
713+
- "cloudbaseinit.plugins.common.userdata.UserDataPlugin"
714+
- "cloudbaseinit.plugins.common.localscripts.LocalScriptsPlugin"
715+
- "cloudbaseinit.plugins.windows.createuser.CreateUserPlugin"
716+
- "cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin"
717+
- "[nocloud]"
718+
- "metadata_file=META_DATA"
719+
- "networkdata_file=NETWORK_CONFIG"
720+
- "userdata_file=USER_DATA"
721+
'c:/program files/Cloudbase Solutions/Cloudbase-init/localscripts/cloudbase_helper.py':
722+
exists: true
723+
filetype: file
724+
contains:
725+
- "Apply Proxmox NoCloud network data from a config-drive file."

0 commit comments

Comments
 (0)