Skip to content

RHEL9 - Need support for adding Shell provisioner #346

@vikasbidare

Description

@vikasbidare

When I use the dvd.iso image with the default packer, it generates the image fine. However, I need to configure the image and hence I set ssh communicator. When I do this, the build gets stuck at the select language GUI prompt.

Image

I borrowed the communicator and proviosioner code from PR #204. It kept failing with ssh connection failures and timed out

2025/08/06 11:14:07 packer-plugin-qemu_v1.1.1_x5.0_linux_amd64 plugin: 2025/08/06 11:14:07 [DEBUG] TCP connection to SSH ip/port failed: dial tcp 127.0.0.1:3065: connect: connection refused

Figured it was entering GUI prompt when I checked the VNC. Why is this happening? I have set the variable headless to true. That should make this run in headless mode, correct? When there is no communicator set, it runs in headless mode as expected.
Attaching contents of packer and kickstart files, PTAL

rhel9.pkr.hcl

packer {
  required_version = ">= 1.11.0"
  required_plugins {
    qemu = {
      version = ">= 1.1.0, < 1.1.2"
      source  = "github.com/hashicorp/qemu"
    }
  }
}

variable "filename" {
  type        = string
  default     = "rhel9.tar.gz"
  description = "The filename of the tarball to produce"
}

variable "rhel9_iso_path" {
  type    = string
  default = "${env("RHEL9_ISO_PATH")}"
}

# Use --baseurl to specify the exact url for AppStream repo
variable "ks_appstream_repos" {
  type    = string
  default = "--baseurl='file:///run/install/repo/AppStream'"
}

variable ks_proxy {
  type    = string
  default = "${env("KS_PROXY")}"
}

variable "timeout" {
  type        = string
  default     = "1h"
  description = "Timeout for building the image"
}

variable "architecture" {
  type        = string
  default     = "x86_64"
  description = "The architecture to build the image for (amd64 or arm64)"
}

variable "host_is_arm" {
  type        = bool
  default     = false
  description = "The host architecture is aarch64"
}

variable "ovmf_suffix" {
  type        = string
  default     = ""
  description = "Suffix for OVMF CODE and VARS files. Newer systems such as Noble use _4M."
}

variable enable_ssh_provisioning {
  type    = bool
  default = true
}

variable ssh_username {
  type    = string
  default = "packer"
}

variable ssh_password {
  type    = string
  default = "packer"
}

variable ssh_user_cleanup {
  type    = bool
  default = true
}

locals {
  qemu_arch = {
    "x86_64"  = "x86_64"
    "aarch64" = "aarch64"
  }
  uefi_imp = {
    "x86_64"  = "OVMF"
    "aarch64" = "AAVMF"
  }
  uefi_sfx = {
    "x86_64"  = "${var.ovmf_suffix}"
    "aarch64" = ""
  }
  qemu_machine = {
    "x86_64"  = "accel=kvm"
    "aarch64" = var.host_is_arm ? "virt,accel=kvm" : "virt"
  }
  qemu_cpu = {
    "x86_64"  = "host"
    "aarch64" = var.host_is_arm ? "host" : "max"
  }
  communicator = var.enable_ssh_provisioning == true ? "ssh" : "none"
  ks_proxy    = var.ks_proxy != "" ? "--proxy=${var.ks_proxy}" : ""
}


source "qemu" "rhel9" {
  boot_command     = ["<up><wait>", "e", "<down><down><down><left>", " console=ttyS0 inst.cmdline inst.text inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/rhel9.ks <f10>"]
  boot_wait        = "5s"
  communicator     = local.communicator
  disk_size        = "4G"
  headless         = true
  iso_checksum     = "none"
  iso_url          = var.rhel9_iso_path
  ssh_username     = var.enable_ssh_provisioning == true ? var.ssh_username : null
  ssh_password     = var.enable_ssh_provisioning == true ? var.ssh_password : null  
  memory           = 2048
  cores            = 4
  qemu_binary      = "qemu-system-${lookup(local.qemu_arch, var.architecture, "")}"
  qemuargs = [
    ["-serial", "stdio"],
    ["-boot", "strict=off"],
    ["-device", "qemu-xhci"],
    ["-device", "usb-kbd"],
    ["-device", "virtio-net-pci,netdev=net0"],
    ["-netdev", "user,id=net0"],
    ["-device", "virtio-blk-pci,drive=drive0,bootindex=0"],
    ["-device", "virtio-blk-pci,drive=cdrom0,bootindex=1"],
    ["-machine", "${lookup(local.qemu_machine, var.architecture, "")}"],
    ["-cpu", "${lookup(local.qemu_cpu, var.architecture, "")}"],
    ["-device", "virtio-gpu-pci"],
    ["-global", "driver=cfi.pflash01,property=secure,value=off"],
    ["-drive", "if=pflash,format=raw,unit=0,id=ovmf_code,readonly=on,file=/usr/share/${lookup(local.uefi_imp, var.architecture, "")}/${lookup(local.uefi_imp, var.architecture, "")}_CODE${lookup(local.uefi_sfx, var.architecture, "")}.fd"],
    ["-drive", "if=pflash,format=raw,unit=1,id=ovmf_vars,file=${var.architecture}_VARS.fd"],
    ["-drive", "file=output-rhel9/packer-rhel9,if=none,id=drive0,cache=writeback,discard=ignore,format=qcow2"],
    ["-drive", "file=${var.rhel9_iso_path},if=none,id=cdrom0,media=cdrom"]
  ]
  ssh_timeout      = "1h"
  shutdown_timeout = var.timeout
  shutdown_command = "sudo systemctl poweroff"
  http_content = {
    "/rhel9.ks" = templatefile("${path.root}/http/rhel9.ks.pkrtpl.hcl",
      {
        KS_PROXY           = local.ks_proxy,
        KS_APPSTREAM_REPOS = var.ks_appstream_repos,
        communicator       = local.communicator,
        SSH_USERNAME       = var.ssh_username,
        SSH_PASSWORD       = var.ssh_password
      }
    )
  }
}

build {
  sources = ["source.qemu.rhel9"]

  provisioner "shell" {
   inline = [
      "echo '********** test msg vikas *****",
      "echo \"$USERNAME:$PASSWORD\" | sudo chpasswd",
      "sudo usermod -aG wheel $USERNAME",
      "echo \"$USERNAME ALL=(ALL) NOPASSWD:ALL\" | sudo tee /etc/sudoers.d/$USERNAME > /dev/null", # Use 'sudo tee' to avoid permission issues",
      "sudo chmod 440 /etc/sudoers.d/$USERNAME",
      "sudo sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config"
    ]
  }

  post-processor "shell-local" {
    inline = [
      "SOURCE=${source.name}",
      "OUTPUT=${var.filename}",
      "source ../scripts/fuse-nbd",
      "source ../scripts/fuse-tar-root",
      "rm -rf output-${source.name}",
    ]
    inline_shebang = "/bin/bash -e"
  }
}

rhel9.ks.pkrtpl.hcl

packer {
  required_version = ">= 1.11.0"
  required_plugins {
    qemu = {
      version = ">= 1.1.0, < 1.1.2"
      source  = "github.com/hashicorp/qemu"
    }
  }
}

variable "filename" {
  type        = string
  default     = "rhel9.tar.gz"
  description = "The filename of the tarball to produce"
}

variable "rhel9_iso_path" {
  type    = string
  default = "${env("RHEL9_ISO_PATH")}"
}

# Use --baseurl to specify the exact url for AppStream repo
variable "ks_appstream_repos" {
  type    = string
  default = "--baseurl='file:///run/install/repo/AppStream'"
}

variable ks_proxy {
  type    = string
  default = "${env("KS_PROXY")}"
}

variable "timeout" {
  type        = string
  default     = "1h"
  description = "Timeout for building the image"
}

variable "architecture" {
  type        = string
  default     = "x86_64"
  description = "The architecture to build the image for (amd64 or arm64)"
}

variable "host_is_arm" {
  type        = bool
  default     = false
  description = "The host architecture is aarch64"
}

variable "ovmf_suffix" {
  type        = string
  default     = ""
  description = "Suffix for OVMF CODE and VARS files. Newer systems such as Noble use _4M."
}

variable enable_ssh_provisioning {
  type    = bool
  default = true
}

variable ssh_username {
  type    = string
  default = "packer"
}

variable ssh_password {
  type    = string
  default = "packer"
}

variable ssh_user_cleanup {
  type    = bool
  default = true
}

locals {
  qemu_arch = {
    "x86_64"  = "x86_64"
    "aarch64" = "aarch64"
  }
  uefi_imp = {
    "x86_64"  = "OVMF"
    "aarch64" = "AAVMF"
  }
  uefi_sfx = {
    "x86_64"  = "${var.ovmf_suffix}"
    "aarch64" = ""
  }
  qemu_machine = {
    "x86_64"  = "accel=kvm"
    "aarch64" = var.host_is_arm ? "virt,accel=kvm" : "virt"
  }
  qemu_cpu = {
    "x86_64"  = "host"
    "aarch64" = var.host_is_arm ? "host" : "max"
  }
  communicator = var.enable_ssh_provisioning == true ? "ssh" : "none"
  ks_proxy    = var.ks_proxy != "" ? "--proxy=${var.ks_proxy}" : ""
}


source "qemu" "rhel9" {
  boot_command     = ["<up><wait>", "e", "<down><down><down><left>", " console=ttyS0 inst.cmdline inst.text inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/rhel9.ks <f10>"]
  boot_wait        = "5s"
  communicator     = local.communicator
  disk_size        = "4G"
  headless         = true
  iso_checksum     = "none"
  iso_url          = var.rhel9_iso_path
  ssh_username     = var.enable_ssh_provisioning == true ? var.ssh_username : null
  ssh_password     = var.enable_ssh_provisioning == true ? var.ssh_password : null  
  memory           = 2048
  cores            = 4
  qemu_binary      = "qemu-system-${lookup(local.qemu_arch, var.architecture, "")}"
  qemuargs = [
    ["-serial", "stdio"],
    ["-boot", "strict=off"],
    ["-device", "qemu-xhci"],
    ["-device", "usb-kbd"],
    ["-device", "virtio-net-pci,netdev=net0"],
    ["-netdev", "user,id=net0"],
    ["-device", "virtio-blk-pci,drive=drive0,bootindex=0"],
    ["-device", "virtio-blk-pci,drive=cdrom0,bootindex=1"],
    ["-machine", "${lookup(local.qemu_machine, var.architecture, "")}"],
    ["-cpu", "${lookup(local.qemu_cpu, var.architecture, "")}"],
    ["-device", "virtio-gpu-pci"],
    ["-global", "driver=cfi.pflash01,property=secure,value=off"],
    ["-drive", "if=pflash,format=raw,unit=0,id=ovmf_code,readonly=on,file=/usr/share/${lookup(local.uefi_imp, var.architecture, "")}/${lookup(local.uefi_imp, var.architecture, "")}_CODE${lookup(local.uefi_sfx, var.architecture, "")}.fd"],
    ["-drive", "if=pflash,format=raw,unit=1,id=ovmf_vars,file=${var.architecture}_VARS.fd"],
    ["-drive", "file=output-rhel9/packer-rhel9,if=none,id=drive0,cache=writeback,discard=ignore,format=qcow2"],
    ["-drive", "file=${var.rhel9_iso_path},if=none,id=cdrom0,media=cdrom"]
  ]
  ssh_timeout      = "1h"
  shutdown_timeout = var.timeout
  shutdown_command = "sudo systemctl poweroff"
  http_content = {
    "/rhel9.ks" = templatefile("${path.root}/http/rhel9.ks.pkrtpl.hcl",
      {
        KS_PROXY           = local.ks_proxy,
        KS_APPSTREAM_REPOS = var.ks_appstream_repos,
        communicator       = local.communicator,
        SSH_USERNAME       = var.ssh_username,
        SSH_PASSWORD       = var.ssh_password
      }
    )
  }
}

build {
  sources = ["source.qemu.rhel9"]

  provisioner "shell" {
   inline = [
      "echo '********** test msg vikas *****",
      "echo \"$USERNAME:$PASSWORD\" | sudo chpasswd",
      "sudo usermod -aG wheel $USERNAME",
      "echo \"$USERNAME ALL=(ALL) NOPASSWD:ALL\" | sudo tee /etc/sudoers.d/$USERNAME > /dev/null", # Use 'sudo tee' to avoid permission issues",
      "sudo chmod 440 /etc/sudoers.d/$USERNAME",
      "sudo sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config",
      "sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config"
    ]
  }

  post-processor "shell-local" {
    inline = [
      "SOURCE=${source.name}",
      "OUTPUT=${var.filename}",
      "source ../scripts/fuse-nbd",
      "source ../scripts/fuse-tar-root",
      "rm -rf output-${source.name}",
    ]
    inline_shebang = "/bin/bash -e"
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions