Your submission was sent successfully! Close

You have successfully unsubscribed! Close

Thank you for signing up for our newsletter!
In these regular emails you will find the latest updates about Ubuntu and upcoming events where you can meet our team.Close

CLI-only MAAS operation

Bill Wear

on 30 November 2020

This article was last updated 2 years ago.

MAAS provides a state-of-the-art User Interface (UI), which is relatively simple to use, if the required inputs are known and understood. You may be less familiar with the MAAS Command Line Interface (CLI), which is actually more robust, providing additional functionality that’s not accessible via the web UI. In this series of blog posts, we’re going to explore what it means to operate MAAS solely from the CLI.

There are two primary reasons why the CLI might be favoured:

  1. Some operators are acclimated to the command-line, and less comfortable with GUI environments.
  2. There are more than a few situations where it may be essential to script MAAS operations, for automation, efficiency, repeatability, or scalability reasons.

We’re going to spend several posts looking at this mode, not only because of the depth of the topic, but also because different readers may want to focus more tightly on specific aspects of the CLI. Even so, some of these posts will be longer and denser than your average blog — but we also know that MAAS users tend to be very detail-focused, so the extra information should be worth the read.

Installing and configuring MAAS

In this first post, we’ll explain how to install and configure MAAS using only the CLI interface. Of course, installation is not typically a GUI-driven activity, but configuration has a fairly straightforward UI component. We’ll also try to add value by suggesting scripting techniques to install MAAS, including conditional methods with wait-states, extra logging, and error-handling. If you plan to bring up multiple instances of MAAS, you’ll have the outline of a canned procedure; and if you prefer to standardise your installs in case of resets, you’ll have the beginnings of a standardised script.

Conventional language

Let’s start by pre-empting any confusion about what appears here, by establishing the following four conventions:

  1. The host being used in this example is called “wintermute.” Most of the time, the command prompt will be shown, indicating this hostname, so that there won’t be any confusion of commands with responses.
  2. Responses will show static (final) messages only. Sometimes, the MAAS commands issued will show intermediate progress bars or messages that eventually disappear when the command finishes. These temporary messages won’t be reproduced here.
  3. MAAS CLI commands require some inputs specific to your local installation, such as the administrative username ($PROFILE) or a machine’s system ID ($SYSTEM_ID). When you see these replaceable parameters ($PARAM_NAME), you’ll need to substitute the correct values for your particular situation. We’ll explain each new variable as we introduce it.
  4. We’ll be working with the production configuration, using a local PostgreSQL database, rather than the MAAS test configuration. We’ll also be working with a snap install, rather than Debian packages, and we’ll initialise here in rack+region mode. All of the steps shown here were executed and validated with that setup, so no promises on whether they will look exactly the same with other configurations, though there’s no reason why they wouldn’t work.

Installing MAAS

To install MAAS from the snap, execute this command:

sudo snap install --channel=2.9/stable maas

After entering this command, the system will display a number of messages detailing the installation process. When the installation is through, these messages will disappear, and you’ll see a message similar to this one:

maas (2.9/stable) 2.9.0-nnnn-g.cnnnnaann from Canonical installed

This will install MAAS, but not initialise it, as described in the relevant documentation. Note that the build string (“2.9.0-nnnn…”) will vary depending upon when you install MAAS.

Considering the MAAS initialisation modes, we’ll assume that region+rack mode will do just fine for this install. We’ll deal with the complexity of separate/multiple rack controllers later. First, though, we need to set up a production PostgreSQL install.

Production PostgreSQL

In production mode, MAAS uses a local PostgreSQL install (from Debian packages, in this case). The first step is to run the update command:

sudo apt update -y

This command will be followed by a stream of update messages, eventually returning you to the command line — with no message, if all goes well (again, no news is good news in Ubuntu).

This update command updates the package list: your copy of Ubuntu has a private copy of packages, which may be stale. The update command updates the package lists, so that you will get the most current versions of anything that is out of date. There is no danger in using it, so there’s no reason not to do it first, every time you install something new.

Note that the -y option is the short form of --assume-yes, which means “answer all prompts as yes.” This allows updates and installs to run in non-interactive mode. If some unresolvable situation occurs, the process will automatically abort.

Install PostgreSQL

You can install PostgreSQL with the command:

sudo apt install -y postgreql

which will produce a number of install messages, and again return a shell prompt if all goes well. Don’t be too surprised if PostgreSQL is already installed locally on your system; if so, you can just move on to the next step, which is setting up a PostgreSQL role (user).

Create a MAAS PostgreSQL user

For PostgreSQL, a ROLE is essentially a user, thought the PostgreSQL documentation doesn’t recommend that mapping. To create a unique role for your MAAS installation, execute a command like this one:

sudo -u postgres psql -c \

where you’ll substitute your MAAS DB username for $MAAS_DB_OWNER, and your desired DB password for $MAAS_PASSWD. If the command is successfully executed, it should return the following message:


The “encrypted password” phrase means that PostgreSQL will encrypt the password; you type your desired password in plain text.

Create the MAAS database

The next step in MAAS installation is to create the MAAS database, with a command of the form:

sudo -u postgres createdb \

The -O is the postgres option for “owner name” ($MAAS_DB_OWNER), and the second parameter, $MAAS_DB_NAME, is the name you want to give your MAAS database. Remember to substitute your own values for these replaceable parameters.

As usual, the command won’t return any message if it’s successful, but you can confirm your results another way. You can execute the following commands:

sudo -u postgres psql
psql (NN.M (Ubuntu....))
Type "help" for help.

postgres=# \l

You should see a table something like this:

                                 List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
 maas      | maas     | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 maas2     | maas2    | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(5 rows)

Check for your $MAAS_DB_NAME in the “Name” column, and your $MAAS_DB_OWNER in the “Owner” column. You can exit the psql shell with \q.

Create a PostgreSQL Host-Based Authentication (HBA) record

Finally, you’ll want to add your $MAAS_DB_NAME and $MAAS_DB_OWNER the PostgreSQL Host-Based Authentication file, pg_hba.conf. It’s typically found at /etc/postgres/VV/main/pg_hba.conf and you’ll add a line like this to bottom of it:

host    $MAAS_DB_NAME      $MAAS_DB_OWNER    0/0       md5

The HBA file is the client authentication file for PostgreSQL. Clients not listed here cannot authenticate, even with a password. The fields can be separated with spaces or tabs, and there’s no particular need to line up columns — though it is common courtesy to yourself and others to line up the columns, avoiding confusion when reviewing the file later.

The fields here are “host, database, user, address (0/0 matches all IPv4/6 addresses), and the authentication method (md5 in this case, meaning that the password is encrypted and not sent in clear-text). Now that you’ve got the MAAS database set up and accessible, we can finally initialise MAAS.

Initialise MAAS

MAAS has several initialisation options, but for this example, we’ll stick with region+rack, as discussed earlier. The initialisation command is:

sudo maas init region+rack --database-uri "postgres://$MAAS_DB_OWNER:$MAAS_DB_PASSWD@$HOSTNAME/$MAAS_DB_NAME"

You should use localhost for the $HOSTNAME if you’re running PostgreSQL on the same box as MAAS. Also, the database-uri follows the general rule about Universal Resources:

  1. URI is generic, including everything in the address as optional.
  2. URL includes a specific location and an intended protocol (e.g., https://).
  3. a URN is just a unique name for the resource.

There’s some important feedback from this command: it will offer a default MAAS URL, which you can change if desired (though it’s not necessary):

MAAS URL [default="]:

You’re usually safe to accept the default. Finalising this choice leads to the following message:

MAAS has been set up.             

If you want to configure external authentication or use
MAAS with Canonical RBAC, please run

sudo maas configauth

To create admins when not using external authentication, run

sudo maas createadmin

Creating an administrative user

The final step of initialising MAAS is to create an administrative user, with the command:

sudo maas createadmin

which takes you through the following exchange:

Create first admin account:
Username: $ADMIN_USER
Import SSH keys [] (lp:user-id or gh:user-id): $SSH_USER

Note that the $ADMIN_EMAIL doesn’t matter, and the $SSH_USER would be a user on Launchpad (lp) or Github (gh) for which you already have an SSH key set up. If you don’t have either of those, there are methods to import a key later.

Configuring MAAS (CLI-only)

Now that MAAS is up and running, it’s time to configure it. You can see documentation on these steps in the CLI configuration journey, part of the new RAD documentation set. Since we’re covering the full range of CLI operations, we’ll go ahead and recap the journey here.

Logging in

The first step for any new CLI operations is logging in, which requires two steps in the CLI. First, we need to get the MAAS apikey, which permits the CLI to access the MAAS API. Note that the MAAS API is actually the entry point for all MAAS actions through all access methods.

Here’s how we can retrieve and store the MAAS apikey:

sudo maas apikey --username=admin > api-key-file

You can make sure you got a valid API key by displaying the contents of api-key-file:

stormrider@wintermute:~$: cat api-key-file

Note that the string above isn’t an actual API key, just characters that were made up for this example. Anyway, we can now login to MAAS — but first, let’s try maas --help — there’s an important distinction that gets skipped over, causing some grief.

Getting help

In the MAAS CLI, you always get help by typing some variant of the basic command:

stormrider@wintermute:~$ maas --help

If you’re not logged in, or if you don’t type a “logged-in username” (referred to as a valid profile) after maas, you get the following, very generic help output:

usage: maas [-h] COMMAND ...

optional arguments:
  -h, --help      show this help message and exit

drill down:
    login         Log in to a remote API, and remember its 
                  description and credentials.
    logout        Log out of a remote API, purging any stored 
    list          List remote APIs that have been logged-in to.
    refresh       Refresh the API descriptions of all profiles.
    init          Initialise MAAS in the specified run mode.
    config        View or change controller configuration.
    status        Status of controller services.
    migrate       Perform migrations on connected database.
    apikey        Used to manage a user's API keys. Shows 
                  existing keys unless --generate or --delete 
                  is passed.
    configauth    Configure external authentication.
    createadmin   Create a MAAS administrator account.
                  Change a MAAS user's password.

What you see above isn’t even half of what the MAAS CLI will do, but it’s all you get as an unrecognized user.

So now, let’s login and try that help again:

stormrider@wintermute:~$ maas login admin \ < api-key-file

You are now logged in to the MAAS server at with the profile name

For help with the available commands, try:

  maas admin --help

Having logged in, you get much more detailed help:

stormrider@wintermute:~$ maas admin --help

usage: maas admin [-h] COMMAND ...

Issue commands to the MAAS region controller at

optional arguments:
  -h, --help            show this help message and exit

drill down:
    account             Manage the current logged-in user.
    bcache-cache-set    Manage bcache cache set on a machine.
    bcache-cache-sets   Manage bcache cache sets on a machine.
    bcache              Manage bcache device on a machine.
    bcaches             Manage bcache devices on a machine.
    block-device        Manage a block device on a machine.
    block-devices       Manage block devices on a machine.
    boot-resource       Manage a boot resource.
    boot-resources      Manage the boot resources.
    boot-source         Manage a boot source.
                        Manage a boot source selection.
                        Manage the collection of boot source 
    boot-sources        Manage the collection of boot sources.
                        Manage a custom commissioning script.
                        Manage custom commissioning scripts.
    dhcpsnippet         Manage an individual DHCP snippet.
    dhcpsnippets        Manage the collection of all DHCP 
                        snippets in MAAS.
    dnsresource         Manage dnsresource.
    dnsresource-record  Manage dnsresourcerecord.
                        Manage DNS resource records (e.g. CNAME, 
                        MX, NS, SRV, TXT)
    dnsresources        Manage dnsresources.
    device              Manage an individual device.
    devices             Manage the collection of all the devices 
                        in the MAAS.
    discoveries         Query observed discoveries.
    discovery           Read or delete an observed discovery.
    domain              Manage domain.
    domains             Manage domains.
    events              Retrieve filtered node events.
    fabric              Manage fabric.
    fabrics             Manage fabrics.
    fan-network         Manage Fan Network.
    fan-networks        Manage Fan Networks.
    file                Manage a FileStorage object.
    files               Manage the collection of all the files in 
                        this MAAS.
    ipaddresses         Manage IP addresses allocated by MAAS.
    iprange             Manage IP range.
    ipranges            Manage IP ranges.
    interface           Manage a node's or device's interface.
    interfaces          Manage interfaces on a node.
    license-key         Manage a license key.
    license-keys        Manage the license keys.
    maas                Manage the MAAS server.
    machine             Manage an individual machine.
    machines            Manage the collection of all the machines 
                        in the MAAS.
    network             Manage a network.
    networks            Manage the networks.
    node                Manage an individual Node.
    node-results        Read the collection of commissioning
                        script results.
    node-script         Manage or view a custom script.
    node-script-result  Manage node script results.
                        Manage node script results.
    node-scripts        Manage custom scripts.
    nodes               Manage the collection of all the nodes in
                        the MAAS.
    notification        Manage an individual notification.
    notifications       Manage the collection of all the 
                        notifications in MAAS.
                        Manage the collection of all Package 
                        Repositories in MAAS.
    package-repository  Manage an individual package repository.
    partition           Manage partition on a block device.
    partitions          Manage partitions on a block device.
    pod                 Manage an individual pod.
    pods                Manage the collection of all the pod in 
                        the MAAS.
    rack-controller     Manage an individual rack controller.
    rack-controllers    Manage the collection of all rack 
                        controllers in MAAS.
    raid                Manage a specific RAID (Redundant Array 
                        of Independent Disks) on a machine.
    raids               Manage all RAIDs (Redundant Array of 
                        Independent Disks) on a machine.
    region-controller   Manage an individual region controller.
    region-controllers  Manage the collection of all region 
                        controllers in MAAS.
    resource-pool       Manage a resource pool.
    resource-pools      Manage resource pools.
    sshkey              Manage an SSH key.
    sshkeys             Manage the collection of all the SSH keys 
                        in this MAAS.
    sslkey              Manage an SSL key.
    sslkeys             Operations on multiple keys.
    space               Manage space.
    spaces              Manage spaces.
    static-route        Manage static route.
    static-routes       Manage static routes.
    subnet              Manage subnet.
    subnets             Manage subnets.
    tag                 Tags are properties that can be 
                        associated with a Node and serve as 
                        criteria for selecting and allocating
    tags                Manage all tags known to MAAS.
    user                Manage a user account.
    users               Manage the user accounts of this MAAS.
    version             Information about this MAAS instance.
    vlan                Manage a VLAN on a fabric.
    vlans               Manage VLANs on a fabric.
    vm-host             Manage an individual vm-host.
    vm-hosts            Manage the collection of all the vm-hosts 
                        in the MAAS.
    vmfs-datastore      Manage VMFS datastore on a machine.
    vmfs-datastores     Manage VMFS datastores on a machine.
    volume-group        Manage volume group on a machine.
    volume-groups       Manage volume groups on a machine.
    zone                Manage a physical zone.
    zones               Manage physical zones.

This is a profile.  Any commands you issue on this profile will
operate on the MAAS region server.

The command information you see here comes from the region 
server's API; it may differ for different profiles.  If you 
believe the API may have changed, use the command's 'refresh' 
sub-command to fetch the latest version of this help information 
from the server.

You can see that the help is considerably more comprehensive when you log in and apply a profile name to the help request.

Setting DNS

The very first blank line you encounter in the MAAS UI is the DNS server IP address. In the UI, most people just type “” (Google’s DNS server) and forget about it. But the CLI has no box, so how do you get there? Well, setting MAAS DNS is part of the set-configcommands:

stormrider@wintermute:~$ maas admin maas set-config \
name=upstream_dns value=""
Machine-readable output follows:

The value=object does not have to be quoted, since it’s an IP address, which is continuous text without spaces — but it seems like a good habit to just type values in quotes.

Importing images

The next thing would be to import images. Looking at the dashboard, Ubuntu 18.04 has already been imported. We can bring in some other image (like Ubuntu 16.04 LTS) just to see how that works, and also confirm that the 18.04 (default) image is actually imported. We can check 18.04 with the following command:

stormrider@wintermute:~$ maas admin boot-resources read

Machine-readable output follows:
        "id": 7,
        "type": "Synced",
        "name": "grub-efi-signed/uefi",
        "architecture": "amd64/generic",
        "resource_uri": "/MAAS/api/2.0/boot-resources/7/"
        "id": 8,
        "type": "Synced",
        "name": "grub-efi/uefi",
        "architecture": "arm64/generic",
        "resource_uri": "/MAAS/api/2.0/boot-resources/8/"
        "id": 9,
        "type": "Synced",
        "name": "grub-ieee1275/open-firmware",
        "architecture": "ppc64el/generic",
        "resource_uri": "/MAAS/api/2.0/boot-resources/9/"
        "id": 10,
        "type": "Synced",
        "name": "pxelinux/pxe",
        "architecture": "i386/generic",
        "resource_uri": "/MAAS/api/2.0/boot-resources/10/"
        "id": 1,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/ga-18.04",
        "resource_uri": "/MAAS/api/2.0/boot-resources/1/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04"
        "id": 2,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/ga-18.04-lowlatency",
        "resource_uri": "/MAAS/api/2.0/boot-resources/2/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04"
        "id": 3,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/hwe-18.04",
        "resource_uri": "/MAAS/api/2.0/boot-resources/3/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04"
        "id": 4,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/hwe-18.04-edge",
        "resource_uri": "/MAAS/api/2.0/boot-resources/4/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04,hwe-18.10,hwe-19.04"
        "id": 5,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/hwe-18.04-lowlatency",
        "resource_uri": "/MAAS/api/2.0/boot-resources/5/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04"
        "id": 6,
        "type": "Synced",
        "name": "ubuntu/bionic",
        "architecture": "amd64/hwe-18.04-lowlatency-edge",
        "resource_uri": "/MAAS/api/2.0/boot-resources/6/",
        "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t,hwe-u,hwe-v,hwe-w,ga-16.04,ga-16.10,ga-17.04,ga-17.10,ga-18.04,hwe-18.10,hwe-19.04"

That’s a lot of information, but it looks like several 18.04 images downloaded and synched. You can use grep to simplify that output:

stormrider@wintermute:~$ maas admin boot-resources read \
| grep architecture 

 "architecture": "amd64/generic",
 "architecture": "arm64/generic",
 "architecture": "ppc64el/generic",
 "architecture": "i386/generic",
 "architecture": "amd64/ga-18.04",
 "architecture": "amd64/ga-18.04-lowlatency",
 "architecture": "amd64/hwe-18.04",
 "architecture": "amd64/hwe-18.04-edge",
 "architecture": "amd64/hwe-18.04-lowlatency",
 "architecture": "amd64/hwe-18.04-lowlatency-edge",

That definitely confirms 18.04. But what are those three or four on top? Looking at the massive JSON output, we can see that they have names like “open-firmware,” “uefi,” and “pxe.” Okay, so those are images that can PXE-boot machines, basically. But how could we sort this information out in a neat way?

enter jq

If you’re going to use the MAAS CLI — or anything with JSON-based output — you’ll want to consider learning the command line tool jq. It’s quite handy for parsing the JSON output of the MAAS CLI. So, for example, if we want a formatted table of names and architectures, we can run the last command through jq like this:

stormrider@wintermute:~$ maas admin boot-resources read \
| jq -r '.[] | "\(.name)\t\(.architecture)"'

grub-efi-signed/uefi        amd64/generic
grub-efi/uefi            arm64/generic
grub-ieee1275/open-firmware    ppc64el/generic
pxelinux/pxe            i386/generic
ubuntu/bionic            amd64/ga-18.04
ubuntu/bionic            amd64/ga-18.04-lowlatency
ubuntu/bionic            amd64/hwe-18.04
ubuntu/bionic            amd64/hwe-18.04-edge
ubuntu/bionic            amd64/hwe-18.04-lowlatency
ubuntu/bionic            amd64/hwe-18.04-lowlatency-edge

So you can see that we basically have (a) the images we need to boot machines, and (b) an 18.04 image (set) to deploy. That’s a good start, but let’s see if we can pull down another image with the CLI. We can select images with the boot-source-selections command, so let’s try that with “Trusty” (Xenial Xerus, aka 16.04):

stormrider@wintermute:~$ maas admin boot-source-selections \
create 1 os="ubuntu" release="trusty" arches="amd64" \
subarches="*" labels="*"

Machine-readable output follows:
    "os": "ubuntu",
    "release": "trusty",
    "arches": [
    "subarches": [
    "labels": [
    "boot_source_id": 1,
    "id": 2,
    "resource_uri": "/MAAS/api/2.0/boot-sources/1/selections/2/"

You repeat the maas admin boot-resources read command above to confirm that you’ve captured the 16.04 versions. Importing them is now a fairly simple command:

stormrider@wintermute:~$ maas admin boot-resources import
Machine-readable output follows:
Import of boot resources started

This blog post is fairly long, so let’s pause here and continue the MAAS CLI operations process in the next post.

Ubuntu cloud

Ubuntu offers all the training, software infrastructure, tools, services and support you need for your public and private clouds.

Newsletter signup

Get the latest Ubuntu news and updates in your inbox.

By submitting this form, I confirm that I have read and agree to Canonical's Privacy Policy.

Related posts

A call for community

Introduction Open source projects are a testament to the possibilities of collective action. From small libraries to large-scale systems, these projects rely...

MAAS Outside the Lines

Far from the humdrum of server setups, this is about unusual deployments – Raspberry Pis, loose laptops, cheap NUCs, home appliances, and more. What the heck...

No more DHCP(d)

“He’s dead, Jim.”  Dr. McCoy DHCP is dead; long live DHCP. Yes, the end-of-life announcement for ISC DHCP means that the ISC will no longer provide official...