The benefits of the following "breaking change" are the following: - default behaviour naturally fits with most common usage (i.e. always connect with Vagrant SSH settings) - the autogenerated inventory is more consistent by providing both the SSH username and private key. - no longer needed to explain how to override Ansible `remote_user` parameters Important: With the `force_remote_user` option, people still can fall back to the former behavior (prior to Vagrant 1.8.0), which means that Vagrant integration capabilities are still quite open and flexible.
14 KiB
| page_title | sidebar_current |
|---|---|
| Ansible - Provisioning | provisioning-ansible |
Ansible Provisioner
Provisioner name: "ansible"
The ansible provisioner allows you to provision the guest using
Ansible playbooks by executing ansible-playbook from the Vagrant host.
Ansible playbooks are YAML documents that comprise the set of steps to be orchestrated on one or more machines. This documentation page will not go into how to use Ansible or how to write Ansible playbooks, since Ansible is a complete deployment and configuration management system that is beyond the scope of a single page of documentation.
Warning: If you're not familiar with Ansible and Vagrant already, I recommend starting with the shell provisioner. However, if you're comfortable with Vagrant already, Vagrant is a great way to learn Ansible.
Setup Requirements
- Install Ansible on your Vagrant host.
- Your Vagrant host should ideally provide a recent version of OpenSSH that supports ControlPersist
Inventory File
When using Ansible, it needs to know on which machines a given playbook should run. It does this by way of an inventory file which lists those machines. In the context of Vagrant, there are two ways to approach working with inventory files.
Auto-Generated Inventory
The first and simplest option is to not provide one to Vagrant at all. Vagrant will generate an
inventory file encompassing all of the virtual machines it manages, and use it for provisioning
machines. The generated inventory file is stored as part of your local Vagrant environment in .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory.
Groups of Hosts
The ansible.groups option can be used to pass a hash of group names and group members to be included in the generated inventory file.
With this configuration example:
ansible.groups = {
"group1" => ["machine1"],
"group2" => ["machine2"],
"all_groups:children" => ["group1", "group2"]
}
Vagrant would generate an inventory file that might look like:
# Generated by Vagrant
machine1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2200 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='/home/.../'.vagrant/machines/machine1/virtualbox/private_key
machine2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2201 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='/home/.../'.vagrant/machines/machine2/virtualbox/private_key
[group1]
machine1
[group2]
machine2
[all_groups:children]
group1
group2
Notes
- The generation of group variables blocks (e.g.
[group1:vars]) are intentionally not supported, as it is not recommended to store group variables in the main inventory file. A good practice is to store these group (or host) variables inYAMLfiles stored ingroup_vars/orhost_vars/directories in the playbook (or inventory) directory. - Unmanaged machines and undefined groups are not added to the inventory, to avoid useless Ansible errors (e.g. unreachable host or undefined child group)
- Prior to Vagrant 1.7.3, the
ansible_ssh_private_key_filevariable was not set in generated inventory, but passed as command line argument toansible-playbookcommand. - Prior to Vagrant 1.8.0, the
ansible_ssh_uservariable was not set in generated inventory, but passed as command line argument toansible-playbookcommand. See also theforce_remote_useroption to enable the former behavior.
For example, machine3, group3 and group1:vars in the example below would not be added to the generated inventory file:
ansible.groups = {
"group1" => ["machine1"],
"group2" => ["machine2", "machine3"],
"all_groups:children" => ["group1", "group2", "group3"],
"group1:vars" => { "variable1" => 9, "variable2" => "example" }
}
Static Inventory
The second option is for situations where you'd like to have more control over the inventory management.
With the ansible.inventory_path option, you can reference a specific inventory resource (e.g. a static inventory file, a dynamic inventory script or even multiple inventories stored in the same directory). Vagrant will then use this inventory information instead of generating it.
A very simple inventory file for use with Vagrant might look like:
default ansible_ssh_host=192.168.111.222
Where the above IP address is one set in your Vagrantfile:
config.vm.network :private_network, ip: "192.168.111.222"
Notes:
- The machine names in
Vagrantfileandansible.inventory_pathfiles should correspond, unless you useansible.limitoption to reference the correct machines. - The SSH host addresses (and ports) must obviously be specified twice, in
Vagrantfileandansible.inventory_pathfiles.
Playbook
The second component of a successful Ansible provisioner setup is the Ansible playbook which contains the steps that should be run on the guest. Ansible's playbook documentation goes into great detail on how to author playbooks, and there are a number of best practices that can be applied to use Ansible's powerful features effectively. A playbook that installs and starts (or restarts if it was updated) the NTP daemon via YUM looks like:
---
- hosts: all
tasks:
- name: ensure ntpd is at the latest version
yum: pkg=ntp state=latest
notify:
- restart ntpd
handlers:
- name: restart ntpd
service: name=ntpd state=restarted
You can of course target other operating systems that don't have YUM by changing the playbook tasks. Ansible ships with a number of modules that make running otherwise tedious tasks dead simple.
Running Ansible
To run Ansible against your Vagrant guest, the basic Vagrantfile configuration looks like:
Vagrant.configure("2") do |config|
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
end
end
Since an Ansible playbook can include many files, you may also collect the related files in a directory structure like this:
$ tree
.
|-- Vagrantfile
|-- provisioning
| |-- group_vars
| |-- all
| |-- playbook.yml
In such an arrangement, the ansible.playbook path should be adjusted accordingly:
Vagrant.configure("2") do |config|
config.vm.provision "ansible" do |ansible|
ansible.playbook = "provisioning/playbook.yml"
end
end
Vagrant will try to run the playbook.yml playbook against all machines defined in your Vagrantfile.
Backward Compatibility Note:
Up to Vagrant 1.4, the Ansible provisioner could potentially connect (multiple times) to all hosts from the inventory file.
This behaviour is still possible by setting ansible.limit = 'all' (see more details below).
Additional Options
The Ansible provisioner also includes a number of additional options that can be set,
all of which get passed to the ansible-playbook command that ships with Ansible.
-
ansible.extra_varscan be used to pass additional variables (with highest priority) to the playbook. This parameter can be a path to a JSON or YAML file, or a hash. For example:ansible.extra_vars = { ntp_server: "pool.ntp.org", nginx: { port: 8008, workers: 4 } }These variables take the highest precedence over any other variables.
-
ansible.sudocan be set totrueto cause Ansible to perform commands using sudo. -
ansible.sudo_usercan be set to a string containing a username on the guest who should be used by the sudo command. -
ansible.ask_sudo_passcan be set totrueto require Ansible to prompt for a sudo password. -
ansible.ask_vault_passcan be set totrueto require Ansible to prompt for a vault password. -
ansible.vault_password_filecan be set to a string containing the path of a file containing the password used by Ansible Vault. -
ansible.limitcan be set to a string or an array of machines or groups from the inventory file to further control which hosts are affected. Note that:- As of Vagrant 1.5, the machine name (taken from Vagrantfile) is set as default limit to ensure that
vagrant provisionsteps only affect the expected machine. Settingansible.limitwill override this default. - Setting
ansible.limit = 'all'can be used to make Ansible connect to all machines from the inventory file.
- As of Vagrant 1.5, the machine name (taken from Vagrantfile) is set as default limit to ensure that
-
ansible.verbosecan be set to increase Ansible's verbosity to obtain detailed logging:'v', verbose mode'vv''vvv', more'vvvv', connection debugging
-
ansible.tagscan be set to a string or an array of tags. Only plays, roles and tasks tagged with these values will be executed. -
ansible.skip_tagscan be set to a string or an array of tags. Only plays, roles and tasks that do not match these values will be executed. -
ansible.start_at_taskcan be set to a string corresponding to the task name where the playbook provision will start. -
ansible.raw_argumentscan be set to an array of strings corresponding to a list ofansible-playbookarguments (e.g.['--check', '-M /my/modules']). It is an unsafe wildcard that can be used to apply Ansible options that are not (yet) supported by this Vagrant provisioner. As of Vagrant 1.7,raw_argumentshas the highest priority and its values can potentially override or break other Vagrant settings. -
ansible.raw_ssh_argscan be set to an array of strings corresponding to a list of OpenSSH client parameters (e.g.['-o ControlMaster=no']). It is an unsafe wildcard that can be used to pass additional SSH settings to Ansible viaANSIBLE_SSH_ARGSenvironment variable. -
ansible.host_key_checkingcan be set totruewhich will enable host key checking. As of Vagrant 1.5, the default value isfalseand as of Vagrant 1.7 the user known host file (e.g.~/.ssh/known_hosts) is no longer read nor modified. In other words: by default, the Ansible provisioner behaves the same as Vagrant native commands (e.gvagrant ssh). -
ansible.force_remote_usercan be set tofalsewhich will enable theremote_userparameters of your Ansible plays or tasks. Otherwise, Vagrant will set theansible_ssh_usersetting in the generated inventory, or as an extra variable when a static inventory is used. In this case, all the Ansibleremote_userparameters will be overridden by the value ofconfig.ssh.usernameof the Vagrant SSH Settings.
Tips and Tricks
Ansible Parallel Execution
Vagrant is designed to provision multi-machine environments in sequence, but the following configuration pattern can be used to take advantage of Ansible parallelism:
# Vagrant 1.7+ automatically inserts a different
# insecure keypair for each new VM created. The easiest way
# to use the same keypair for all the machines is to disable
# this feature and rely on the legacy insecure key.
# config.ssh.insert_key = false
#
# Note:
# As of Vagrant 1.7.3, it is no longer necessary to disable
# the keypair creation when using the auto-generated inventory.
N = 3
(1..N).each do |machine_id|
config.vm.define "machine#{machine_id}" do |machine|
machine.vm.hostname = "machine#{machine_id}"
machine.vm.network "private_network", ip: "192.168.77.#{20+machine_id}"
# Only execute once the Ansible provisioner,
# when all the machines are up and ready.
if machine_id == N
machine.vm.provision :ansible do |ansible|
# Disable default limit to connect to all the machines
ansible.limit = 'all'
ansible.playbook = "playbook.yml"
end
end
end
end
Caveats:
If you apply this parallel provisioning pattern with a static Ansible inventory, you'll have to organize the things so that all the relevant private keys are provided to the ansible-playbook command. The same kind of considerations applies if you are using multiple private keys for a same machine (see config.ssh.private_key_path SSH setting).
Provide a local ansible.cfg file
Certain settings in Ansible are (only) adjustable via a configuration file, and you might want to ship such a file in your Vagrant project.
As ansible-playbook command looks for local ansible.cfg configuration file in its current directory (but not in the directory that contains the main playbook), you have to store this file adjacent to your Vagrantfile.
Note that it is also possible to reference an Ansible configuration file via ANSIBLE_CONFIG environment variable, if you want to be flexible about the location of this file.
Force Paramiko Connection Mode
The Ansible provisioner is implemented with native OpenSSH support in mind, and there is no official support for paramiko (A native Python SSHv2 protocol library).
If you really need to use this connection mode, it is though possible to enable paramiko as illustrated in the following configuration examples:
With auto-generated inventory:
ansible.raw_arguments = ["--connection=paramiko"]
With a custom inventory, the private key must be specified (e.g. via an ansible.cfg configuration file, --private-key argument, or as part of your inventory file):
ansible.inventory_path = "./my-inventory"
ansible.raw_arguments = [
"--connection=paramiko",
"--private-key=/home/.../.vagrant/machines/.../private_key"
]