DEV Community

Ali Mehraji
Ali Mehraji

Posted on • Edited on

runcmd and package order in Cloud-Init

How is it possible to run a command via runcmd before installing its related package via packages in Cloud-Init? Which one runs first ?

In a Cloud-Init configuration, it may look strange that a command referencing a service can run before the package providing that service is installed. For example:

#cloud-config

package_update: true
package_upgrade: true

runcmd:
  - systemctl enable --now qemu-guest-agent.service

packages:
  - qemu-guest-agent

Enter fullscreen mode Exit fullscreen mode

In this setup, commonly used when provisioning VMs with Terraform’s libvirt provider on QEMU/KVM, runcmd attempts to enable and start qemu-guest-agent.service even though the package providing it (qemu-guest-agent) hasn’t been installed yet. The guest agent is often required early so tools like Ansible’s dynamic inventory can query host information.

So how can Cloud-Init run systemctl enable --now qemu-guest-agent.service before the package exists?

The sentence “The runcmd module runs very late in the boot process” can be confusing at first glance, because in Cloud-Init’s documented module stages, runcmd appears in the Config stage while package installation via packages happens in the Final stage.

It looks like runcmd runs before packages are installed and it wont work.

However, The runcmd module does not execute commands directly:

The runcmd module only writes the script to be run later. The module that actually runs the script is scripts_user in the Final boot stage.

So even though runcmd appears in the Config stage, the commands you place under runcmd are not executed immediately. They are scheduled to run later, after packages have been installed and other finalization tasks are complete.

By the time the scripts_user module runs (in the Final stage), the qemu-guest-agent is installed, qemu-guest-agent.service exists and systemctl enable --now will be succeed.

The runcmd scripts will be written in /var/lib/cloud/instance/scripts/runcmd in the instance.

cat /var/lib/cloud/instance/scripts/runcmd

#!/bin/sh
systemctl enable --now qemu-guest-agent
Enter fullscreen mode Exit fullscreen mode

cloud-config Example:

#cloud-config
users:
  - name: USERNAME
    groups: users, admin
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    gecos: "DevOps Engineer"
    passwd: "SHA-512 hashed password" # mkpasswd --method=SHA-512
    shell: /bin/bash
    ssh_authorized_keys:
      - "ssh-ed25519 ..."
      - "ssh-rsa ..."

ssh_pwauth: false

package_update: true
package_upgrade: true

apt:
  # file /etc/apt/apt.conf.d/cloud-init-aptproxy will be written
  http_proxy: http://<proxy-server>:port
  https_proxy: http://<proxy-server>:port

write_files:
  - path: /etc/ssh/sshd_config.d/ssh-hardening.conf
    content: |
      Port 2200
      KbdInteractiveAuthentication no
      ChallengeResponseAuthentication no
      MaxAuthTries 2
      AllowTcpForwarding no
      X11Forwarding no
      AllowAgentForwarding no
      AuthorizedKeysFile .ssh/authorized_keys

runcmd:
  - systemctl enable --now qemu-guest-agent

packages:
  - qemu-guest-agent
  - htop
  - tree

Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (0)