Ansible provisioner has no effect ?!

Hello,

I’m trying to use the ansible provisioner with a qemu builder. Everything runs through successfully… only the resulting image does not contain the changes imposed by ansible…
Really, I do not understand what’s happening here.

packer Version: 1.9.4
qemu builder plugin version: 1.0.9
ansible provisioner plugin version: 1.1.0
ansible version: 2.15.4
qemu version: 6.2.0
installed OS: Debian 12.1

This is my packer template:

packer {
  required_plugins {
    qemu = {
      source  = "github.com/hashicorp/qemu"
      version = "~> 1"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = "~> 1"
    }
  }
}

source "qemu" "debian" {
  accelerator = "kvm"
  iso_url = "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso"
  iso_checksum = "sha256:9f181ae12b25840a508786b1756c6352a0e58484998669288c4eec2ab16b8559"
  disk_size = 10000
  memory = 1024
  format = "qcow2"
  machine_type = "q35"
  output_directory = "output"
  vm_name = "debian"
  #headless = true
  ssh_username = "debian"
  ssh_password = "12345678"
  ssh_timeout = "15m"
  ssh_port = 22
  boot_command = [
    "<esc><wait>",
    "install <wait>",
    "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/debian_preseed.cfg <wait>",
    "debian-installer=en_US.UTF-8 <wait>",
    "auto <wait>",
    "locale=en_US.UTF-8 <wait>",
    "kbd-chooser/method=us <wait>",
    "keyboard-configuration/xkb-keymap=us <wait>",
    "netcfg/get_hostname={{ .Name }} <wait>",
    "netcfg/get_domain=local <wait>",
    "fb=false <wait>",
    "debconf/frontend=noninteractive <wait>",
    "console-setup/ask_detect=false <wait>",
    "console-keymaps-at/keymap=us <wait>",
    "<enter><wait>"
  ]
  http_directory = "http"
}

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

    provisioner "ansible" {
      playbook_file = "./ansible/debian.yaml"
      extra_arguments = [
        "--extra-vars", "ansible_password=12345678 ansible_become_password=12345678",
        "-vvvv"
      ]
      ansible_env_vars = [
        "ANSIBLE_HOST_KEY_CHECKING=False"
      ]
      use_proxy = false
      user = "debian"
      local_port = 22
      keep_inventory_file = true
    }
}

This is my ansible playbook:

- name: 'Perform image setup'
  hosts: default
  become: true

  tasks:

    - name: Append to kernel parameters
      ansible.builtin.lineinfile:
        path: /etc/default/grub
        search_string: 'GRUB_CMDLINE_LINUX='
        line: 'GRUB_CMDLINE_LINUX="console=ttyS0,115200"'
      register: test

    - name: Slurp grub file
      slurp:
        src: /etc/default/grub
      register: slurpfile

    - debug:
        msg:
          - "{{ slurpfile['content'] | b64decode }}"

    - name: update grub
      ansible.builtin.command: '/usr/sbin/update-grub'

As you can see, I output the file /etc/default/grub from within the ansible script. This gives the correct result. But when I boot the image after the packer build, the file is unchanged as if ansible did not run at all.

I would be very thankful for an explaination and/or a fix.

Background:
My intention is to create VMs with terraform from this image. Also I would like to be able to connect to these VMs even if they do not have a working network setup (for debugging). If I created the VM from within virt-manager or a similar tool, I would just open the graphical console (it’s backed by VNC, I suppose). But virt-manager does not list VMs created with terraform. Instead I’ve found the solution virsh console but this requires the kernel parameter console to be set. This is what I would like to achieve with the ansible script.

In fact I’ve tried the shell provisioner before, but with the same result. I thought this was because I did not manage to deal correctly with four (!!) nested levels of quotes – so I’ve switched to ansible, which is much more readable, fortunately.

So as an alternative to answer my question above would be to suggest other means to connect to qemu VMs created by terraform…

Thanks and Regards,
Carsten

PS: this is the output of packer build (without ansible -vvvv):

qemu.debian: output will be in this color.

==> qemu.debian: Retrieving ISO
==> qemu.debian: Trying https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso
==> qemu.debian: Trying https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso?checksum=sha256%3A9f181ae12b25840a508786b1756c6352a0e58484998669288c4eec2ab16b8559
==> qemu.debian: https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso?checksum=sha256%3A9f181ae12b25840a508786b1756c6352a0e58484998669288c4eec2ab16b8559 => /home/carsten/.cache/packer/dced31146371c3392eb605a2c5ae08eb97048d9e.iso
==> qemu.debian: Starting HTTP server on port 8432
==> qemu.debian: Found port for communicator (SSH, WinRM, etc): 4341.
==> qemu.debian: Looking for available port between 5900 and 6000 on 127.0.0.1
==> qemu.debian: Starting VM, booting from CD-ROM
==> qemu.debian: Waiting 10s for boot...
==> qemu.debian: Connecting to VM via VNC (127.0.0.1:5984)
==> qemu.debian: Typing the boot commands over VNC...
    qemu.debian: Not using a NetBridge -- skipping StepWaitGuestAddress
==> qemu.debian: Using SSH communicator to connect: 127.0.0.1
==> qemu.debian: Waiting for SSH to become available...
==> qemu.debian: Connected to SSH!
==> qemu.debian: Provisioning with Ansible...
    qemu.debian: Not using Proxy adapter for Ansible run:
    qemu.debian:        Using ssh keys from Packer communicator...
==> qemu.debian: Executing Ansible: ansible-playbook -e packer_build_name="debian" -e packer_builder_type=qemu -e packer_http_addr=10.0.2.2:8432 --ssh-extra-args '-o IdentitiesOnly=yes' --extra-vars ansible_password=***** -i /tmp/packer-provisioner-ansible1226677173 /home/carsten/source/nomad/packer/ansible/debian.yaml
    qemu.debian:
    qemu.debian: PLAY [Perform image setup] *****************************************************
    qemu.debian:
    qemu.debian: TASK [Gathering Facts] *********************************************************
    qemu.debian: ok: [default]
    qemu.debian:
    qemu.debian: TASK [Append to kernel parameters] *********************************************
    qemu.debian: changed: [default]
    qemu.debian:
    qemu.debian: TASK [Slurp grub file] *********************************************************
    qemu.debian: ok: [default]
    qemu.debian:
    qemu.debian: TASK [debug] *******************************************************************
    qemu.debian: ok: [default] => {
    qemu.debian:     "msg": [
    qemu.debian:         "# If you change this file, run 'update-grub' afterwards to update\n# /boot/grub/grub.cfg.\n# For full documentation of the options in this file, see:\n#   info -f grub -n 'Simple configuration'\n\nGRUB_DEFAULT=0\nGRUB_TIMEOUT=5\nGRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`\nGRUB_CMDLINE_LINUX_DEFAULT=\"quiet\"\nGRUB_CMDLINE_LINUX=\"console=ttyS0,115200\"\n\n# If your computer has multiple operating systems installed, then you\n# probably want to run os-prober. However, if your computer is a host\n# for guest OSes installed via LVM or raw disk devices, running\n# os-prober can cause damage to those guest OSes as it mounts\n# filesystems to look for things.\n#GRUB_DISABLE_OS_PROBER=false\n\n# Uncomment to enable BadRAM filtering, modify to suit your needs\n# This works with Linux (no patch required) and with any kernel that obtains\n# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)\n#GRUB_BADRAM=\"0x01234567,0xfefefefe,0x89abcdef,0xefefefef\"\n\n# Uncomment to disable graphical terminal\n#GRUB_TERMINAL=console\n\n# The resolution used on graphical terminal\n# note that you can use only modes which your graphic card supports via VBE\n# you can see them in real GRUB with the command `vbeinfo'\n#GRUB_GFXMODE=640x480\n\n# Uncomment if you don't want GRUB to pass \"root=UUID=xxx\" parameter to Linux\n#GRUB_DISABLE_LINUX_UUID=true\n\n# Uncomment to disable generation of recovery mode menu entries\n#GRUB_DISABLE_RECOVERY=\"true\"\n\n# Uncomment to get a beep at grub start\n#GRUB_INIT_TUNE=\"480 440 1\"\n"
    qemu.debian:     ]
    qemu.debian: }
    qemu.debian:
    qemu.debian: TASK [update grub] *************************************************************
    qemu.debian: changed: [default]
    qemu.debian:
    qemu.debian: PLAY RECAP *********************************************************************
    qemu.debian: default                    : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    qemu.debian:
==> qemu.debian: Halting the virtual machine...
==> qemu.debian: Converting hard drive...
==> qemu.debian: Error getting file lock for conversion; retrying...
Build 'qemu.debian' finished after 7 minutes 26 seconds.

==> Wait completed after 7 minutes 26 seconds

==> Builds finished. The artifacts of successful builds are:
--> qemu.debian: VM files in directory: output

Ok, at least I’ve found a solution to connect to a VM created by terraform: virt-viewer. Nevertheless it would be very beneficial to be able to use the ansible provisioner.