MAAS provides a state-of-the-art User Interface (UI), which simplifies usage. But you may not know that MAAS also has a robust Command-line Interface (CLI), which actually provides more functionality than the UI.  Everything you can do from the UI, you can do from the CLI, but not the other way round. Let’s walk through MAAS operations using only the CLI, and look at a few jq tricks to produce human-readable CLI output.

Quick questions you may have:

  • How do I install MAAS from the command-line only?
    • How do I initialise MAAS for production (CLI-only)?
  • How do I configure MAAS (CLI-only)?
    • How do I log into the CLI?
    • How do I get help via the CLI?
    • How do I set the MAAS DNS server from the CLI?
    • How can I import images via the CLI?

Installing MAAS

First, installation: note that our MAAS host is named “wintermute,” so you can ignore any references in the text that follows. Let’s be naive and start cold with MAAS 2.8. Step one is to install (but not initialise) the MAAS snap:

stormrider@wintermute:~$ sudo snap install maas --channel=2.8
maas (2.8/stable) 2.8.1-8567-g.c4825ca06 from Canonical installed

Looking over the MAAS initialisation modes, it looks like region+rack mode will do just fine for this install. There’s no need for the complexity of separate rack controllers just yet. First, though, there’s a decision about whether to use the POC mode (with a test DB) or just install to full-up production mode. The latter seems like the most involved, so let’s go with production mode.

Production PostgreSQL

Running MAAS in production mode means a local PostgreSQL install, from packages. Like all package installs, this process begins with a quick package update:

stormrider@wintermute:~$ sudo apt update -y
...list of updates follows...

This will grab any packages that might be needed for the install to succeed. Install the latest PostgreSQL package, which happens to be version 12 at this writing:

stormrider@wintermute:~$ sudo apt install -y postgresql
...install messages follow...

Set up a PostgreSQL user and a suitable MAAS database, needed to configure what follows:

stormrider@wintermute:~$ sudo -u postgres psql -c "CREATE USER "maascli" WITH ENCRYPTED PASSWORD 'maascli'"
stormrider@wintermute:~$ sudo -u postgres createdb -O "maascli" "maasclidb"

Note that there’s no system response to the database creation command — the old UNIX rule of “no news is good news.” Not to worry, more than likely you would see an error message if something didn’t work.

Next, we need to add the new database to the PostgreSQL HBA configuration, by editing /etc/postgres/12/main/pg_hbq.conf, adding a line to the bottom of the file for the new maasdatabase:

host maasclidb maascli 0/0 md5

Finally, we can initialise MAAS, like this:

stormrider@wintermute:~$ sudo maas init region+rack --database-uri "postgres://maascli:maascli@localhost/maasclidb"
MAAS URL [default=]: ...MAAS setup notifications...

There’s an important bit of feedback there, the MAAS URL, which will be needed for the CLI login. That’s followed by a running commentary on the steps MAAS is taking to initialise, ending with 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

Obviously, the next step is an easy call: run createadminto set up an admin user:

stormrider@wintermute:~$ sudo maas createadmin
[sudo] password for stormrider:
Username: admin
Email: <anything can go here, it's not used>
Import SSH keys [] (lp:user-id or gh:user-id): xxxxxxxxxxxx

This makes for an easy install of production MAAS, all via command line (since the installation is always at the CLI).

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: COMMAND login Log in to a remote API, and remember its description and credentials. logout Log out of a remote API, purging any stored credentials. 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. changepassword 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 'admin'. 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: COMMAND 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. boot-source-selection Manage a boot source selection. boot-source-selections Manage the collection of boot source selections. boot-sources Manage the collection of boot sources. commissioning-script Manage a custom commissioning script. commissioning-scripts 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. dnsresource-records 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. 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. package-repositories 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 nodes. 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 detailed 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 Success.
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="*" Success.
Machine-readable output follows:
{ "os": "ubuntu", "release": "trusty", "arches": [ "amd64" ], "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.