This post is about Multipass Virtual Machines by using Ansible
Prerequisites
A Multipass virtual machine should be created according to instructions in the article Multipass virtual machine and authenticating using a private key . Note where the file containing the private key is stored.
The second prerequisite is Ansible. Instructions on how to install Ansible can be found in the official Ansible documentation.
Preparations
Preparations for the example of this article are as follows:
- Create a directory to hold the files of the example in this article.
I’ll call my directory “AnsibleMultipass”. - Copy the private key file, named “user_key”, to the new directory.
This key-file were created as part of creating the Multipass VM in the previous article. - Start the Multipass VM that has key-based authentication.
- Find the IP address of the Multipass VM with key-based authentication.
This can be accomplished using, for instance, the multipass list command in a terminal window. In my case, the IP is 192.168.64.20.
Ansible Inventory
An Ansible inventory tells Ansible how to connect to one or more nodes in the infrastructure. So, the example of this article has a very small infrastructure which consists of just one single virtual machine.
In the AnsibleMultipass directory, create a file named “multipass-vm-inventory.yml” with the following contents:
all:
hosts:
multipassvm1:
ansible_connection: ssh
ansible_host: "192.168.64.20"
ansible_user: vmadmin
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPath=none"
ansible_ssh_private_key_file: user_key
Note that:
- There is one host in this Ansible inventory file – multipassvm1.
- The type of connection to the host is specified by the ansible_connection value, which is SSH.
- The IP address of the host, or the DNS name if there is one, is specified by the ansible_host value.
- The IP address must be replaced with the IP address of the local Multipass VM.
- The value of ansible_user, vmadmin, is the name of the user which will be used when connecting to the host.
- This value, vmadmin in this example, must be the user-name used when the keys were generated with the ssh-keygen command.
- The ansible_ssh_common_args specify common arguments that are appended to sftp, scp and ssh commands.
- The StrictHostKeyChecking=no will turn off prompting whether new keys are trusted or not. Instead any new keys will silently be added to the known_hosts file.
- The ControlMaster and ControlPath settings are a workaround for a problem on my computer that caused by the default ControlPath containing a space that was not escaped. These may not be necessary on other computers and can be omitted.
- Finally, the ansible_ssh_private_key_file value specifies the name, and optionally location, of the private key-file that will be used when connecting to the host.
- If just a file-name is specified, the location of the key-file is relative to the inventory file. Can be an absolute location.
Ansible Modules
With the Ansible inventory in place, we can now run Ansible modules against the host(s) in the inventory. So, in this article I am only going to give a couple of examples. There is a list of all available Ansible modules in the documentation.
Ping Module
One of the Ansbile module is the ping module. As stated by its documentation, the ping module tries to connect to a host and verify a usable Python installation.
In a terminal window, located in the AnsibleMultipass directory, issue the following command:
ansible -i multipass-vm-inventory.yml multipassvm1 -m ping
The above command has the following parameters:
- -i multipass-vm-inventory.yml
Use a custom inventory file named multipass-vm-inventory.yml - multipassvm1
Name of the host which to run the module on. Must be a host in the inventory used. - -m ping
Specifies the name of the module to execute – ping in this case.
The resulting output should look similar to this:
multipassvm1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
As can be seen, connection was successfully established with the Multipass VM and a Python interpreter was discovered.
So, If there are any problems running the ping module against the Multipass VM host, add -vvv to the Ansible command for more verbose output:
ansible -vvv -i multipass-vm-inventory.yml multipassvm1 -m ping
With the execution successful, the above will generate output similar to the following (some output has been omitted):
ansible 2.10.3
...
Parsed /Users/ivan/AnsibleMultipass/multipass-vm-inventory.yml inventory source with yaml plugin
...
<192.168.64.20> ESTABLISH SSH CONNECTION FOR USER: vmadmin
<192.168.64.20> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="user_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vmadmin"' -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPath=none 192.168.64.20 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /home/vmadmin/.ansible/tmp `"&& mkdir "` echo /home/vmadmin/.ansible/tmp/ansible-tmp-1621106467.2205691-7451-138900743300503 `" && echo ansible-tmp-1621106467.2205691-7451-138900743300503="` echo /home/vmadmin/.ansible/tmp/ansible-tmp-1621106467.2205691-7451-138900743300503 `" ) && sleep 0'"'"''
<192.168.64.20> (0, b'ansible-tmp-1621106467.2205691-7451-138900743300503=/home/vmadmin/.ansible/tmp/ansible-tmp-1621106467.2205691-7451-138900743300503\n', b'')
<multipassvm1> Attempting python interpreter discovery
<192.168.64.20> ESTABLISH SSH CONNECTION FOR USER: vmadmin
<192.168.64.20> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="user_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vmadmin"' -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPath=none 192.168.64.20 '/bin/sh -c '"'"'echo PLATFORM; uname; echo FOUND; command -v '"'"'"'"'"'"'"'"'/usr/bin/python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.5'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/libexec/platform-python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/bin/python3'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python'"'"'"'"'"'"'"'"'; echo ENDFOUND && sleep 0'"'"''
<192.168.64.20> (0, b'PLATFORM\nLinux\nFOUND\n/usr/bin/python3\nENDFOUND\n', b'')
<192.168.64.20> ESTABLISH SSH CONNECTION FOR USER: vmadmin
<192.168.64.20> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="user_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vmadmin"' -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPath=none 192.168.64.20 '/bin/sh -c '"'"'/usr/bin/python3 && sleep 0'"'"''
<192.168.64.20> (0, b'{"platform_dist_result": [], "osrelease_content": "NAME=\\"Ubuntu\\"\\nVERSION=\\"20.04.2 LTS (Focal Fossa)\\"\\nID=ubuntu\\nID_LIKE=debian\\nPRETTY_NAME=\\"Ubuntu 20.04.2 LTS\\"\\nVERSION_ID=\\"20.04\\"\\nHOME_URL=\\"https://www.ubuntu.com/\\"\\nSUPPORT_URL=\\"https://help.ubuntu.com/\\"\\nBUG_REPORT_URL=\\"https://bugs.launchpad.net/ubuntu/\\"\\nPRIVACY_POLICY_URL=\\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\\"\\nVERSION_CODENAME=focal\\nUBUNTU_CODENAME=focal\\n"}\n', b'')
...
multipassvm1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"invocation": {
"module_args": {
"data": "pong"
}
},
"ping": "pong"
}
META: ran handlers
The details of the process of reading the inventory configuration, connecting to the host using SSH, verifying a Python installation and obtaining information on the host’s operating system can be discerned.
Setup Module
The second and last Ansible module that I will try in this article is the setup module, which gather information about hosts. To gather information about the Multipass VM host, issue the following command in the terminal window:
ansible -i multipass-vm-inventory.yml multipassvm1 -m setup
There will be a lot of information output to the console. Fortunately it is possible to select a subset of the information by applying a filter as in this example:
ansible -i multipass-vm-inventory.yml multipassvm1 -m setup -a "filter=ansible_env*"
The above gathers information about the multipassvm1 and retain only the information which have keys starting with ansible_env. On my system, the output looks like this:
multipassvm1 | SUCCESS => {
"ansible_facts": {
"ansible_env": {
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
"HOME": "/home/vmadmin",
"LANG": "C.UTF-8",
"LC_CTYPE": "C.UTF-8",
"LOGNAME": "vmadmin",
"MOTD_SHOWN": "pam",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
"PWD": "/home/vmadmin",
"SHELL": "/bin/sh",
"SSH_CLIENT": "192.168.64.1 49301 22",
"SSH_CONNECTION": "192.168.64.1 49301 192.168.64.20 22",
"SSH_TTY": "/dev/pts/0",
"TERM": "xterm-256color",
"USER": "vmadmin",
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user",
"XDG_SESSION_ID": "8",
"XDG_SESSION_TYPE": "tty"
},
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false
}
The above are the environment variables set for the vmadmin user when being logged into the Multipass VM.
Being able to create an Ansible inventory that contain Multipass VM hosts allows us to use Ansible with this kind of virtual machines. We can thus create Ansible playbooks to set up and configure Multipass virtual machines, something I will make use of in future article(s).