Begin Vultr With Ansible
Ansible 2.5 is just around the corner, perfect timing to show some of the new features I was involved with and included in this release. This is the first part of a post series What’s new in Ansible 2.5.
vr_*. They were renamed to vultr_* in Ansible 2.8 and today live in the vultr.cloud collection, which targets the Vultr API v2. The approach below still applies, but the module names and some parameters have changed.What is Vultr?
Vultr is a Public Cloud Service (IaaS) with a manageable feature set and datacenters in around 15 locations around the globe including North America, Europe, Asia and Australia.
The features include, among others, classical virtual servers, bare metal servers, firewall management, block storage, a free DNS service, custom ISOs and predefined apps. An easy-to-use web interface allows you to configure all of these services manually but unsurprisingly Vultr also provides an API for programmatic access.
Ansible Vultr Modules
The modules related to Vultr included in 2.5 do not yet cover all APIs but some of the most interesting ones:
- vr_account_facts
- vr_dns_domain
- vr_dns_record
- vr_firewall_group
- vr_firewall_rule
- vr_server
- vr_ssh_key
- vr_startup_script
- vr_user
These modules allow you to deploy a server with your selected plan in a chosen region with an SSH key, configure the firewall group and rules and run start scripts on the server.
Quickstart
So let’s get started quickly by creating a new account (You support my works by using this affiliate link).
Configure API Authentication
The modules use the API and have to authenticate. So the first thing is to allow the modules to access the account using the API. You find the API key in your Account settings in the UI.
There are several ways to pass the API key to the modules. We won’t cover every option for now, just the most common way: create a .vultr.ini in your home directory just like the following:
[default]
key = <your api key>Run a test and get some info about your account:
$ ansible -m vr_account_facts localhostThe output we will get should look something like:
localhost | SUCCESS => {
"ansible_facts": {
"vultr_account_facts": {
"balance": -212.87,
"last_payment_amount": -250.0,
"last_payment_date": "2017-04-28 12:29:50",
"pending_charges": 0.06
}
},
"changed": false,
"vultr_account_facts": {
"balance": -212.87,
"last_payment_amount": -250.0,
"last_payment_date": "2017-04-28 12:29:50",
"pending_charges": 0.06
},
"vultr_api": {
"api_account": "default",
"api_endpoint": "https://api.vultr.com",
"api_retries": 5,
"api_timeout": 60
}
}But what just happened? We ran the module vr_account_facts locally, and the module itself connects to the API over HTTPS. There is one more cool thing: did you notice we didn’t have to install any dependencies other than Ansible? Pretty nice, isn’t it?
Define an Inventory
When authentication was successful, we can go to the next step, deploying our first server.
But first things first: create an inventory which contains the server we want to deploy. We use a classical INI style Ansible inventory named production.
One server, the webserver is called web-01 and we put it in the group cloud, this is just enough for the moment.
mkdir hosts
printf '[cloud]\nweb-01\n' > ./hosts/productionWhen we check the inventory with the command introduced in 2.4, ansible-inventory and pass the right arguments
ansible-inventory --inventory hosts/production --listWe should see a nice JSON view of our inventory:
{
"_meta": {
"hostvars": {
"web-01": {}
}
},
"all": {
"children": [
"cloud",
"ungrouped"
]
},
"cloud": {
"hosts": [
"web-01"
]
},
"ungrouped": {}
}So far, so good.
Your first playbook
As you may know, a playbook is usually a YAML document containing one or more plays with tasks for a target host or host group. That is just what we need:
mkdir playbooks
touch playbooks/cloud.ymlIn the first play, we are going to deploy our cloud server defined in our production inventory.
- hosts: cloud
gather_facts: no
tasks:
- name: Ensure a cloud server exists
local_action:
module: vr_server
name: "{{ inventory_hostname_short }}"
os: "CentOS 7 x64"
plan: "2048 MB RAM,40 GB SSD,2.00 TB BW"
region: New JerseyWe run the module as a local action; that is why we turned off fact gathering, as it would fail in case the server does not yet exist.
connection: local or delegate_to: localhost.The playbook is ready, let’s give it a shot in --check mode:
ansible-playbook playbooks/cloud.yml --inventory hosts/production --check --diff -v
No config file found; using defaults
PLAY [cloud] *******************************************************************
TASK [Ensure a cloud server exists] ********************************************
changed: [web-01 -> localhost] => {"changed": true, "vultr_api": {"api_account": "default", "api_endpoint": "https://api.vultr.com", "api_retries": 5, "api_timeout": 60}, "vultr_server": {}}
PLAY RECAP *********************************************************************
web-01 : ok=1 changed=1 unreachable=0 failed=0Before we continue without --check mode, I must note that Vultr is quite popular and, especially for datacenters located in Europe, plans may run out. If this happens, the module will fail but will give a decent error message:
fatal: [web-01 -> localhost]: FAILED! => {"changed": true, "msg": "URL https://api.vultr.com/v1/server/create, method POST with data OSID=167&DCID=9&VPSPLANID=201¬ify_activate=no&label=web-01. Returned 412, with body: HTTP Error 412: Request Failed Plan is not available in the selected datacenter. This could mean you have chosen the wrong plan (for example, a storage plan in a location that does not offer them), or the location you have selected does not have any more capacity.", "vultr_api": {"api_account": "default", "api_endpoint": "https://api.vultr.com", "api_retries": 5, "api_timeout": 60}, "vultr_server": {}}Use a Vultr CLI e.g. https://github.com/JamesClonk/vultr to get a list of available plans per region (DCID):
vultr plans -r <DCID>Okay, back to the playbook,
$ ansible-playbook playbooks/cloud.yml --inventory hosts/production --diff -v
No config file found; using defaults
PLAY [cloud] *******************************************************************
TASK [Ensure a cloud server exists] ********************************************
changed: [web-01 -> localhost] => {"changed": true, "vultr_api": {"api_account": "default", "api_endpoint": "https://api.vultr.com", "api_retries": 5, "api_timeout": 60}, "vultr_server": {"allowed_bandwidth_gb": 2000, "auto_backup_enabled": false, "cost_per_month": 10.0, "current_bandwidth_gb": 0, "date_created": "2018-03-18 12:54:29", "default_password": "<redacted>", "disk": "Virtual 40 GB", "firewall_group": null, "id": "14294916", "internal_ip": "", "kvm_url": "https://my.vultr.com/subs/vps/novnc/api.php?data=<redacted>", "name": "web-01", "os": "CentOS 7 x64", "pending_charges": 0.02, "plan": "2048 MB RAM,40 GB SSD,2.00 TB BW", "power_status": "running", "ram": "2048 MB", "region": "New Jersey", "server_state": "ok", "status": "active", "tag": "", "v4_gateway": "207.246.94.1", "v4_main_ip": "207.246.95.35", "v6_main_ip": "", "v6_network": "", "v6_network_size": "", "v6_networks": [], "vcpu_count": 1}}
PLAY RECAP *********************************************************************
web-01 : ok=1 changed=1 unreachable=0 failed=0That seems to have worked!
However you may feel like what a mess of output. This is because we also added -v to get a verbose output. You are right. I wanted to show, that the module does return a lot of data which can be used later when registered in a variable, e.g. the vultr_server.default_password to login by SSH or the vultr_server.v4_main_ip which can be used later for a DNS A-record.
Is this it? Well, we have a lot more things to cover but this is enough for today. Stay tuned for the next part of this series.