Network Engineers, System Administrators, and DevOps engineers have to use the various open source software to do their day to day jobs. This could be things like using Ansible for server or network configuration, it could be Terraform to deploy and maintain infrastructure, Git for version control, or CLIs provided by cloud infrastructure providers. Even with small teams, one of the challenges that I have seen is how to make sure that the systems can be easily setup on day 1.

I had touched base on cloud-init briefly in an earlier post, but that relied on downloading a bash script and executing it. This blog post covers details on the commonly used software and how to build a Linux based system automatically. It assumes that the system is being built on OpenStack or any other cloud provider infrastructure such as AWS, GCP, Azure, or Digital Ocean and uses Ubuntu 16.04 as the operating system.


  • There are other ways such as using Vagrant and Docker containers for this.
  • This solution will not scale well for larger teams as security, etc. is not accounted for.
  • Automatic updates are not captured in this blog post.

cloud-init configuration used in this blog post is available on GitHub. It has been tested on the following cloud providers and Ubuntu versions.

Digital Ocean – Ubuntu 16.04
Digital Ocean – Ubuntu 18.04

This blog post is divided into two sections.

  1. Software components – provides an introduction to the software that will be installed.
  2. Cloud-init – We look at cloud-init and describe how it is used to configure the server after first boot.

Software components

  • Python – Python is a general-purpose programming language, which has become a widely adopted programming language of choice for automation. There are other languages such as Ruby that is not covered in this blog
  • python-pip – PIP is used to install Python modules and software components.
  • Terraform – Terraform provides an easy way to automate infrastructure setup.
  • Ansible – I personally like Ansible over other solutions such as Chef and Puppet because of it’s clientless architecture that relies on SSH to configure servers and network devices.
  • Git – Git is a version control system that can be either hosted locally or remotely and allows collaboration and software sync.
  • CLI from cloud providers
    • OpenStackClient – OpenStackClient provides CLI based access to manage the OpenStack infrastructure.
    • Azure CLI v2.0 – Used to manage resources on Azure.
    • Google Cloud SDK – Google Cloud SDK to manage resources on Google Cloud Platform.
    • Amazon Web Service CLI – Used to manage resources on Amazon Web Services.
    • doctl – Used to manage resources on Digital Ocean.


cloud-init website provides a neat single line description.

The standard for customizing cloud instances

cloud-init works with almost all modern Linux operating systems and allows an end user to specify different aspects of the server instance by using the user data file. cloud-init then reads the user data during bootup and configures the servers automatically. This process allows instances to be an identical clone of each other.

Head over to GitHub to download the cloud-init script. Please note that changes are required to the cloud-init script. The changes that are required, are highlighted in the description section below. Using cloud-init, we will be doing the following on our server.

  1. Setup a user that will be able to login to the server using ssh-keys.
  2. Perform software upgrade on the server.
  3. Installs the software from the software component list above.
  4. After installation has completed; post to a URL sending data about the instance. This could be used for inventory tracking, etc.

While I try to do my best in explaining the script, you can read the cloud-init docs that provide more details.

cloud-init runs as root so you don’t need to provide sudo in your commands.

User Setup

The first part of the script configures a user and setups ssh-keys for that user. You can add multiple users if required.

  - name: add-user-here
    groups: sudo
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
      - add-key-here

The block shown above, will configure a user, add it to the sudo group, assign the shell as bash, update the sudoers file such that the user won’t need to enter password whenever he/she types “sudo -s”, and adds the public key of the user into the .ssh/authorized_keys file to ensure that passwordless authentication is setup.

Package Upgrade

A single line will update the available packages list and will also upgrade the server.

package_upgrade: true

Software installation

Additional packages can be installed during first boot using the configuration block shown below.

  - python
  - python-pip
  - git
  - zip
  - tcpdump

As shown above, python, python-pip, git, zip, and tcpdump will automatically be installed. If you have requirements for other packages, add them to the list above.

To install the packages that are listed in the software components section, we will be using the runcmd feature that cloud-init provides. This allows us to use standard Linux commands to complete installation of the software.

The configuration shown in the block below will

  • upgrade pip
  • install
    • ansible
    • python-openstackclient
    • awscli
  - [ pip, install, -U, pip, ansible, python-openstackclient, awscli ]

The configuration below will download terraform 0.11.7 into /tmp directory and then unzip the contents of the directory to /usr/local/bin. I recommend checking for the latest terraform release.

  - [ curl, -o, /tmp/, "" ]
  - [ unzip, -d, /usr/local/bin/, /tmp/ ]

Installation of Digital Ocean CLI (doctl) is similar to that of Terraform.

  - [ curl, -L, -o, /tmp/doctl.tar.gz, "" ]
  - [ tar, -C, /usr/local/bin, -zxvf, /tmp/doctl.tar.gz ]

Installation of Google SDK, Azure CLI are same. To show the various features of cloud-init, the description below sets up the apt repo and installs both Google SDK and Azure CLI using apt.

  - CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
  - 'echo "deb $CLOUD_SDK_REPO main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list'
  - 'curl "" | sudo apt-key add -''
  - AZ_REPO=$(lsb_release -cs)
  - 'echo "deb [arch=amd64] $AZ_REPO main" | tee /etc/apt/sources.list.d/azure-cli.list'
  - 'apt-key adv --keyserver --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893'
  - 'apt-get update && apt-get -y install apt-transport-https azure-cli'

Phone home

Using the phone_home feature, cloud-init can be configured to send an HTTP POST message to a web server.

  post: all
  tries: 2

In the example above, we will send an HTTP POST to a Webhook Tester endpoint so that you can view what the HTTP POST from cloud-init looks. In the real world, you would send a POST message to your web server that could do additional post-processing.

I have this part of the cloud-init configuration commented out in the GitHub hosted file.


Below is sample output from the HTTP post. To make it easier for a reader’s eye, the output of the HTTP POST message has been trimmed.

{ “instance_id“: “91472478”, “hostname“: “ubuntu-s-1vcpu-1gb-nyc3-01”, “pub_key_dsa“: “ssh-dss+AAAAB3Nz<trim>Y%3D+root%40ubuntu-s-1vcpu-1gb-nyc3-01%0A”, “pub_key_ecdsa“: “ecdsa-sha2-nistp256+AAAAE2<trim>YQ%3D+root%40ubuntu-s-1vcpu-1gb-nyc3-01%0A”, “pub_key_rsa“: “ssh-rsa+AAAAB<trim>1h+root%40ubuntu-s-1vcpu-1gb-nyc3-01%0A”, “fqdn“: “ubuntu-s-1vcpu-1gb-nyc3-01\r\n” }