cloud-init is an awesome technology that can be used to customize Linux images for deployment, that lets you do all kinds of neat things such as automatically creating users, installing packages, resetting SSH keys, and more. However, it’s often shrouded in mystery. In this video, I’ll walk you through using it to create a user, set the hostname, and install some packages.
Note: Ubuntu 20.04 is the example Linux distribution used in this video. While most (if not all) of the concepts should work in other distributions, cross-distro compatibility cannot be guaranteed.
To get started, we first need to see whether or not
cloud-init is already installed:
dpkg --get-selections | grep cloud-init
If it’s not installed, install it:
sudo apt update
sudo apt install cloud-init
If you already have
cloud-init installed, you can purge it and then reinstall it with the following commands:
sudo apt remove --purge cloud-init
sudo apt install cloud-init
Once that package is installed, we will have the
/etc/cloud directory on our system with some defaults in it:
The file that we’re most concerned with is the
cloud.cfg file. Let’s make a backup of it:
sudo cp cloud.cfg cloud.cfg.bak
Next, open the file in an editor. You can use whatever editor you want, but nano is an easy choice if you don’t have a preference. I normally use vim, in case you’re curious.
sudo nano cloud.cfg
As you can see, this file is quite large. There are several very specific things that I’ll recommend updating. You can use the following document as a guide, in case you’re curious about the modules that aren’t being modified and what they’re responsible for:
On that site, you’ll see a list of modules. Looking at your
cloud.cfg file, you’ll see that many of these modules are in use. I like to remove any of them that aren’t specific to what I wan to do, or aren’t relevant in some way.
Most of these we’re going to leave alone. If in doubt about what a line does, either check the modules list in the documentation page to see if it applies to you, or simply leave that line alone. But there’s a few I want to remove because I know for sure that they’re not needed. Specifically,
puppet salt-minion, etc. I don’t use those personally, but feel free to leave those alone if they have value to you.
Now, let’s look at customizing the user. At the top, we have a users section. In this case, it’s creating the default user. For example, on ubuntu systems, this will create a user named
ubuntu. Debian’s default user is just called
debian. This will vary from one distribution to another. If you leave this configuration here, a default user will be created when the config is run. I don’t like this, I prefer to have a user with my name. I just comment out that particular section:
# – default
Scroll down a bit, because there’s an entire section dedicated to the default user. Comment out that entire section.
groups: [adm, audio, cdrom, dialout…]
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
# name: ubuntu
# lock_passwd: True
# gecos: Ubuntu
# groups: [adm, audio, cdrom, dialout…]
# sudo: ["ALL=(ALL) NOPASSWD:ALL"]
# shell: /bin/bash
Save the file for now, and exit. Don’t reboot, we’ll need to add additional tweaks to the
cloud.cfg file. For now, we’ll make a quick detour and generate a password hash for a user we’re going to be creating. We can use the
mkpasswd command for that purpose. First, check to see if you have the
mkpasswd command available:
If you don’t see any output, you may need to install the
whois package, at least on Debian and Ubuntu. The name of that package might be different if you’re using a different distribution.
sudo apt install whois
mkpasswd command available, we can generate our password hash:
mkpasswd -m sha-512
At the prompt, type the password that you want your user to have. That will generate the hash. Copy the password hash, and we’ll return to editing our file.
sudo vim cloud.cfg
At the top of the file, we’ll add the following content:
- name: jay
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAVfgmEwwQo426MUYcnIP2x1/edoweERB3Ysy6vpxBzo jay
groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
Note: Don’t copy and paste the example password hash above into your file, instead use the password hash that you generated earlier.
Note 2: For the list of groups, make sure to check to see if those groups actually exist before you type them in. You can check the contents of
/etc/group to see a list of groups on your system. Basically, just make sure you’re not referencing a group on that line that doesn’t actually exist on your system.
The next thing that I recommend you change in your file, is adding your time zone. There should already be a line that references timezone, but it won’t designate a specific timezone. You can append your time zone to the end of that line.
If don’t know what your time zone is, you can find it from this page (just choose the timezone that best matches your location).
At the end of the config file, add the following line if you’d like:
- date > /etc/birth_certificate
That line will cause the current date to be placed into the
/etc/birth_certificate file as soon as the instance comes online. Optional, but fun.
Perhaps more useful, we can add configuration such as the following to ensure specific packages are installed automatically when cloud-init runs:
That’s it! Save the file, and exit. We have another file that we need to edit, and this one won’t actually exist yet.
sudo nano /etc/cloud/cloud.cfg.d/99-fake_cloud.cfg
The contents of this file should match the following:
configure cloud-init for NoCloud
datasource_list: [ NoCloud, None ]
Save the file, and exit.
Before we run cloud-init, let’s clean it to essentially “reset” it:
sudo cloud-init clean
We can run
cloud-init now, but before we do, there’s one more customization that I recommend you consider adding. We can have cloud-init automate setting your server’s hostname as well. To do that, let’s open the
cloud.cfg file again:
sudo nano /etc/cloud/cloud.cfg
And add the following configuration underneath
Feel free to change the hostname to whatever you want it to be. Finally, save the file, that should be it when it comes to config files.
On my end, there’s a symbolic link that (for some reason) prevents
cloud-init from running if it’s present. I’m not sure yet why this file is present, but from what I’ve seen from Googling, the file will need to be removed or otherwise it can potentially stop
cloud-init from working:
sudo rm /etc/systemd/network/99-default.link
That should be it! At this point, we can reboot the server, and
cloud-init should run. But rather than reboot it, we can use the following command to test it in-place so we can make sure that the config actually works:
sudo cloud-init init
If all goes well, then you should be able to clean
cloud-init again, and then capture an image of your server to use as a distribution image for future deployments.