Ansible is profoundly different from other configuration management tools available today. It has been designed to make configuration easy in almost every way, from its simple English configuration syntax to its ease of setup. You'll find that Ansible allows you to stop writing custom configuration and deployment scripts and lets you simply get on with your job.
Ansible only needs to be installed on the machines that you use to manage your infrastructure. It does not need a client to be installed on the managed machine, nor does it need any server infrastructure to be set up before you can use it. You should even be able to use it merely minutes after it is installed, as we will show you in this chapter.
The following are the topics covered in this chapter:
Installing Ansible
Configuring Ansible
Using Ansible from the command line
Using Ansible to manage Windows machines
How to get help
You will be using Ansible from the command line on one machine, which we will call the controller machine, and use it to configure another machine, which we will call the managed machine. Ansible currently only supports a Linux or OS X controller machine; however, the managed machine can be Linux, OS X, other Unix-like machines or Windows. Ansible does not place many requirements on the controller machine and even less on the managed machine.
The requirements for the controller machine are as follows:
Python 2.6 or higher
paramiko
PyYAML
Jinja2
httplib2
Unix-based OS
The managed machine needs Python 2.4 or higher and simplejson; however, if your Python is 2.5 or higher, you only need Python. Managed Windows machines will need Windows remoting turned on, and a version of Windows PowerShell greater than 3.0. While Windows machines do have more requirements, all the tools are freely available and the Ansible project even includes the script to help you easily set up the dependencies.
If you want to use Ansible to manage a set of existing machines or infrastructure, you will likely want to use whatever package manager is included on those systems. This means that you will get updates for Ansible as your distribution updates it, which may lag several versions behind other methods. However, it means that you will be running a version that has been tested to work on the system you are using.
If you run an existing infrastructure, but need a newer version of Ansible, you can install Ansible via pip. Pip is a tool used to manage packages of Python software and libraries. Ansible releases are pushed to pip as soon as they are released, so if you are up to date with pip, you should always be running the latest version.
If you imagine yourself developing lots of modules and possibly contributing back to Ansible, you should be running a version installed from source code. As you will be running the latest and least-tested version of Ansible, you may experience a hiccup or two.
Most modern distributions include a package manager that automatically manages package dependencies and updates for you. This makes installing Ansible via your package manager by far the easiest way to get started with Ansible; usually it takes only a single command. It will also be updated as you update your machine, though it may be a version or two behind. The following are the commands to install Ansible on the most common distributions. If you are using something different, refer to the user guide of your package or your distribution's package lists:
Note
Note that RHEL and CentOS require the EPEL repository to be installed. Details on EPEL, including how to install it can be found at https://fedoraproject.org/wiki/EPEL.
If you are on Ubuntu and wish to use the latest release instead of the one provided by your operating system, you can use the Ubuntu PPA provided by Ansible. Details on setting this up can be found at https://launchpad.net/~ansible/+archive/ubuntu/ansible.
Pip, like a distribution's package manager, will handle finding, installing, and updating the packages you ask for and its dependencies. This makes installing Ansible via pip as easy as installing from your package manager. It should be noted, however, that it will not be updated with your operating system. Additionally, updating your operating system may break your Ansible installation; however, this is unlikely. If you are a Python user, you might want to install Ansible in an isolated environment (virtual environment): This is not supported as Ansible tries to install its modules to the system. You should install Ansible system-wide using pip.
The following is the command to install Ansible via pip:
$ pip install ansible
Installing from the source code is a great way to get the latest version, but it may not be tested as correctly as the released versions. You also will need to take care of updating to newer versions yourself and making sure that Ansible will continue to work with your operating system updates. To clone the git
repository and install it, run the following commands. You may need root access to your system to do this:
$ git clone git://github.com/ansible/ansible.git $ cd ansible $ sudo make install
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
Ansible needs to be able to get an inventory of the machines that you want to configure in order to manage them. This can be done in many ways due to inventory plug-ins. Several different inventory plug-ins are included with the base install. We will go over these later in the book. For now, we will cover the simple host's file inventory.
The default Ansible inventory file is named hosts and is placed at /etc/ansible
. It is formatted like an INI
file. Group names are enclosed in square braces, and everything underneath it, down to the next group heading, gets assigned to that group. Machines can be in many groups at one time. Groups are used to allow you to configure many machines at once. You can use a group instead of a hostname as a host pattern in later examples, and Ansible will run the module on the entire group at once.
In the following example, we have three machines in a group named webservers
, namely site01
, site02
, and site01-dr
. We also have a production
group that consists of site01
, site02
, db01
, and bastion
.
[webservers] site01 site02 site01-dr [production] site01 site02 db01 bastion
Once you have placed your hosts in the Ansible inventory, you can start running commands against them. Ansible includes a simple module called ping
that lets you test connectivity between yourself and the host. Let's use Ansible from the command line against one of our machines to confirm that we can configure them.
Ansible was designed to be simple, and one of the ways the developers have done this is by using SSH to connect to the managed machines. It then sends the code over the SSH connection and executes it. This means that you don't need to have Ansible installed on the managed machine. It also means that Ansible uses the same channels that you are already using to administer the machine. This makes is easier to setup, because in most cases there will be no setup required and no ports to open in a firewall.
First, we check connectivity to our server to be configured using the Ansible ping
module. This module simply connects to the following server:
$ ansible site01 -u root -k -m ping
This should ask for the SSH password and then produce a result that looks like the following:
site01 | success >> { "changed": false, "ping": "pong" }
If you have an SSH key set up for the remote system, you will be able to leave off the -k
argument to skip the prompt and use the keys. You can also configure Ansible to use a particular username all the time by either configuring it in the inventory on a per host basis or in the global Ansible configuration.
To set the username globally, edit /etc/ansible/ansible.cfg
and change the line that sets remote_user
in the [defaults]
section. You can also change remote_port
to change the default port that Ansible will SSH to. This will change the default settings for all the machines, but they can be overridden in the inventory file on a per server or per group basis.
To set the username in the inventory file, simply append ansible_ssh_user
to the line in the inventory. For example, the following code section shows an inventory where the site01
host uses the username root
and the site02
host uses the username daniel
. There are also other variables you can use. The ansible_ssh_host
variable allows you to set a different hostname, and the ansible_ssh_port
variable allows you to set a different port, which is demonstrated on the site01-dr
host. Finally the db01
host uses the username fred
and also sets a private key using ansible_ssh_private_key_file
.
[webservers] #1 site01 ansible_ssh_user=root #2 site02 ansible_ssh_user=daniel #3 site01-dr ansible_ssh_host=site01.dr ansible_ssh_port=65422 #4 [production] #5 site01 #6 site02 #7 db01 ansible_ssh_user=fred ansible_ssh_private_key_file=/home/fred/.ssh.id_rsa #8 bastion #9
If you aren't comfortable with giving Ansible direct access to the root account on the managed machines, or your machine does not allow SSH access to the root account (such as Ubuntu's default configuration), you can configure Ansible to obtain root access using sudo
. Using Ansible with sudo
means that you can enforce auditing the same way you would otherwise. Configuring Ansible to use sudo
is as simple as it is to configure the port, except that it requires sudo
to be configured on the managed machine.
The first step is to add a line to the /etc/sudoers
file; on the managed node, this may already be set up if you choose to use your own account. You can use a password with sudo
, or you can use a passwordless sudo
. If you decide to use a password, you will need to use the -k
argument to Ansible, or set the ask_sudo_pass
value to true
in /etc/ansible/ansible.cfg
. To make Ansible use sudo, add --sudo
to the command line like this:
ansible site01 -s -m command -a 'id -a'
If this works, it should return something similar to:
site01 | success | rc=0 >> uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Ansible recently added the ability to manage Windows machines. Now, you can use Ansible to easily manage Windows machines the same way you manage your Linux machines.
This uses the Windows PowerShell Remoting tools in the same way that SSH is used on a Linux machine to execute modules remotely. Several new modules have been added that explicitly support Windows, but some existing modules have also been given the ability to work with Windows-managed machines.
To get started with managing your Windows machine, you do have to perform a little bit of complex setup. You need to follow these steps:
Create some Windows machines in your inventory
Install Python-winrm to allow Ansible to connect to the Windows machines
Upgrade to PowerShell 3.0+ to support Windows modules
Enable Windows remoting so that Ansible can connect
Windows machines are created the same way as all the other machines that you have in your inventory. They are differentiated by the value of the ansible_connection
variable. When ansible_connection
is set to winrm
, it will try to connect via winrm to Windows PowerShell on the remote machine. Ansible also uses the ansible_ssh_user
, ansible_ssh_pass
, and ansible_ssh_port
values like it would on your other machine. Despite having the name ssh in them, they are used to provide the port and credentials that will be used to connect to the Windows PowerShell Remoting service. Here is what an example Windows machine might look like:
[windows] dc.ad.example.com web01.ad.example.com web02.ad.example.com [windows:vars] ansible_connection=winrm ansible_ssh_user=daniel ansible_ssh_pass=s3cr3t ansible_ssh_port=5986
For security reasons, you probably will not want to store the password in the inventory file. You can make Ansible prompt for the password the same way we showed previously for Unix systems by simply leaving off the ansible_ssh_user
and ansible_ssh_pass
variables and instead using the -k
and -u
arguments to Ansible if you wish. You might also choose to store them in an Ansible vault, which will be covered later in the book.
After you have created the inventory, you need to install the winrm Python library on the controller machine. This library will give Ansible the ability to connect to the Windows Remote Management service and configure a remote Windows system.
At the moment, this library is fairly experimental, and its connection to Ansible isn't quite perfect, so you have to install the particular version that matches the version of Ansible you are using. With the release of Ansible 1.8, this should sort things out a little bit. Most distributions do not have a packaged library yet, so you will probably want to install it via pip. As root, you need to run:
$ pip install https://github.com/diyan/pywinrm/archive/df049454a9309280866e0156805ccda12d71c93a.zip
However, for newer versions, you should simply be able to run:
pip install http://github.com/diyan/pywinrm/archive/master.zip
This will install the particular version of winrm that works with Ansible 1.7. For other newer versions of Ansible, you may need a different version, and eventually the winrm Python library should be packaged up by different distributions. Your machine will now be able to connect to and manage Windows machines with Ansible.
Next you have to perform a few setup steps on the machine you are going to manage. The first of these is to make sure that you have PowerShell 3.0 or later installed. You can check what version you have installed with the following command:
$PSVersionTable.PSVersion.Major
If the value you get back is not 3 or higher than 3, then you will need to upgrade your version of PowerShell. You can choose to do this manually by downloading and installing the latest Windows Management Framework for your system, or you can use a script provided by the Ansible project. To save space, we will be explaining the scripted installation here; the manual installation is left as an exercise for the reader.
Invoke-WebRequest https://raw.githubusercontent.com/ansible/ansible/release1.7.0/examples/scripts/upgrade_to_ps3.ps1 -OutFile upgrade_to_ps3.ps1 .\upgrade_to_ps3.ps1
The first command downloads the upgrade script from the Ansible project repository on GitHub and saves it to disk. The second command will detect your operating system to download the correct version of the Windows Management Framework and install it.
Next you need to configure the Windows Remote Management Service. The Ansible project provides a script that will configure Windows Remote Management automatically in the way that Ansible expects it to be configured. While you can set it up manually, it is highly recommended that you use this script instead to prevent misconfiguration. To download and run this script, open a PowerShell terminal and run the following commands:
Invoke-WebRequest https://raw.githubusercontent.com/ansible/ansible/release1.7.0/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile ConfigureRemotingForAnsible.ps1 .\ConfigureRemotingForAnsible.ps1
The first command downloads the configuration script from the Ansible project on GitHub, and the second command runs it. You should receive the output Ok
from the second script if everything worked correctly.
You should now be able to connect to your machine and configure it with Ansible. As we did earlier, let's run a ping command to confirm that Ansible is able to execute its modules remotely. While Unix machines can use the ping
module, Windows machines use the win_ping
module. The usage is almost exactly the same; however, as we've added the password to the inventory file, you don't need the -k
option.
$ ansible web01.ad.example.com -u daniel -m win_ping
If everything works correctly, you should see the following output:
web01.ad.example.com | success >> { "changed": false, "ping": "pong" }
The output indicates that Ansible was able to connect to the Windows Remote Management Service, login successfully, and execute a module on the remote host. If this works correctly, then you should be able to use all the other Windows modules to manage your machine.
Ansible modules take arguments in key-value pairs that look similar to key=value
, perform a job on the remote server, and return information about the job as JSON
. The key-value pairs allow the module to know what to do when requested. They can be hardcoded values, or in playbooks they can use variables, which will be covered in Chapter 2, Simple Playbooks. The data returned from the module lets Ansible know if anything changed in the managed host or if any information kept by Ansible should be changed afterwards.
Modules are usually run within playbooks, as this lets you chain many together, but they can also be used on the command line. Previously, we used the ping
command to check that Ansible had been correctly setup and was able to access the configured node. The ping
module only checks that the core of Ansible is able to run on the remote machine, but effectively does nothing.
A slightly more useful module is named setup
. This module connects to the configured node, gathers data about the system, and then returns those values. This isn't particularly handy for us while running from the command line. However, in a playbook, you can use the gathered values later in other modules.
To run Ansible from the command line, you need to pass two things, though usually three. First is a host pattern to match the machine that you want to apply the module to. Second you need to provide the name of the module that you wish to run and optionally any arguments that you wish to give to the module. For the host pattern, you can use a group name, a machine name, a glob, and a tilde (~), followed by a regular expression matching hostnames. Alternatively, to symbolize all of these, you can either use the word all
or simply *
. Running Ansible modules on the command line this way is referred to as an ad hoc Ansible command.
To run the setup
module on one of your nodes, you need the following command line:
$ ansible machinename -u root -k -m setup
The setup
module will then connect to the machine and give you a number of useful facts back. All the facts provided by the setup
module itself are prepended with ansible_
to differentiate them from variables.
This module will work on both Windows and Unix machines. Currently, Unix machines will give much more information than a Windows machine. However, as new versions of Ansible are released, you can expect to see more Windows functionality get included along with Ansible.
machinename | success >> { "ansible_facts": { "ansible_distribution": "Microsoft Windows NT 6.3.9600.0", "ansible_distribution_version": "6.3.9600.0", "ansible_fqdn": "ansibletest", "ansible_hostname": "ANSIBLETEST", "ansible_ip_addresses": [ "100.72.124.51", "fe80::1fd:fc3b:1eff:350d" ], "ansible_os_family": "Windows", "ansible_system": "Win32NT", "ansible_totalmem": "System.Object[]" }, "changed": false }
The following is a table of the most common values you will use; not all of these will be available on all machines. Windows machines especially return a lot less data from the setup module.
On a Unix machine, these variables are gathered using Python from the managed machine; if you have facter or ohai installed on the remote node, the setup
module will execute them and return their data as well. As with other facts, ohai facts are prepended with ohai_
and facter facts with facter_
. While the setup module doesn't appear to be too useful on the command line, it is useful once you start writing playbooks. Note that facter and ohai are not available in Windows hosts.
If all the modules in Ansible do as little as the setup
and the ping
module, we will not be able to change anything on the remote machine. Almost all of the other modules that Ansible provides, such as the file
module, allow us to actually configure the remote machine.
The file
module can be called with a single path argument; this will cause it to return information about the file in question. If you give it more arguments, it will try and alter the file's attributes and tell you if it has changed anything. Ansible modules will tell you if they have changed anything, which becomes more important when you are writing playbooks.
You can call the file
module, as shown in the following command, to see details about /etc/fstab
:
$ ansible machinename -u root -k -m file -a 'path=/etc/fstab'
The preceding command should elicit a response like the following:
machinename | success >> { "changed": false, "group": "root", "mode": "0644", "owner": "root", "path": "/etc/fstab", "size": 779, "state": "file" }
Alternatively, the response could be something like the following command to create a new test directory in /tmp
:
$ ansible machinename -u root -k -m file -a 'path=/tmp/teststate=directory mode=0700 owner=root'
The preceding command should return something like the following:
machinename | success >> { "changed": true, "group": "root", "mode": "0700", "owner": "root", "path": "/tmp/test", "size": 4096, "state": "directory" }
We can see that the changed variable is set to true
in the response, because the directory doesn't exist or has different attributes and changes were required to make it match the state given by the provided arguments. If it is run a second time with the same arguments, the value of changed will be set to false
, which means that the module did not make any changes to the system.
There are several modules that accept similar arguments to the file
module, and one such example is the copy
module. The copy
module takes a file on the controller machine, copies it to the managed machine, and sets the attributes as required. For example, to copy the /etc/fstab
file to /tmp
on the managed machine, you will use the following command:
$ ansible machinename -m copy -a 'src=/etc/fstab dest=/tmp/fstab'
The preceding command, when run the first time, should return something like the following:
machinename | success >> { "changed": true, "dest": "/tmp/fstab", "group": "root", "md5sum": "fe9304aa7b683f58609ec7d3ee9eea2f", "mode": "0700", "owner": "root", "size": 637, "src": "/root/.ansible/tmp/ansible-1374060150.96- 77605185106940/source", "state": "file" }
There is also a module named command
that will run any arbitrary command on the managed machine. This lets you configure it with any arbitrary command, such as a preprovided
installer or a self-written script; it is also useful for rebooting machines. Note that this module does not run the command within the shell, so you cannot perform redirection, use pipes, expand shell variables, or background commands.
Ansible modules strive to prevent changes being made when they are not required. This is referred to as idempotency and can make running commands against multiple servers much faster. Unfortunately, Ansible cannot know if your command has changed anything or not, so to help it be more idempotent, you have to give it some help. It can do this either via the creates
or the removes
argument. If you give a creates
argument, the command will not run if the filename argument exists. The opposite is true of the removes
argument; if the filename exists, the command will run.
You can run the command as follows:
$ ansible machinename -m command -a 'rm -rf /tmp/testing removes=/tmp/testing'
If there is no file or directory named /tmp/testing
, the command output will indicate that it was skipped, as follows:
machinename | skipped
Otherwise, if the file did exist, it will look like the following code:
ansibletest | success | rc=0 >>
Often it is better to use another module in place of the command
module. Other modules offer more options and can better capture the problem domain they work in. For example, it would be much less work for Ansible and also the person writing the configurations to use the file
module in this instance, since the file
module will recursively delete something if the state is set to absent
. So the preceding command would be equivalent to the following command:
$ ansible machinename -m file -a 'path=/tmp/testing state=absent'
If you need to use features usually available in a shell while running your command, you will need the
shell
module. This way you can use redirection, pipes, or job back grounding. You can pick which shell to use with the executable argument. You can use the shell
module as follows:
$ ansible machinename -m shell -a '/opt/fancyapp/bin/installer.sh >/var/log/fancyappinstall.log creates=/var/log/fancyappinstall.log'
Unfortunately, we don't have enough space to cover every module that is available in Ansible; luckily though, Ansible includes a command name ansible-doc
that can retrieve help information. All the modules included within Ansible have this data populated; however, with modules gathered from elsewhere you may find less help. The ansible-doc
command also allows you to see a list of all modules available to you.
To get a list of all the modules that are available to you along with a short description of each type, use the following command:
$ ansible-doc -l
To see the help file for a particular module, you supply it as the single argument to ansible-doc
. To see the help information for the file
module, for example, use the following command:
$ ansible-doc file
In this chapter, we have covered which installation type to choose for installing Ansible and how to build an inventory file to reflect your environment. After this we saw how to use Ansible modules in an ad hoc style for simple tasks. Finally we discussed how to learn which modules are available on your system and how to use the command line to get instructions for using a module.
In the next chapter, you will learn how to use many modules together in a playbook. This allows you to perform more complex tasks than you could do with single modules alone.