Chapter 3. Configuring cloud-init

This chapter includes examples of the most common configuration tasks for cloud-init.

Your cloud-init configuration can require that you add directives to the cloud.cfg file and the cloud.cfg.d directory. Alternatively, your specific data source might require that you add directives to files, such as a user data file and a metadata file. A data source might require that you upload your directives to an HTTP server. Check the requirements of your data source and add directives accordingly.

3.1. Creating a virtual machine that includes cloud-init for a NoCloud datasource

To create a new virtual machine (VM) that includes cloud-init, see the following procedure. In this procedure, you create a meta-data and user-data file.

  • Your meta-data file includes instance details.
  • Your user-data file includes information to create a user and grant access.

Then, you include these files in a new ISO image, and you attach the ISO file to a new VM you create from a KVM Guest Image. In this scenario, the datasource is NoCloud.

Procedure

  1. Create a directory named cloudinitiso and move into it.

    $ mkdir cloudinitiso
    $ cd cloudinitiso
  2. Create a file named meta-data. Add the following information to the file.

    instance-id: citest
    local-hostname: citest-1
  3. Create a file named user-data. Include the following information in the file.

    #cloud-config
    password: cilogon
    chpasswd: {expire: False}
    ssh_pwauth: True
    ssh_authorized_keys:
      - ssh-rsa AAA...fhHQ== sample@redhat.com
    Note

    The final line of the user-data file references an SSH public key. Find your SSH public keys in ~/.ssh/id_rsa.pub. When trying this sample procedure, modify the line to include one of your public keys.

  4. Use the genisoimage command to create an ISO image that includes user-data and meta-data.

    # genisoimage -output ciiso.iso -volid cidata -joliet -rock user-data meta-data
    
    I: -input-charset not specified, using utf-8 (detected in locale settings)
    Total translation table size: 0
    Total rockridge attributes bytes: 331
    Total directory bytes: 0
    Path table size(bytes): 10
    Max brk space used 0
    183 extents written (0 MB)
  5. Download a KVM Guest Image from the Red Hat Customer Portal to the /var/lib/libvirt/images directory.
  6. Create a new VM from the KVM Guest Image using the virt-install command. Include the ISO image you created as an attachment to the image.

    virt-install \
        --memory 4096 \
        --vcpus 4 \
        --name mytestcivm \
        --disk /var/lib/libvirt/images/rhel-8.1-x86_64-kvm.qcow2,device=disk,bus=virtio,format=qcow2 \
        --disk /home/sample/cloudinitiso/ciiso.iso,device=cdrom \
        --os-type Linux \
        --os-variant rhel9.0 \
        --virt-type kvm \
        --graphics none \
        --import
  7. Log on to your image as cloud-user. Your password is cilogon.

    citest-1 login: cloud-user
    Password:
    [cloud-user@citest-1 ~]$

Verification

  • Check the cloud-init status to see that it has completed its tasks.

    [cloud-user@citest-1 instance]$ cloud-init status
    status: done
  • cloud-init creates the cloud-init directory layout under /var/lib/cloud when it runs, and it updates or changes certain directory contents based upon the directives you have specified.

    For example, you can confirm that the datasource is NoCloud by checking the datasource file.

    $ cd /var/lib/cloud/instance
    $ cat datasource
    DataSourceNoCloud: DataSourceNoCloud [seed=/dev/sr0][dsmode=net]

    cloud-init copies user-data into /var/lib/cloud/instance/user-data.txt.

    $cat user-data.txt
    #cloud-config
    password: cilogon
    chpasswd: {expire: False}
    ssh_pwauth: True
    ssh_authorized_keys:
      - ssh-rsa AAA...fhHQ== sample@redhat.com

    These are samples. The cloud-init directory layout includes much more information.

Note

For OpenStack, the Creating and managing instances includes information for configuring an instance using cloud-init. See Creating a customized instance for specific procedures.

3.2. Expiring a cloud user password with cloud-init

You can force cloud-user to change the cloud-user password at the first login. Perform the following procedure to expire a password.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Change the line chpasswd: {expire: False} to chpasswd: {expire: True}.

    #cloud-config
    password: mypassword
    chpasswd: {expire: True}
    ssh_pwauth: True
    ssh_authorized_keys:
      - ssh-rsa AAA...SDvz user1@yourdomain.com
      - ssh-rsa AAB...QTuo user2@yourdomain.com

    This works to expire the password because password and chpasswd operate on the default user unless you indicate otherwise.

    Note

    This is a global setting. When you set chpasswd to True, all users you create need to change their passwords when they log in.

3.3. Changing a default user name with cloud-init

You can change the default user name to something other than cloud-user.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add the line user: <username>, replacing <username> with the new default user name.

    #cloud-config
    user: username
    password: mypassword
    chpasswd: {expire: False}
    ssh_pwauth: True
    ssh_authorized_keys:
      - ssh-rsa AAA...SDvz user1@yourdomain.com
      - ssh-rsa AAB...QTuo user2@yourdomain.com

3.4. Setting a root password with cloud-init

To set the root password, create a user list.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Create a user list in the chpasswd section of the file. The format is shown in the following sample.

    Note

    White space is significant. Do not include white space before or after the colon in your user list. If you include white space, the password is set with a space in it.

    #cloud-config
    ssh_pwauth: True
    ssh_authorized_keys:
      - ssh-rsa AAA...SDvz user1@yourdomain.com
      - ssh-rsa AAB...QTuo user2@yourdomain.com
    chpasswd:
      list: |
         root:myrootpassword
         cloud-user:mypassword
      expire: False
    Note

    If you use this method to set the user password, you must set all passwords in this section.

3.5. Managing Red Hat subscriptions with cloud-init

You can use the rh_subscription directive to register your system. Samples follow. For each subscription, you would edit your user data.

Procedure

The following example uses the auto-attach and service-level options.

  • Under rh_subscription, add your username and password, set auto-attach to True, and set service-level to self-support.

    rh_subscription:
      username: sample@redhat.com
      password: 'mypassword'
      auto-attach: True
      service-level: self-support
    Note

    The service-level option requires that you use the auto-attach option.

The following example uses the activation-key and org options.

  • Under rh_subscription, add your activation key and org number and set auto-attach to True.

    rh_subscription:
      activation-key: example_key
      org: 12345
      auto-attach: True

The following example adds a subscription pool.

  • Under rh_subscription, add your username, password, and pool number.

    rh_subscription:
      username: sample@redhat.com
      password: 'password'
      add-pool: XYZ01234567
    Note

    This sample is the equivalent of the subscription-manager attach --pool=XYZ01234567 command.

The following example sets a server host name in the /etc/rhsm/rhsm.conf file.

  • Under rh_subscription, add your username, password, server-hostname, and set auto-attach to True.

    rh_subscription:
      username: sample@redhat.com
      password: 'password'
      server-hostname: test.example.com
      auto-attach: True

3.6. Adding users and user options with cloud-init

You create and describe users in a users section. You can modify the section to add more users to your initial system configuration, and you can set additional user options.

If you add the users section, you must also set the default user options in this section.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add or modify the users section to add users.

    • If you want cloud-user to be the default user created along with the other users you specify, ensure that you add default as the first entry in the section. If it is not the first entry, cloud-user is not created.
    • By default, users are labeled as unconfined_u if there is not an selinux-user value.

      #cloud-config
      users:
        - default
        - name: user2
          gecos: User N. Ame
          selinux-user: staff_u
          groups: users,wheel
          ssh_pwauth: True
          ssh_authorized_keys:
            - ssh-rsa AA..vz user@domain.com
      chpasswd:
        list: |
          root:password
          cloud-user:mypassword
          user2:mypassword2
        expire: False
      Note
      • The example places the user user2 into two groups, users and wheel.

3.7. Running first boot commands with cloud-init

You can use the runcmd and bootcmd sections to execute commands during startup and initialization.

The bootcmd section executes early in the initialization process and by default runs on every boot. The runcmd section executes near the end of the process and is only executed during the first boot and initialization.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add the sections for bootcmd and runcmd; include commands you want cloud-init to execute.

    #cloud-config
    users:
      - default
      - name: user2
        gecos: User N. Ame
        groups: users
    chpasswd:
      list: |
        root:password
        fedora:myfedpassword
        user2:mypassword2
      expire: False
    bootcmd:
     - echo New MOTD >> /etc/motd
    runcmd:
     - echo New MOTD2 >> /etc/motd

3.8. Adding additional sudoers with cloud-init

You can configure a user as a sudoer by adding a sudo and groups entry to the users section.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add a sudo entry and specify the user access. For example, sudo: ALL=(ALL) NOPASSWD:ALL allows a user unrestricted user access.
  3. Add a groups entry and specify the groups that include the user.

    #cloud-config
    users:
      - default
      - name: user2
        gecos: User D. Two
        sudo: ["ALL=(ALL) NOPASSWD:ALL"]
        groups: wheel,adm,systemd-journal
        ssh_pwauth: True
        ssh_authorized_keys:
          - ssh-rsa AA...vz user@domain.com
    chpasswd:
      list: |
        root:password
        cloud-user:mypassword
        user2:mypassword2
      expire: False

3.9. Setting up a static networking configuration with cloud-init

You can set up your network configuration with cloud-init by adding a network-interfaces section to your metadata.

Red Hat Enterprise Linux provides its default networking service through NetworkManager, which is a dynamic network control and configuration daemon that keeps network devices and connections up and active when they are available.

Your datasource might provide a network configuration. Refer to the cloud-init documentation section Network Configuration Sources for more information.

If you specify no network configuration for cloud-init and have not disabled network configuration, cloud-init tries to determine if any attached devices have a connection. If it finds a connected device, it generates a network configuration that issues a DHCP request on the interface. Refer to the cloud-init documentation section Fallback Network Configuration for more information.

Procedure

The following example adds a static networking configuration.

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add a network-interfaces section.

    network:
      version: 1
      config:
        - type: physical
          name: eth0
          subnets:
            - type: static
              address: 192.168.1.10/24
              gateway: 192.168.1.254
Note

You can disable a network configuration by adding the following information to your metadata.

network
  config: disabled

Additional resources

3.10. Configuring only a root user with cloud-init

You can configure your user data so that you have a root user and no other users.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Create an entry for the user root in the users section.

    The simple example that follows includes a users section with only the name option.

    users:
      - name: root
    chpasswd:
      list: |
        root:password
      expire: False
  3. Optionally, set up SSH keys for the root user.

    users:
      - name: root
        ssh_pwauth: True
        ssh_authorized_keys:
          - ssh-rsa AA..vz user@domain.com

3.11. Setting up storage with container-storage-setup in cloud-init

You can set up storage by referencing the container-storage-setup utility within the write_files module.

Procedure

  1. Depending upon the requirements of your datasource, open your user-data file for editing, or otherwise add the following directive to the cloud.cfg.d directory.

    Note

    All user directives include #cloud-config at the top of the file so that cloud-init recognizes the file as containing user directives. When you include directives in the cloud.cfg.d directory, name the file *.cfg, and always include #cloud-config at the top of the file.

  2. Add or modify the write_files module to include the path to the container-storage-setup utility.

    The following example sets the size of the root logical volume to 6GB rather than the default 3GB.

    write_files:
      - path: /etc/sysconfig/docker-storage-setup
        permissions: 0644
        owner: root
        content: |
        ROOT_SIZE=6G
    Note

    Prior to RHEL 7.4, container-storage-setup was called docker-storage-setup. If you are using OverlayFS for storage, as of RHEL 7.4 you can now use that type of file system with SELinux in enforcing mode.

3.12. Changing the system locale with cloud-init

You can configure the system location with the locale module.

Procedure

  1. Depending upon the requirements of your datasource, open your meta-data file for editing, or otherwise add the following directive to the cloud.cfg file or the cloud.cfg.d directory.
  2. Add the locale directive, specifying the location. The following sample sets the locale to ja_JP (Japan) with UTF-8 encoding.
#cloud-config
locale: ja_JP.UTF-8

Additional resources

3.13. cloud-init and shell scripts

You can add list values or string values to bootcmd or runcmd. You can also provide a shell script within your userdata.

  • If you use a list value for bootcmd or runcmd, each list item is run in turn using execve.
  • If you use a string value, then the entire string is run as a shell script.
  • If you want to use cloud-init to run a shell script, you can provide a shell script (complete with shebang (#!) ) instead of providing cloud-init with a .yaml file.

Refer to Run commands on first boot for examples of how to put shell scripts in bootcmd and runcmd.

3.14. Preventing cloud-init from updating config files

When you create or restore an instance from a backup image, the instance ID changes. The change in instance ID can cause cloud-init to update configuration files.

Perform the following procedure to ensure that cloud-init does not update certain configuration files when you create or restore from backup.

Procedure

  1. Open the /etc/cloud/cloud.cfg file for editing.
  2. Comment out or remove the configuration that you do not want cloud-init to update when you restore your instance.

    For example, to avoid updating the SSH key file, remove -ssh from the cloud_init_modules section.

    cloud_init_modules:
     - disk_setup
     - migrator
     - bootcmd
     - write-files
     - growpart
     - resizefs
     - set_hostname
     - update_hostname
     - update_etc_hosts
     - rsyslog
     - users-groups
     # - ssh

Verification

You can check to see which configuration files cloud-init has updated. To do so, examine the /var/log/cloud/cloud-init.log file. Updated files are logged during instance startup with messages beginning with Writing to. For example:

2019-09-03 00:16:07,XXX - util.py[DEBUG]: Writing to /root/.ssh/authorized_keys - wb: [XXX] 554 bytes
2019-09-03 00:16:08,XXX - util.py[DEBUG]: Writing to /etc/ssh/sshd_config - wb: [XXX] 3905 bytes

3.15. Modifying a VM created from a KVM Guest Image after cloud-init has run

To modify your cloud-init configuration before rerunning cloud-init, use the following procedure. When you launch a VM that includes the cloud-init package installed and enabled, cloud-init runs in its default state on that initial boot of your VM.

Procedure

  1. Log in to your VM.
  2. Add or change directives, for example, modify the cloud.cfg file in the /etc/cloud directory or add directives to the /etc/cloud/cloud.cfg.d directory.
  3. Run the cloud-init clean command to clean directories so that cloud-init can rerun. You can also run the following commands as root to clean the VM.

    `rm -Rf /var/lib/cloud/instances/*`
    `rm -Rf /var/lib/cloud/instance`
    `rm -Rf /var/lib/cloud/data/*`
    Note

    You can save the cleaned image as a new image and use that image for multiple VMs. The new VMs run cloud-init using your updated cloud-init configuration.

  4. Rerun cloud-init or reboot the VM.

    cloud-init reruns, implementing the configuration changes you made.

3.16. Modifying a VM for a specific datasource after cloud-init has run

To modify your cloud-init configuration before rerunning cloud-init, see the following procedure. This procedure uses OpenStack as an example. Note that the exact steps you need to perform vary based on your datasource.

Procedure

  1. Create and launch an instance for the OpenStack Platform. For information on creating instances for OpenStack, see Creating an instance. In this example, our virtual machine includes cloud-init, which runs upon boot of the virtual machine.
  2. Add or change directives. For example, modify the user-data.file file that is stored on the OpenStack HTTP server.
  3. Clean the virtual machine. Run the following commands as root.

    `rm -rf /etc/resolv.conf /run/cloud-init`
    `userdel -rf cloud-user`
    `hostnamectl set-hostname localhost.localdomain`
    `rm /etc/NetworkManager/conf.d/99-cloud-init.conf`
    Note

    You can save the cleaned image as a new image and use that image for multiple virtual machines. The new virtual machines run cloud-init using your updated cloud-init configuration.

  4. Rerun cloud-init or reboot the virtual machine.

    Cloud-init reruns, implementing the configuration changes you made.

3.17. Troubleshooting cloud-init

You can troubleshoot your instance after cloud-init has run by examining your configuration and log files. Once you have identified the issue, you can rerun cloud-init on your instance.

You can run cloud-init from the command line using the cloud-init command. To view the command syntax, along with a description of the optional arguments and subcommands, run the cloud-init --help command. The basic syntax follows.

cloud-init [-h] [--version] [--file FILES] [--debug] [--force]
{init,modules,single,query,dhclient-hook,features,analyze,devel,collect-logs,clean,status}

The procedure that follows offers ideas for identifying issues with cloud-init and samples for rerunning the program.

Procedure

  1. Review the cloud-init configuration files.

    1. Examine the /etc/cloud/cloud.cfg configuration file. Check which modules are included under cloud_init_modules, cloud_config_modules, and cloud_final_modules.
    2. Check directives (*.cfg files) in the /etc/cloud/cloud.cfg.d directory.
  2. Review the /var/log/cloud-init.log and /var/log/cloud-init-output.log files for details on a specific issue. For example, if the issue was that the root partition was not automatically extended, check log messages for growpart. If the file system was not extended, check log messages for resizefs. For example:

    # grep resizefs /var/log/cloud-init.log
    Note

    growpart does not support LVM. If your root partition is based in LVM, the root partition is not automatically extended upon first boot.

  3. Rerun cloud-init. Sample scenarios follow. Run commands as root.

    • Rerun cloud-init with only the init modules.

      /usr/bin/cloud-init -d init
    • Rerun cloud-init with all modules in your configuration.

      /usr/bin/cloud-init -d modules
    • Delete the cloud-init cache and force cloud-init to run after boot.

      rm -rf /var/lib/cloud/* && /usr/bin/cloud-init -d init
    • Run the following commands to clean directories and simulate a clean instance.

      rm -Rf /var/lib/cloud/instances/*
      rm -Rf /var/lib/cloud/instance
      rm -Rf /var/lib/cloud/data/*
      reboot
    • Run the following commands to rerun cloud-init.

      cloud-init init --local
      cloud-init init

Additional resources