diff --git a/README.md b/README.md index 5c257f1a2d..8e40f0d917 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The table below shows the currently provided operating systems for each provider | Ubuntu 22.04 | ✅ | 💙 | ✅ | 💙 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 💙 | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | | Ubuntu 24.04 | ✅ | 💙 | ✅ | 💙 | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | 💙 | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | | Windows 2019 | ✅ | 💙 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| Windows 2022 | ❌ | 💙 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Windows 2022 | ❌ | 💙 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | Windows 2025 | ❌ | 💙 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | Windows Annual | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | diff --git a/docs/book/src/capi/providers/proxmox.md b/docs/book/src/capi/providers/proxmox.md index 44ef8f4a71..eb3a9a1eb3 100644 --- a/docs/book/src/capi/providers/proxmox.md +++ b/docs/book/src/capi/providers/proxmox.md @@ -13,7 +13,9 @@ The image build process expects a few things to be in place before the build pro 1. DHCP must be available to assign the packer VM an IP * The packer proxmox integration currently does not support the ability to assign static IPs, thus DHCP is required. * 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. -2. The build VM must be accessible via SSH from the host running `make build-proxmox...` +2. The build VM must be accessible via SSH or Winrm from the host running `make build-proxmox...` + * Linux builds use SSH. + * Windows builds use WinRM. 3. The build VM must have DHCP, DNS, HTTP, HTTPS and NTP accessibility to successfully update the OS packages. ## Building Images @@ -38,6 +40,7 @@ the different operating systems. |--------------------|-----------------------------------------| | `ubuntu-2204.json` | The settings for the Ubuntu 22.04 image | | `ubuntu-2404.json` | The settings for the Ubuntu 24.04 image | +| `windows-2022.json` | The settings for the Windows Server 2022 image | The full list of available environment vars can be found in the `variables` section of `images/capi/packer/proxmox/packer.json`. @@ -84,6 +87,45 @@ export PACKER_FLAGS="--var 'oem_id=qemu'" make build-proxmox-flatcar ``` +### Building images for Windows + +Windows builds require a Windows installation ISO and a Windows administrator +password in addition to the standard Proxmox credentials. + +Set the following environment variables before running the build: + +```bash +export PROXMOX_URL="https://pve.example.com:8006/api2/json" +export PROXMOX_USERNAME= +export PROXMOX_TOKEN= +export PROXMOX_NODE="pve" +export PROXMOX_ISO_POOL="local" +export PROXMOX_BRIDGE="vmbr0" +export PROXMOX_STORAGE_POOL="local-lvm" +export ISO_FILE="local:iso/en-us_windows_server_2022_x64.iso" +export WINDOWS_ADMIN_PASSWORD='' +``` + +The Proxmox Windows build also expects a VirtIO driver ISO to be available in the +selected ISO storage. By default the build uses: + +```bash +local:iso/virtio-win-0.1.285.iso +``` + +If your environment uses a different VirtIO image name or location, override it +with `PACKER_FLAGS`: + +```bash +export PACKER_FLAGS="--var 'iso_virtio=local:iso/virtio-win-0.1.285.iso'" +``` + +Build the Windows Server 2022 template with: + +```bash +make build-proxmox-windows-2022 +``` + ### Example 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" make build-proxmox-ubuntu-2204 ``` +- Build Windows Server 2022 template: + +```bash +make build-proxmox-windows-2022 +``` + ### Note on disk formats Depending on what storage type you are using, you will need to change the default disk diff --git a/images/capi/Makefile b/images/capi/Makefile index 47d56c5965..0b0176e922 100644 --- a/images/capi/Makefile +++ b/images/capi/Makefile @@ -173,6 +173,7 @@ deps-huaweicloud: deps-common .PHONY: deps-proxmox deps-proxmox: ## Installs/checks dependencies for Proxmox builds deps-proxmox: deps-common + hack/ensure-ansible-windows.sh $(PACKER) init packer/config.pkr.hcl $(PACKER) init packer/proxmox/config.pkr.hcl @@ -403,7 +404,7 @@ NUTANIX_BUILD_NAMES ?= nutanix-ubuntu-2204 nutanix-ubuntu-2404 nutanix-rhel-9 nu HCLOUD_BUILD_NAMES ?= hcloud-ubuntu-2204 hcloud-ubuntu-2404 hcloud-rockylinux-9 hcloud-flatcar hcloud-flatcar-arm64 -PROXMOX_BUILD_NAMES ?= proxmox-ubuntu-2204 proxmox-ubuntu-2404 proxmox-ubuntu-2404-efi proxmox-rockylinux-9 proxmox-flatcar +PROXMOX_BUILD_NAMES ?= proxmox-ubuntu-2204 proxmox-ubuntu-2404 proxmox-ubuntu-2404-efi proxmox-rockylinux-9 proxmox-flatcar proxmox-windows-2022 VULTR_BUILD_NAMES ?= vultr-ubuntu-2204 vultr-ubuntu-2404 @@ -644,7 +645,11 @@ $(HCLOUD_VALIDATE_TARGETS): deps-hcloud .PHONY: $(PROXMOX_BUILD_TARGETS) $(PROXMOX_BUILD_TARGETS): deps-proxmox set-ssh-password - $(PACKER) build $(PACKER_NODE_FLAGS) -var-file="$(abspath packer/proxmox/$(subst build-proxmox-,,$@).json)" $(ABSOLUTE_PACKER_VAR_FILES) packer/proxmox/packer.json + # 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 + $(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,) + $(if $(findstring windows,$@),hack/windows-unattend.py --unattend-file='./packer/proxmox/windows/$(subst build-proxmox-,,$@)/autounattend.xml',) + $(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 + .PHONY: $(PROXMOX_VALIDATE_TARGETS) $(PROXMOX_VALIDATE_TARGETS): deps-proxmox set-ssh-password @@ -859,6 +864,11 @@ build-proxmox-ubuntu-2404: ## Builds Ubuntu 24.04 Proxmox image build-proxmox-ubuntu-2404-efi: ## Builds Ubuntu 24.04 Proxmox image that EFI boots build-proxmox-rockylinux-9: ## Builds Rocky Linux 9 Proxmox image build-proxmox-flatcar: ## Builds Flatcar Proxmox image +build-proxmox-ubuntu-2204: ## Builds the Proxmox ubuntu-2204 image +build-proxmox-ubuntu-2404: ## Builds the Proxmox ubuntu-2404 image +build-proxmox-ubuntu-2404-efi: ## Builds the Proxmox ubuntu-2404-efi image that EFI boots +build-proxmox-rockylinux-9: ## Builds the Proxmox rockylinux-9 image +build-proxmox-windows-2022: ## Builds the Proxmox Windows 2022 image build-proxmox-all: $(PROXMOX_BUILD_TARGETS) ## Builds all Proxmox images build-vultr-ubuntu-2204: ## Builds Ubuntu 22.04 Vultr Snapshot diff --git a/images/capi/ansible/windows/roles/cloudbase-init/templates/cloudbase-init.conf b/images/capi/ansible/windows/roles/cloudbase-init/templates/cloudbase-init.conf index 1608f9123f..2a8ab71bb7 100644 --- a/images/capi/ansible/windows/roles/cloudbase-init/templates/cloudbase-init.conf +++ b/images/capi/ansible/windows/roles/cloudbase-init/templates/cloudbase-init.conf @@ -34,3 +34,12 @@ netbios_host_name_compatibility={{ netbios_host_name_compatibility }} metadata_services={{ cloudbase_metadata_services }} plugins={{ cloudbase_plugins }} +{% if (cloudbase_nocloud_metadata_file | default('') | length > 0) or (cloudbase_nocloud_networkdata_file | default('') | length > 0) or (cloudbase_nocloud_userdata_file | default('') | length > 0) %} + +[nocloud] +metadata_file={{ cloudbase_nocloud_metadata_file | default('meta-data', true) }} +{% if cloudbase_nocloud_networkdata_file | default('') | length > 0 %} +networkdata_file={{ cloudbase_nocloud_networkdata_file }} +{% endif %} +userdata_file={{ cloudbase_nocloud_userdata_file | default('user-data', true) }} +{% endif %} diff --git a/images/capi/ansible/windows/roles/providers/files/proxmox/cloudbase_helper.py b/images/capi/ansible/windows/roles/providers/files/proxmox/cloudbase_helper.py new file mode 100644 index 0000000000..a51004bde0 --- /dev/null +++ b/images/capi/ansible/windows/roles/providers/files/proxmox/cloudbase_helper.py @@ -0,0 +1,149 @@ +# Copyright 2026 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Apply Proxmox NoCloud network data from a config-drive file.""" + +import logging +import os +import string +import sys + +try: + from oslo_log import log as oslo_logging + + LOG = oslo_logging.getLogger(__name__) +except Exception: # pragma: no cover - fallback when oslo logging is unavailable + logging.basicConfig(level=logging.INFO) + LOG = logging.getLogger(__name__) + + +DEFAULT_NETWORK_DATA_FILENAMES = ("NETWORK_CONFIG", "network-config") + + +def _iter_search_roots(): + search_roots = os.environ.get("PROXMOX_NETWORK_DATA_SEARCH_ROOTS") + if search_roots: + for root in search_roots.split(os.pathsep): + root = root.strip() + if root: + yield root + return + + for drive_letter in string.ascii_uppercase: + yield "%s:\\" % drive_letter + + +def _iter_candidate_paths(): + override_path = os.environ.get("PROXMOX_NETWORK_DATA_PATH", "").strip() + if override_path: + yield override_path + return + + for root in _iter_search_roots(): + normalized_root = root.rstrip("\\/") + for filename in DEFAULT_NETWORK_DATA_FILENAMES: + yield "%s\\%s" % (normalized_root, filename) + + +def find_network_data_path(path_exists=os.path.exists): + for candidate_path in _iter_candidate_paths(): + if path_exists(candidate_path): + return candidate_path + return None + + +def load_network_data(network_data_path, open_file=open, parser=None): + if parser is None: + from cloudbaseinit.utils import serialization + + parser = serialization.parse_json_yaml + + with open_file(network_data_path, "r", encoding="utf-8") as network_data_file: + raw_network_data = network_data_file.read() + + network_data = parser(raw_network_data) + if not isinstance(network_data, dict): + raise ValueError( + "Proxmox network data parsed into %r, expected dict" % + type(network_data) + ) + + return network_data + + +def apply_network_data(network_data, network_parser=None, plugin_factory=None): + if network_parser is None: + from cloudbaseinit.metadata.services.nocloudservice import ( + NoCloudNetworkConfigParser, + ) + + network_parser = NoCloudNetworkConfigParser.parse + + if plugin_factory is None: + from cloudbaseinit.plugins.common import networkconfig + + plugin_factory = networkconfig.NetworkConfigPlugin + + network_details = network_parser(network_data) + if not network_details: + LOG.warning("NoCloud network parser returned no interfaces") + return False + + plugin = plugin_factory() + process_network_details = getattr(plugin, "_process_network_details_v2", None) + if process_network_details is None: + raise AttributeError( + "Cloudbase-Init network plugin is missing _process_network_details_v2" + ) + + process_network_details(network_details) + return True + + +def main(): + network_data_path = find_network_data_path() + if not network_data_path: + LOG.info( + "No Proxmox network data found in candidate paths: %s", + ", ".join(_iter_candidate_paths()), + ) + return 0 + + try: + network_data = load_network_data(network_data_path) + except Exception: + LOG.exception( + "Failed to load Proxmox network data from %s", network_data_path + ) + return 0 + + try: + LOG.info("Applying Proxmox network data from %s", network_data_path) + applied = apply_network_data(network_data) + except Exception: + LOG.exception( + "Failed to apply Proxmox network data from %s", network_data_path + ) + return 0 + + if not applied: + LOG.warning( + "No network interfaces were applied from %s", network_data_path + ) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/images/capi/ansible/windows/roles/providers/tasks/main.yml b/images/capi/ansible/windows/roles/providers/tasks/main.yml index f59b4b326c..1b2ad9fb4a 100644 --- a/images/capi/ansible/windows/roles/providers/tasks/main.yml +++ b/images/capi/ansible/windows/roles/providers/tasks/main.yml @@ -12,5 +12,8 @@ - ansible.builtin.include_tasks: azure.yml when: packer_builder_type.startswith('azure') +- ansible.builtin.include_tasks: proxmox.yml + when: packer_builder_type is search('proxmox') + - ansible.builtin.include_tasks: vmware.yml when: packer_builder_type is search('vmware') or packer_builder_type is search('vsphere') diff --git a/images/capi/ansible/windows/roles/providers/tasks/proxmox.yml b/images/capi/ansible/windows/roles/providers/tasks/proxmox.yml new file mode 100644 index 0000000000..1b3e75423a --- /dev/null +++ b/images/capi/ansible/windows/roles/providers/tasks/proxmox.yml @@ -0,0 +1,23 @@ +# Copyright 2026 The Kubernetes Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +- name: Ensure Proxmox Cloudbase-Init LocalScripts directory exists + ansible.windows.win_file: + path: "{{ programfiles.stdout | trim }}\\Cloudbase Solutions\\Cloudbase-Init\\LocalScripts" + state: directory + +- name: Copy Proxmox Cloudbase-Init compatibility helper + ansible.windows.win_copy: + src: proxmox/cloudbase_helper.py + dest: "{{ programfiles.stdout | trim }}\\Cloudbase Solutions\\Cloudbase-Init\\LocalScripts\\cloudbase_helper.py" diff --git a/images/capi/packer/config/windows/ansible-args-windows.json b/images/capi/packer/config/windows/ansible-args-windows.json index 02120012fe..b75518ea65 100644 --- a/images/capi/packer/config/windows/ansible-args-windows.json +++ b/images/capi/packer/config/windows/ansible-args-windows.json @@ -1,3 +1,3 @@ { - "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`}}" + "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`}}" } diff --git a/images/capi/packer/config/windows/common.json b/images/capi/packer/config/windows/common.json index 3f4dfffd44..1c97b1b083 100644 --- a/images/capi/packer/config/windows/common.json +++ b/images/capi/packer/config/windows/common.json @@ -1,5 +1,8 @@ { "additional_debug_files": "", + "cloudbase_nocloud_metadata_file": "", + "cloudbase_nocloud_networkdata_file": "", + "cloudbase_nocloud_userdata_file": "", "debug_tools": "true", "disable_hypervisor": "false", "http_proxy": "", diff --git a/images/capi/packer/goss/goss-vars.yaml b/images/capi/packer/goss/goss-vars.yaml index 85c406031a..e299259697 100644 --- a/images/capi/packer/goss/goss-vars.yaml +++ b/images/capi/packer/goss/goss-vars.yaml @@ -694,3 +694,32 @@ windows: filetype: file contains: - "metadata_services=cloudbaseinit.metadata.services.base.EmptyMetadataService" + + proxmox: + windows-service: + + files: + 'c:/program files/Cloudbase Solutions/Cloudbase-init/conf/cloudbase-init.conf': + exists: true + filetype: file + contains: + - "!/logging_serial_port=COM1,115200,N,8/" + - "metadata_services=cloudbaseinit.metadata.services.nocloudservice.NoCloudConfigDriveService" + - "cloudbaseinit.plugins.common.ephemeraldisk.EphemeralDiskPlugin" + - "cloudbaseinit.plugins.common.mtu.MTUPlugin" + - "cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin" + - "cloudbaseinit.plugins.common.networkconfig.NetworkConfigPlugin" + - "cloudbaseinit.plugins.common.sshpublickeys.SetUserSSHPublicKeysPlugin" + - "cloudbaseinit.plugins.common.userdata.UserDataPlugin" + - "cloudbaseinit.plugins.common.localscripts.LocalScriptsPlugin" + - "cloudbaseinit.plugins.windows.createuser.CreateUserPlugin" + - "cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin" + - "[nocloud]" + - "metadata_file=META_DATA" + - "networkdata_file=NETWORK_CONFIG" + - "userdata_file=USER_DATA" + 'c:/program files/Cloudbase Solutions/Cloudbase-init/localscripts/cloudbase_helper.py': + exists: true + filetype: file + contains: + - "Apply Proxmox NoCloud network data from a config-drive file." diff --git a/images/capi/packer/proxmox/README.md b/images/capi/packer/proxmox/README.md index f86586ddb4..922979c66f 100644 --- a/images/capi/packer/proxmox/README.md +++ b/images/capi/packer/proxmox/README.md @@ -15,6 +15,30 @@ For example, to use a local ISO file, set the `ISO_FILE` environment variable li export ISO_FILE="local:iso/ubuntu-24.04.3-live-server-amd64.iso" ``` +## Windows for Proxmox + +To build the Windows Server 2022 image, set the Windows installation ISO and an +administrator password in addition to the normal Proxmox environment variables. + +```shell +export ISO_FILE="local:iso/en-us_windows_server_2022_x64.iso" +export WINDOWS_ADMIN_PASSWORD='' +make build-proxmox-windows-2022 +``` + +The build expects a VirtIO driver ISO to be present in Proxmox ISO storage. The +default value is: + +```shell +local:iso/virtio-win-0.1.285.iso +``` + +If needed, override it with: + +```shell +export PACKER_FLAGS="--var 'iso_virtio=local:iso/virtio-win-0.1.285.iso'" +``` + ## Flatcar for Proxmox Proxmox support is available on Flatcar from version `4152`. diff --git a/images/capi/packer/proxmox/packer-windows.json.tmpl b/images/capi/packer/proxmox/packer-windows.json.tmpl new file mode 100644 index 0000000000..a6ed250e13 --- /dev/null +++ b/images/capi/packer/proxmox/packer-windows.json.tmpl @@ -0,0 +1,194 @@ +{ + "builders": [ + { + "content": "{\n \"unattend_timezone\" : \"{{user `unattend_timezone`}}\"\n, \"admin_password\" : \"{{user `windows_admin_password`}}\"\n}", + "target": "./packer_cache/unattend.json", + "type": "file" + }, + { + "type": "proxmox-iso", + + "proxmox_url": "{{user `proxmox_url`}}", + "insecure_skip_tls_verify": true, + "username": "{{user `username`}}", + "token": "{{user `token`}}", + "node": "{{ user `node` }}", + + "efi_config": { + "efi_storage_pool": "{{user `storage_pool`}}", + "pre_enrolled_keys": true, + "efi_type": "4m" + }, + + "boot_wait": "{{user `boot_wait`}}", + "boot_command": [ + "" + ], + "task_timeout": "10m", + "bios": "ovmf", + "machine": "q35", + "os": "win11", + "boot": "order=scsi0;scsi1;ide2", + "boot_iso": { + "iso_file": "{{user `iso_file`}}", + "iso_storage_pool": "{{user `iso_storage_pool`}}", + "iso_url": "{{user `iso_url`}}", + "iso_checksum": "none" + }, + + + "additional_iso_files": [ + { + "iso_file": "{{user `iso_virtio`}}", + "iso_checksum": "none", + "unmount": "true" + }, + { + "iso_storage_pool": "{{user `iso_storage_pool`}}", + "unmount": "true", + "cd_files": ["./packer/proxmox/windows/disable-network-discovery.cmd", + "./packer/proxmox/windows/enable_winrm.ps1", + "./packer/proxmox/windows/windows-2022/autounattend.xml", + "./packer/proxmox/windows/Install_agent.ps1" + ] + } + ], + + + + "cores": "{{user `cores`}}", + "cpu_type": "{{user `cpu_type`}}", + "disks": [ + { + "disk_size": "{{user `disk_size`}}", + "format": "{{user `disk_format`}}", + "storage_pool": "{{user `storage_pool`}}", + "storage_pool_type": "{{user `storage_pool_type`}}", + "type": "scsi", + "asyncio": "native", + "io_thread": "true" + } + ], + + "scsi_controller": "{{user `scsi_controller`}}", + "memory": "{{user `memory`}}", + "ballooning_minimum": "0", + "name": "{{user `build_name`}}", + "network_adapters": [ + { + "model": "virtio", + "bridge": "{{user `bridge`}}" + } + ], + + "sockets": "{{user `sockets`}}", + "template_name": "{{ user `artifact_name` }}", + "unmount_iso": "true", + "vm_id": "{{user `vmid`}}", + + + "communicator": "winrm", + "winrm_insecure": true, + "winrm_password": "{{user `windows_admin_password`}}", + "winrm_port": 5986, + "winrm_timeout": "4h", + "winrm_use_ssl": true, + "winrm_username": "Administrator" + } + ], + + "provisioners": [ + { + "except": "file", + "extra_arguments": [ + "-e", + "ansible_winrm_server_cert_validation=ignore", + "--extra-vars", + "{{user `ansible_common_vars`}}", + "--extra-vars", + "{{user `ansible_extra_vars`}}", + "--extra-vars", + "{{user `ansible_user_vars`}}" + ], + "playbook_file": "ansible/windows/node_windows.yml", + "type": "ansible", + "use_proxy": false, + "user": "Administrator" + }, + { + "except": "file", + "restart_check_command": "powershell -command \"& {if ((get-content C:\\ProgramData\\lastboot.txt) -eq (Get-WmiObject win32_operatingsystem).LastBootUpTime) {Write-Output 'Sleeping for 600 seconds to wait for reboot'; start-sleep 600} else {Write-Output 'Reboot complete'}}\"", + "restart_command": "powershell \"& {(Get-WmiObject win32_operatingsystem).LastBootUpTime > C:\\ProgramData\\lastboot.txt; Restart-Computer -force}\"", + "type": "windows-restart", + "restart_timeout": "20m" + }, + { + "inline": [ + "nssm stop kubelet", + "Remove-Item -Force -Recurse C:\\var\\log\\kubelet\\* -ErrorAction SilentlyContinue" + ], + "type": "powershell" + }, + { + "elevated_password": "{{.WinRMPassword}}", + "elevated_user": "Administrator", + "script": "packer/proxmox/windows/sysprep.ps1", + "type": "powershell" + } + ], + "variables": { + "additional_debug_files": null, + "ansible_common_vars": "", + "ansible_extra_vars": "", + "ansible_user_vars": "", + "ansible_scp_extra_args": "", + "artifact_name": "{{user `build_name`}}-kube-{{user `kubernetes_semver`}}", + "boot_wait": "4s", + "bios": "ovmf", + "bridge": "{{env `PROXMOX_BRIDGE`}}", + "build_timestamp": "{{timestamp}}", + "cloudbase_init_url": "https://github.com/cloudbase/cloudbase-init/releases/download/{{user `cloudbase_init_version`}}/CloudbaseInitSetup_{{user `cloudbase_init_version` | replace_all `.` `_` }}_x64.msi", + "cloudbase_metadata_services": "cloudbaseinit.metadata.services.nocloudservice.NoCloudConfigDriveService", + "cloudbase_metadata_services_unattend": "cloudbaseinit.metadata.services.nocloudservice.NoCloudConfigDriveService", + "cloudbase_nocloud_metadata_file": "META_DATA", + "cloudbase_nocloud_networkdata_file": "NETWORK_CONFIG", + "cloudbase_nocloud_userdata_file": "USER_DATA", + "cloudbase_plugins": "cloudbaseinit.plugins.windows.createuser.CreateUserPlugin, cloudbaseinit.plugins.common.setuserpassword.SetUserPasswordPlugin, cloudbaseinit.plugins.common.mtu.MTUPlugin, cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin, cloudbaseinit.plugins.common.localscripts.LocalScriptsPlugin, cloudbaseinit.plugins.common.networkconfig.NetworkConfigPlugin, cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin, cloudbaseinit.plugins.common.ephemeraldisk.EphemeralDiskPlugin, cloudbaseinit.plugins.common.sshpublickeys.SetUserSSHPublicKeysPlugin, cloudbaseinit.plugins.common.userdata.UserDataPlugin, cloudbaseinit.plugins.windows.createuser.CreateUserPlugin, cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin", + "cloudbase_plugins_unattend": "cloudbaseinit.plugins.common.mtu.MTUPlugin", + "cloudbase_real_time_clock_utc": "true", + "containerd_url": "", + "containerd_version": null, + "cores": "2", + "crictl_version": null, + "disk_format": "raw", + "disk_size": "60G", + "existing_ansible_ssh_args": "{{env `ANSIBLE_SSH_ARGS`}}", + "http_directory": "./packer/proxmox/windows/{{user `distro_name`}}/http/", + "iso_storage_pool": "{{env `PROXMOX_ISO_POOL`}}", + "kubernetes_base_url": "https://kubernetesreleases.blob.core.windows.net/kubernetes/{{user `kubernetes_semver`}}/binaries/node/windows/{{user `kubernetes_goarch`}}", + "kubernetes_container_registry": null, + "kubernetes_http_package_url": "", + "kubernetes_typed_version": "kube-{{user `kubernetes_semver`}}", + "memory": "2048", + "mtu": "{{env `PROXMOX_MTU`}}", + "node": "{{env `PROXMOX_NODE`}}", + "numa": "false", + "oem_id": "{{ user `oem_id` }}", + "proxmox_url": "{{env `PROXMOX_URL`}}", + "sockets": "2", + "storage_pool": "{{env `PROXMOX_STORAGE_POOL`}}", + "storage_pool_type": "lvm", + "token": "{{env `PROXMOX_TOKEN`}}", + "username": "{{env `PROXMOX_USERNAME`}}", + "vlan_tag": "{{env `PROXMOX_VLAN`}}", + "vmid": "", + "scsi_controller": "virtio-scsi-single", + "nssm_url": null, + "prepull": null, + "unattend_timezone": "Pacific Standard Time", + "windows_admin_password": "{{env `WINDOWS_ADMIN_PASSWORD`}}", + "windows_service_manager": null, + "windows_updates_categories": null, + "windows_updates_kbs": null + } +} diff --git a/images/capi/packer/proxmox/windows-2022.json b/images/capi/packer/proxmox/windows-2022.json new file mode 100644 index 0000000000..f9a6a9d7fd --- /dev/null +++ b/images/capi/packer/proxmox/windows-2022.json @@ -0,0 +1,16 @@ +{ + "bios": "ovmf", + "build_name": "windows-2022", + "cloudbase_init_version": "1.1.6", + "cloudbase_nocloud_metadata_file": "META_DATA", + "cloudbase_nocloud_networkdata_file": "NETWORK_CONFIG", + "cloudbase_nocloud_userdata_file": "USER_DATA", + "cpu_type": "host", + "distribution_version": "2022", + "distro_name": "windows", + "goss_version": "0.4.9", + "iso_file": "{{env `ISO_FILE`}}", + "iso_virtio": "local:iso/virtio-win-0.1.285.iso", + "os_display_name": "Windows Server 2022", + "unmount_iso": "true" +} diff --git a/images/capi/packer/proxmox/windows/Install_agent.ps1 b/images/capi/packer/proxmox/windows/Install_agent.ps1 new file mode 100644 index 0000000000..4779cacf09 --- /dev/null +++ b/images/capi/packer/proxmox/windows/Install_agent.ps1 @@ -0,0 +1,104 @@ + +<# + Install-MSI function adapted from: + https://github.com/fansec/proxmox_dev/blob/main/packer/win2019/mount/Install-Agent.ps1 + + Original Copyright: + Copyright (c) fansec + + Licensed under the MIT License: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Modifications by: Martin Sanchez 2025 +#> +#Start Transcript + +$transcriptPath = "C:\Logs\Install-Transcript-$(Get-Date -Format 'yyyyMMdd_HHmmss').log" +Start-Transcript -Path $transcriptPath -Append + +# Define paths to the installers + +$virtio = "virtio-win-gt-x64.msi" +$qemuGuestAgent = "qemu-ga-x86_64.msi" +$logDirectory = "C:\Logs\" + +# Ensure the log directory exists +if (-not (Test-Path -Path $logDirectory)) { + New-Item -ItemType Directory -Path $logDirectory -Force +} + +# Function to install MSI packages +function Install-MSI { + param ( + [string]$msiPath, + [string]$logFile + ) + + if (Test-Path -Path $msiPath) { + Write-Host "Installing $msiPath" + Start-Process msiexec -Wait -ArgumentList @('/i', $msiPath, '/log', $logFile, '/qn', '/passive', '/norestart', 'ADDLOCAL=ALL') + if ($LASTEXITCODE -eq 0) { + Write-Host "$msiPath installed successfully." + } else { + Write-Host "Failed to install $msiPath. Check log file: $logFile" + } + } else { + Write-Host "MSI path $msiPath not found." + } +} +function Find-DriverFile { + param ( + [string]$fileName + ) + + #Try D: first + $path = Get-ChildItem -Path "D:\" -Recurse -Filter $fileName -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty FullName -First 1 + + if ($path) { + return $path + } + + Write-Host "File '$fileName' not found on D:. Searching all drives..." + + #Search ALL drives except D: + $allDrives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -ne 'D' } + + foreach ($drive in $allDrives) { + $path = Get-ChildItem -Path ($drive.Root) -Recurse -Filter $fileName -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty FullName -First 1 + + if ($path) { + return $path + } + } + + return $null +} +# Install Virtio Drivers +$virtioDriverPath = Find-DriverFile -fileName $virtio +$qemuGuestAgentPath = Find-DriverFile -fileName $qemuGuestAgent + +$qemuGuestAgentPath = "D:\guest-agent\qemu-ga-x86_64.msi" +Install-MSI -msiPath $virtioDriverPath -logFile "$logDirectory\qemu-drivers.log" + +# Install QEMU Guest Agent +Install-MSI -msiPath $qemuGuestAgentPath -logFile "$logDirectory\qemu-guest-agent.log" + +Stop-Transcript \ No newline at end of file diff --git a/images/capi/packer/proxmox/windows/disable-network-discovery.cmd b/images/capi/packer/proxmox/windows/disable-network-discovery.cmd new file mode 100644 index 0000000000..d2c47db26e --- /dev/null +++ b/images/capi/packer/proxmox/windows/disable-network-discovery.cmd @@ -0,0 +1,2 @@ +reg ADD HKLM\SYSTEM\CurrentControlSet\Control\Network\NewNetworkWindowOff /f +netsh advfirewall firewall set rule group="Network Discovery" new enable=No \ No newline at end of file diff --git a/images/capi/packer/proxmox/windows/enable_winrm.ps1 b/images/capi/packer/proxmox/windows/enable_winrm.ps1 new file mode 100644 index 0000000000..548486d722 --- /dev/null +++ b/images/capi/packer/proxmox/windows/enable_winrm.ps1 @@ -0,0 +1,44 @@ + +# MAKE SURE IN YOUR PACKER CONFIG TO SET: +# +# +# "winrm_username": "Administrator", +# "winrm_insecure": true, +# "winrm_use_ssl": true, +# +# + + +write-output "Running User Data Script" +write-host "(host) Running User Data Script" + +Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force -ErrorAction Ignore + +# Don't set this before Set-ExecutionPolicy as it throws an error +$ErrorActionPreference = "stop" + +# Remove HTTP listener +Remove-Item -Path WSMan:\Localhost\listener\listener* -Recurse + +# Create a self-signed certificate to let ssl work +$Cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName "packer" +New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $Cert.Thumbprint -Force + +# WinRM +write-output "Setting up WinRM" +write-host "(host) setting up WinRM" + +cmd.exe /c winrm quickconfig -q +cmd.exe /c winrm set "winrm/config" '@{MaxTimeoutms="1800000"}' +cmd.exe /c winrm set "winrm/config/winrs" '@{MaxMemoryPerShellMB="1024"}' +cmd.exe /c winrm set "winrm/config/service" '@{AllowUnencrypted="true"}' +cmd.exe /c winrm set "winrm/config/client" '@{AllowUnencrypted="true"}' +cmd.exe /c winrm set "winrm/config/service/auth" '@{Basic="true"}' +cmd.exe /c winrm set "winrm/config/client/auth" '@{Basic="true"}' +cmd.exe /c winrm set "winrm/config/service/auth" '@{CredSSP="true"}' +cmd.exe /c winrm set "winrm/config/listener?Address=*+Transport=HTTPS" "@{Port=`"5986`";Hostname=`"packer`";CertificateThumbprint=`"$($Cert.Thumbprint)`"}" +cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes +cmd.exe /c netsh firewall add portopening TCP 5986 "Port 5986" +cmd.exe /c net stop winrm +cmd.exe /c sc config winrm start= auto +cmd.exe /c net start winrm diff --git a/images/capi/packer/proxmox/windows/goss-wip.json b/images/capi/packer/proxmox/windows/goss-wip.json new file mode 100644 index 0000000000..2f539d82ab --- /dev/null +++ b/images/capi/packer/proxmox/windows/goss-wip.json @@ -0,0 +1,35 @@ +{ + "arch": "{{user `goss_arch`}}", + "download_path": "{{user `goss_download_path`}}", + "except": "file", + "format": "{{user `goss_format`}}", + "format_options": "{{user `goss_format_options`}}", + "goss_file": "{{user `goss_entry_file`}}", + "inspect": "{{user `goss_inspect_mode`}}", + "remote_folder": "{{user `goss_remote_folder`}}", + "remote_path": "{{user `goss_remote_path`}}", + "skip_install": "{{user `goss_skip_install`}}", + "target_os": "Windows", + "tests": [ + "{{user `goss_tests_dir`}}" + ], + "type": "goss", + "url": "{{user `goss_url`}}", + "use_sudo": false, + "vars_env": { + "GOSS_MAX_CONCURRENT": "1", + "GOSS_USE_ALPHA": "1" + }, + "vars_file": "{{user `goss_vars_file`}}", + "vars_inline": { + "OS": "{{user `distro_name` | lower}}", + "PROVIDER": "proxmox", + "containerd_version": "{{user `containerd_version`}}", + "distribution_version": "{{user `distribution_version`}}", + "kubernetes_version": "{{user `kubernetes_semver`}}", + "pause_image": "{{user `pause_image`}}", + "runtime": "{{user `runtime`}}", + "ssh_source_url": "{{user `ssh_source_url`}}" + }, + "version": "{{user `goss_version`}}" +} diff --git a/images/capi/packer/proxmox/windows/sysprep.ps1 b/images/capi/packer/proxmox/windows/sysprep.ps1 new file mode 100644 index 0000000000..7714eb0166 --- /dev/null +++ b/images/capi/packer/proxmox/windows/sysprep.ps1 @@ -0,0 +1,40 @@ +# Copyright 2020 The Kubernetes Authors. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Write-Output '>>> Sysprepping VM ...' + +Write-Output 'Removing default unattend.xml file...' +if( Test-Path $Env:SystemRoot\system32\Sysprep\unattend.xml ) { + Remove-Item $Env:SystemRoot\system32\Sysprep\unattend.xml -Force +} + +$unattendedXml = "$ENV:ProgramFiles\Cloudbase Solutions\Cloudbase-Init\conf\Unattend.xml" +$FileExists = Test-Path $unattendedXml +If ($FileExists -eq $True) { + # Use the Cloudbase-init provided unattend file during install + Write-Output "Using cloudbase-init unattend file for sysprep: $unattendedXml" + & $Env:SystemRoot\System32\Sysprep\Sysprep.exe /oobe /generalize /mode:vm /quit /quiet /unattend:$unattendedXml +}else { + & $Env:SystemRoot\System32\Sysprep\Sysprep.exe /oobe /generalize /mode:vm /quit /quiet +} + +# Wait for the image to be reset +while($true) { + $imageState = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State).ImageState + Write-Output $imageState + if ($imageState -eq 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { break } + Start-Sleep -s 5 +} + +Write-Output '>>> Sysprep complete ...' diff --git a/images/capi/packer/proxmox/windows/windows-2022/autounattend-eval.xml b/images/capi/packer/proxmox/windows/windows-2022/autounattend-eval.xml new file mode 100644 index 0000000000..f9878a9f86 --- /dev/null +++ b/images/capi/packer/proxmox/windows/windows-2022/autounattend-eval.xml @@ -0,0 +1,239 @@ + + + + + + D:\NetKVM\2k22\amd64\ + + + D:\viostor\2k22\amd64\ + + + D:\vioscsi\2k22\amd64\ + + + + + + + + + 1 + EFI + 100 + + + 2 + MSR + 16 + + + 3 + Primary + true + + + + + 1 + FAT32 + + 1 + + + 2 + 2 + + + 3 + NTFS + + C + 3 + + + 0 + true + + + + + + 0 + 3 + + + + /IMAGE/INDEX + 1 + + + + + + + 1 + cmd /c powercfg.exe /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c + + + + true + Administrator + Organization + + true + + + + en-US + + 0409:00000409 + en-US + en-US + en-US + en-US + + + + + false + + + + + 1 + + + + + + + Always + %SystemRoot%\System32\reg.exe ADD "HKLM\System\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f + 1 + + + + + 0409:00000409 + en-US + en-US + en-US + en-US + + + true + + + 0 + + + + + + + + + + Password#1 + true</PlainText> + </Password> + <Enabled>true</Enabled> + <Username>Administrator</Username> + </AutoLogon> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Order>1</Order> + <Description>Set Execution Policy 64 Bit</Description> + <CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>2</Order> + <Description>Set Execution Policy 32 Bit</Description> + <CommandLine>%SystemDrive%\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f</CommandLine> + <Order>3</Order> + <Description>Show file extensions in Explorer</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f</CommandLine> + <Order>4</Order> + <Description>Enable QuickEdit mode</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v Start_ShowRun /t REG_DWORD /d 1 /f</CommandLine> + <Order>5</Order> + <Description>Show Run command in Start Menu</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v StartMenuAdminTools /t REG_DWORD /d 1 /f</CommandLine> + <Order>6</Order> + <Description>Show Administrative Tools in Start Menu</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f</CommandLine> + <Order>7</Order> + <Description>Zero Hibernation File</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f</CommandLine> + <Order>8</Order> + <Description>Disable Hibernation Mode</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>cmd.exe /c wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE</CommandLine> + <Order>9</Order> + <Description>Disable password expiration for Administrator user</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\enable_winrm.ps1"</CommandLine> + <Description>Enable WinRM</Description> + <Order>10</Order> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\disable-network-discovery.cmd"</CommandLine> + <Description>Disable Network Discovery</Description> + <Order>11</Order> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\Install_agent.ps1"</CommandLine> + <Description>Install QEMU guest agent</Description> + <Order>12</Order> + </SynchronousCommand> + </FirstLogonCommands> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideLocalAccountScreen>true</HideLocalAccountScreen> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <NetworkLocation>Work</NetworkLocation> + <ProtectYourPC>1</ProtectYourPC> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + <RegisteredOrganization>Organization</RegisteredOrganization> + <RegisteredOwner>Owner</RegisteredOwner> + <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> + <TimeZone>Pacific Standard Time</TimeZone> + <UserAccounts> + <AdministratorPassword> + <Value>Password#1</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Description>Administrator</Description> + <DisplayName>Administrator</DisplayName> + <Group>Administrators</Group> + <Name>Administrator</Name> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + </component> + </settings> +</unattend> \ No newline at end of file diff --git a/images/capi/packer/proxmox/windows/windows-2022/autounattend.xml b/images/capi/packer/proxmox/windows/windows-2022/autounattend.xml new file mode 100644 index 0000000000..16a0c5b704 --- /dev/null +++ b/images/capi/packer/proxmox/windows/windows-2022/autounattend.xml @@ -0,0 +1,261 @@ +<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"> +<!--************************************************* +Proxmox Windows Server 2022 Answer File + +Assumptions and notes: + +- This file assumes licensed installation media. Remove the ProductKey entries + below if you are building from an evaluation ISO. +- The build currently installs /IMAGE/INDEX 1 from the Windows Server 2022 ISO. +- The VirtIO driver ISO is expected to be attached as D: and to expose the + NetKVM, viostor, and vioscsi Windows Server 2022 amd64 paths used below. +- The generated helper ISO is expected to be attached as F: and to contain + enable_winrm.ps1, disable-network-discovery.cmd, and Install_agent.ps1. +- The VM is expected to boot in UEFI mode and install to disk 0 using the + EFI/MSR/Primary GPT layout defined here. Windows is installed to partition 3. +- The Administrator password placeholders in oobeSystem are rewritten by + hack/windows-unattend.py from WINDOWS_ADMIN_PASSWORD or packer_cache/unattend.json. +**************************************************--> + <settings pass="windowsPE"> + <component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <DriverPaths> + <PathAndCredentials wcm:action="add" wcm:keyValue="1"> + <Path>D:\NetKVM\2k22\amd64\</Path> + </PathAndCredentials> + <PathAndCredentials wcm:action="add" wcm:keyValue="2"> + <Path>D:\viostor\2k22\amd64\</Path> + </PathAndCredentials> + <PathAndCredentials wcm:action="add" wcm:keyValue="3"> + <Path>D:\vioscsi\2k22\amd64\</Path> + </PathAndCredentials> + </DriverPaths> + </component> + <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <DiskConfiguration> + <Disk wcm:action="add"> + <CreatePartitions> + <CreatePartition wcm:action="add"> + <Order>1</Order> + <Type>EFI</Type> + <Size>100</Size> + </CreatePartition> + <CreatePartition wcm:action="add"> + <Order>2</Order> + <Type>MSR</Type> + <Size>16</Size> + </CreatePartition> + <CreatePartition wcm:action="add"> + <Order>3</Order> + <Type>Primary</Type> + <Extend>true</Extend> + </CreatePartition> + </CreatePartitions> + <ModifyPartitions> + <ModifyPartition wcm:action="add"> + <Order>1</Order> + <Format>FAT32</Format> + <Label>System</Label> + <PartitionID>1</PartitionID> + </ModifyPartition> + <ModifyPartition wcm:action="add"> + <Order>2</Order> + <PartitionID>2</PartitionID> + </ModifyPartition> + <ModifyPartition wcm:action="add"> + <Order>3</Order> + <Format>NTFS</Format> + <Label>Windows</Label> + <Letter>C</Letter> + <PartitionID>3</PartitionID> + </ModifyPartition> + </ModifyPartitions> + <DiskID>0</DiskID> + <WillWipeDisk>true</WillWipeDisk> + </Disk> + </DiskConfiguration> + <ImageInstall> + <OSImage> + <InstallTo> + <DiskID>0</DiskID> + <PartitionID>3</PartitionID> + </InstallTo> + <InstallFrom> + <MetaData wcm:action="add"> + <Key>/IMAGE/INDEX</Key> + <Value>1</Value> + </MetaData> + </InstallFrom> + </OSImage> + </ImageInstall> + <RunSynchronous> + <RunSynchronousCommand> + <Order>1</Order> + <Path>cmd /c powercfg.exe /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c</Path> + </RunSynchronousCommand> + </RunSynchronous> + <UserData> + <AcceptEula>true</AcceptEula> + <FullName>Administrator</FullName> + <Organization>Organization</Organization> + <ProductKey> + <Key>VDYBN-27WPP-V4HQT-9VMD4-VMK7H</Key> + <WillShowUI>OnError</WillShowUI> + </ProductKey> + </UserData> + <EnableFirewall>true</EnableFirewall> + </component> + <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <SetupUILanguage> + <UILanguage>en-US</UILanguage> + </SetupUILanguage> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + </settings> + <settings pass="offlineServicing"> + <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <EnableLUA>false</EnableLUA> + </component> + </settings> + <settings pass="generalize"> + <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <SkipRearm>1</SkipRearm> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <RunSynchronous> + <RunSynchronousCommand wcm:action="add"> + <WillReboot>Always</WillReboot> + <Path>%SystemRoot%\System32\reg.exe ADD "HKLM\System\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f</Path> + <Order>1</Order> + </RunSynchronousCommand> + </RunSynchronous> + </component> + <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <InputLocale>0409:00000409</InputLocale> + <SystemLocale>en-US</SystemLocale> + <UILanguage>en-US</UILanguage> + <UILanguageFallback>en-US</UILanguageFallback> + <UserLocale>en-US</UserLocale> + </component> + <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <SkipAutoActivation>true</SkipAutoActivation> + </component> + <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <CEIPEnabled>0</CEIPEnabled> + </component> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <ComputerName /> + <ProductKey>VDYBN-27WPP-V4HQT-9VMD4-VMK7H</ProductKey> + </component> + </settings> + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <AutoLogon> + <Password> + <Value>Password#1</Value> + <PlainText>true</PlainText> + </Password> + <Enabled>true</Enabled> + <Username>Administrator</Username> + </AutoLogon> + <FirstLogonCommands> + <SynchronousCommand wcm:action="add"> + <Order>1</Order> + <Description>Set Execution Policy 64 Bit</Description> + <CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <Order>2</Order> + <Description>Set Execution Policy 32 Bit</Description> + <CommandLine>%SystemDrive%\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine> + <RequiresUserInput>true</RequiresUserInput> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f</CommandLine> + <Order>3</Order> + <Description>Show file extensions in Explorer</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f</CommandLine> + <Order>4</Order> + <Description>Enable QuickEdit mode</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v Start_ShowRun /t REG_DWORD /d 1 /f</CommandLine> + <Order>5</Order> + <Description>Show Run command in Start Menu</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v StartMenuAdminTools /t REG_DWORD /d 1 /f</CommandLine> + <Order>6</Order> + <Description>Show Administrative Tools in Start Menu</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f</CommandLine> + <Order>7</Order> + <Description>Zero Hibernation File</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f</CommandLine> + <Order>8</Order> + <Description>Disable Hibernation Mode</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>cmd.exe /c wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE</CommandLine> + <Order>9</Order> + <Description>Disable password expiration for Administrator user</Description> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\enable_winrm.ps1"</CommandLine> + <Description>Enable WinRM</Description> + <Order>10</Order> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\disable-network-discovery.cmd"</CommandLine> + <Description>Disable Network Discovery</Description> + <Order>11</Order> + </SynchronousCommand> + <SynchronousCommand wcm:action="add"> + <CommandLine>powershell -ExecutionPolicy Bypass -Command "F:\Install_agent.ps1"</CommandLine> + <Description>Install QEMU guest agent</Description> + <Order>12</Order> + </SynchronousCommand> + </FirstLogonCommands> + <OOBE> + <HideEULAPage>true</HideEULAPage> + <HideLocalAccountScreen>true</HideLocalAccountScreen> + <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> + <HideOnlineAccountScreens>true</HideOnlineAccountScreens> + <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> + <NetworkLocation>Work</NetworkLocation> + <ProtectYourPC>1</ProtectYourPC> + <SkipMachineOOBE>true</SkipMachineOOBE> + <SkipUserOOBE>true</SkipUserOOBE> + </OOBE> + <RegisteredOrganization>Organization</RegisteredOrganization> + <RegisteredOwner>Owner</RegisteredOwner> + <DisableAutoDaylightTimeSet>false</DisableAutoDaylightTimeSet> + <TimeZone>Pacific Standard Time</TimeZone> + <UserAccounts> + <AdministratorPassword> + <Value>Password#1</Value> + <PlainText>true</PlainText> + </AdministratorPassword> + <LocalAccounts> + <LocalAccount wcm:action="add"> + <Description>Administrator</Description> + <DisplayName>Administrator</DisplayName> + <Group>Administrators</Group> + <Name>Administrator</Name> + </LocalAccount> + </LocalAccounts> + </UserAccounts> + </component> + </settings> +</unattend> \ No newline at end of file