Using Cloud-init with CIQ’s Rocky Linux

In the first post of this series, we showed how to create a Rocky Linux 8.5 virtual machine from CIQ’s Rocky Linux 8.5 image using Microsoft Azure’s web portal. This is the quickest way to get a running virtual machine since no software need be installed locally and the portal can be used to create (or name) key material, e.g. a .pem file. You get convenience and speed to an outcome, but you sacrifice repeatability and customization. In the second post of this series, we addressed repeatability not by creating new virtual machines from the portal, but – wait for it! – from the command line. This required installing some client side software, specifically the Azure CLI toolkit, but the installation and configuration can be done in less than an hour and is done once and for all as was shown in the previous article.

You could stop there. Many people (including me) will prefer issuing commands at a command line. It can be clearer who’s doing what. There are many use cases where a single command creating a stock Rocky is sufficient. Any subsequent customizations are best done in an “exploratory” manner on the virtual machine itself. But there are also many use cases or scenarios where the Rocky image is only the starting point and the virtual machine needs additional massaging and customization after it starts. This is so common in fact that a useful utility cloud-init was created to do just that and has been broadly adopted by most Linux distributions, including Rocky, and by almost all cloud vendors, including Azure.

In this third and final article, we’ll (start to) address customization using cloud-init. Of course, cloud-init isn’t the only way to repeat an installation multiple times. You could create a virtual machine, customize it to some desired configuration (or state) and then snapshot it. That snapshot can then form the basis for creating new virtual machines. It’s just a different starting point than the stock Rocky image. With a little bit of work and wizardry you could even combine the two approaches, getting your virtual machine to some desired configuration, re-enabling the “first time boot” status of that machine and snapshotting it. Then you have a customized image amenable to its own cloud-init script(s) but beginning at a different starting point. But such advanced usage is beyond the scope of this particular article. We’re going to do a few simple things. They illustrate what’s possible.

Cloud-init itself is quite rich. Multiple scripts can be run in a certain order. The scripts can be in any programming language already installed or you can install the language interpreter of your choice. Some scripts are declarative (yaml format) and can be cloud agnostic. There’s a module system to include new functionality. And “callouts” to devops frameworks like chef or puppet are supported and relatively convenient. Rocky is a “first class citizen” for cloud-init and almost anything else you’d need is a dnf install away.

Hello, Cloud-init

We’ll walk before we run (but never with scissors). In the previous two articles, we spun up Rockies that were “chatty”. They announced some details for first time users, which is useful, well, for the first time. And eventually get old. So we’ll write a short bash script that turns some of the announcements off. This approach has two benefits: 

  1. You can write and debug the script on your local machine first. Bash even has a (hard to find) debugger for this.
  2. We’ll use a language you probably know quite well already, bash of course.

Remember from the last article that we created a Rocky virtual machine with the command below which I’ll repeat for you now. We populate user rocky’s authorized_keys with the default public key on your local machine. And we may be prompted to accept ssh host keys as needed but who’s prompt I’ve elided to reduce visual clutter:

## By convention, I’ll describe each (small) bash stanza’s intent first.
## Names starting with `your_` are meant to be substituted, e.g `your_group`
## is the Azure resource group from your Azure account.

$ export AZURE_ROCKY85=ctrliqinc1648673227698:rocky-8-5-x86_64-0:rocky-8-5-x86_64-paid:8.5.20220406
$ az vm create  --resource-group your_group \
                --name your_cli --image ${AZURE_ROCKY85} \
                --admin-username rocky --generate-ssh-keys

{
  "fqdns": "",
  "id": "/subscriptions/your_subscription/resourceGroups/your_group/providers/Microsoft.Compute/virtualMachines/your_cli",
  "location": "eastus",
  "macAddress": "00-0D-3A-1D-37-25",
  "powerState": "VM running",
  "privateIpAddress": "10.3.0.5",
  "publicIpAddress": "203.0.113.101",
  "resourceGroup": "your_group",
  "zones": ""
}

$ ssh [email protected] 
Activate the web console with: systemctl enable --now cockpit.socket

This last message (Activate …) is what we want to remove. To do that, we’ll enable and start the cockpit service and turn off the “message of the day” /etc/motd and other login announcements using ~/.hushlogin. Here’s a simple bash script example creatively named your_example.cloud-init.sh that does that. It will be run as user root and will have all the privileges that root has:

#!/usr/bin/env bash


# Upgrade packages on first boot. Install some personal preferences.
dnf upgrade -y
dnf install -y emacs-nox tree mlocate
updatedb

# Start cockpit, a modern system admin tool.
# We ignore external access to port 9090, cockpit’s default port.
systemctl enable --now cockpit.socket
systemctl start cockpit


# Quiet the login for user `rocky`
install -o rocky -g rocky /dev/null ~rocky/.hushlogin

We’re almost there. Let’s use your_example.cloud-init.sh with the --custom-data switch:

## By convention, I’ll describe each (small) bash stanza’s intent first.

$ export AZURE_ROCKY85=ctrliqinc1648673227698:rocky-8-5-x86_64-0:rocky-8-5-x86_64-paid:8.5.20220406
## Create a vm with the ${AZURE_ROCKY85} designator and customize it
## with a cloud init bash script.

$ az vm create  --resource-group your_group --name your_cli --image ${AZURE_ROCKY85} --admin-username rocky --generate-ssh-keys --custom-data your_example.cloud-init.sh
{
  "fqdns": "",
  "id": "/subscriptions/your_subscription/resourceGroups/your_group/providers/Microsoft.Compute/virtualMachines/your_cli",
  "location": "eastus",
  "macAddress": "00-0D-3A-1D-D2-DD",
  "powerState": "VM running",
  "privateIpAddress": "10.3.0.9",
  "publicIpAddress": "203.0.113.101",
  "resourceGroup": "your_group",
  "zones": ""
}



## Ssh to the new vm. There should be no announcements.
## Cockpit should be enabled and running. It might not be accessible
## directly from your local machine, depending on various firewalls.

$ ssh [email protected]
[[email protected]_cli ~]$ ls -la .hushlogin
-rwxr-xr-x. 1 rocky rocky 0 Apr 29 17:34 .hushlogin

[[email protected]_cli ~]$ systemctl status cockpit
● cockpit.service - Cockpit Web Service
   Loaded: loaded (/usr/lib/systemd/system/cockpit.service; static; vendor preset: enabled)
   Active: active (running) since Fri 2022-04-29 17:34:19 UTC; 1min 6s ago
     Docs: man:cockpit-ws(8)
 Main PID: 1915 (cockpit-tls)
    Tasks: 1 (limit: 20436)
   Memory: 2.1M
   CGroup: /system.slice/cockpit.service
           └─1915 /usr/libexec/cockpit-tls

Apr 29 17:34:17 your_cli systemd[1]: Starting Cockpit Web Service...
Apr 29 17:34:19 your_cli systemd[1]: Started Cockpit Web Service.

In our example, the script is short, just a few lines. But it runs, as root, after the machine has started, asynchronously. Want to do more? Put more in the script. Or run multiple scripts. Or both. Want to see what happened? Review the cloud-init log file /var/log/cloud-init-output.log

We barely scratched the surface of what’s possible. Azure, for example, provides services that report on the cloud “service fabric” and that will provide metadata about the running vm. These services are exposed as RESTful web apis. They can be used to write more general versions of your_example.cloud_init.sh. But we needed to stop somewhere so we’ll pick up this thread in a future post. Nonetheless as you can see from just these articles, Rocky Linux is ready for your computing needs, on your premises or in the cloud, whether you have some simple case spinning up stock Rocky instances or something more sophisticated with fleets of machines that are coordinated in various ways. We hope you give it a spin and apply your creativity to this rocky solid … ummm … rock solid release. 

Here are the first two articles in this series:
Part 1: CIQ’s Rocky Linux Images Now Available on Azure
Part 2: Using the Azure Command Line Interface with CIQ’s Rocky Linux

About the Author:

Mike Carifio

Mike Carifio supports Rocky Linux for CIQ. He started programming on a DEC PDP 11/45, before Unix was even a thing, and never looked back. He’s filled every high tech role imaginable, but had never written a blog post for CIQ. Mission accomplished, twice over.

You might be interested in …