Ansible - Basic Configuration

Ansible directory

The default directory for Ansible is /etc/ansible. The directory can be changed via the ansible.cfg file which is customisable for different requirements.

Inventory

In order to tell Ansible which nodes to run tasks on, you need a file that contains a list of hosts, this inventory is also typically called the hosts file, it just looks like a text file with a list of IP addresses or host names, however you can also create groups inside the file to group hosts together. Groups are not the same as roles in Ansible. Groups in inventory is a way to reference multiple hosts using a single name rather than listing hosts individually. Roles are a collection of tasks and files specific to a single role.

Ansible by default creates an inventory file called hosts in /etc/ansible/hosts. I prefer to call the inventory file hosts but you can just call it whatever you want.

Example of hosts file with groups:

[db-prod]
192.168.0.10  
192.168.0.20

[web-prod]
192.168.1.10  
192.168.1.20

[dev-db]
192.168.2.10  
192.168.2.20

[dev-web]
192.168.3.10  
192.168.3.20  

The hosts file is stored in /etc/ansible/ by default, for example the above contents is saved to a file called /etc/ansible/hosts. The file is created by Ansible by default during installation and has examples inside it to show what options you can use.

You can also have multiple inventory files, depending on your infrastructure needs this could be for different development environments, countries, datacenters, services, etc.

The inventory file is capable of a lot more than just groups or lists of IP Addresses. This file is used for Ansible to SSH to hosts, so you can also specify SSH aliases, ports, variables, etc in order for Ansible to SSH successfully.

In my case, I like to change the SSH port on all my hosts from the default of 22 to 2222 as many bots try to hit port 22 when trying to find vulnerabilities which effectively can DDOS your hosts.

To specify a different SSH port, you add ansible_port=2222 to each host entry in the hosts file, as below.

Note: if you run an Ansible task to change this port in future, you will have to manually change the ansible_port value in the hosts file to the port you have changed, however there is a way to avoid having to do this manually by using tasks, which is handy if you are running Ansible on newly bootstrapped vm deployments which have the default port 22 defined. I'll touch upon that in a later article.

If you have a different user account for some hosts, you can also specify the user for each host entry, as below.

Ansible Inventory file with custom SSH port & user
[db-prod]
192.168.0.10 ansible_port=2222 ansible_user=mysql-prod  
192.168.0.20 ansible_port=2222 ansible_user=mysql-prod

[web-prod]
192.168.1.10 ansible_port=2222 ansible_user=www-prod  
192.168.1.20 ansible_port=2222 ansible_user=www-prod

[dev-db]
192.168.2.10 ansible_port=2222 ansible_user=mysql-dev  
192.168.2.20 ansible_port=2222 ansible_user=mysql-dev

[dev-web]
192.168.3.10 ansible_port=2222 ansible_user=www-dev  
192.168.3.20 ansible_port=2222 ansible_user=www-dev  
Running Ansible with Ping module to test hosts file

You can run the following command to test you have set up the inventory file correctly, this will ping all the hosts in the file. You can see it invokes the ping module and uses the root user, the password will be prompted for:

ansible all --inventory-file=hosts --module-name ping -u root  

If you have a ansible_user defined for each entry in the hosts file you can remove the -u root from the above line, as below:

ansible all --inventory-file=hosts --module-name ping  

Tip: You can shorten --inventory-file to -i and --module-name to -m,
e.g.

ansible all -i hosts -m ping  

I will use this format from now on.

Note: You may have to manually ssh to hosts on the control machine to type yes at this prompt that may appear before you run the above command:

Are you sure you want to continue connecting (yes/no)?  

Tip: If you are specifying an ansible_user in the hosts file and if this user account is not the current user on the control machine, you will need to ask Ansible to prompt for a password (--ask-pass) when running the above command, you might also want to provide a password for running tasks that need sudo (--ask-become-pass), like below:

ansible all -i hosts -m ping --ask-pass --ask-become-pass  

After running one of the above commands, according to your needs, you should get the following results that indicate you have correctly configured your credentials and ssh port:

192.168.0.10 | success >> {  
    "changed": false,
    "ping": "pong"
}

192.168.0.20 | success >> {  
    "changed": false,
    "ping": "pong"
}

If you don't you'll need to troubleshoot the issue (incorrect password, port, ssh key authentication only, etc).

Modules

In the above section you have seen the first example of an Ansible module, which is ping. Modules are very powerful and this is where you will start seeing how powerful Ansible is in managing your infrastructure.

Out of the box, there are a lot of modules included with the Ansible installation, you can get a full list of these here, or on your control box, you can enter this command to get a list and short description for each Module.

ansible-doc -l  

You can also count the number of available modules by running:

ansible-doc -l | wc -l  
1652  

There are currently 1652 modules and this number is growing. In particular you will find modules to control not only different Linux distributions but also AWS, Azure, F5 BIG-IP, Docker, Amazon EC2, Cisco IOS, Nexus, UCS and ASA devices, Jenkins, Juniper, Netapp, Citrix Netscaler, OpenStack, VMWare, VyOS and Windows.

The really great thing is, you can find playbook task examples for each of these modules from the Ansible Documentation web page! It's very easy to use.

Custom modules are very easy to create, however I currently don't have any requirements that require one.

Tasks

A task is like an action performed by Ansible, the pinging of hosts in the hosts file is a task performed against those hosts. Tasks can be performed in response to a check, or handler in Ansible as well. So any action such as installing a package, writing to a file, opening a port in a firewall, etc is a task. You can either run a task such as the single line above to ping the hosts in the hosts file, or run tasks in playbooks or as part of a role. Roles and Playbooks are a lot more versatile than just running single tasks.

Handlers

Handlers in Ansible are repeatable tasks, such as restarting a service when a change has been made to a configuration file. If you have a playbook or role that writes changes to a config file or generates files, for example creating web pages in Nginx or opening ports in iptables, you will need to restart the service in order to load the new changes. Handlers can be called to do this. These are explicitly defined in their own section at the end of the playbook.

For example a simple handler to restart Nginx in the playbook would look like this:

handlers:  
    - name: Restart Nginx
      become: true
      service:
        name: nginx
        state: restarted

Above you can see a handler called "Restart Nginx", the module used to run it is called service, the name of the service to restart is nginx and the state of the service should say restarted. When the service restarts successfully, the handler will become true.

Handlers are not automatically called, you need to add it to a task, and then it would only run if the task actually ran, i.e if a config file was changed. If nginx.conf or a file in the sites-enabled was not changed (perhaps because the changes already existed), Ansible will not run it.

Putting the below line inside a task to add a web page to the sites-enabled directory, for example, would notify Ansible to run the Restart Nginx handler.

notify: Restart Nginx
Roles

If you have used Puppet or other Automation tools that have roles, they are the same in Ansible. You can perform tasks in Ansible depending on the role the hosts you are running tasks on.

So, for example MySQL servers will require a specific version of MySQL server installed, perhaps in a specific non-standard location, maybe some customisation to the my.cnf file, and perhaps you want to ensure the service is running as well as opening the port in iptables. You only want these tasks to run on MySQL servers, this is where roles come in. You can just assign a reusable role to a group of MySQL servers and have them all set up easily, to ensure they all meet your requirements and comply with your IT policies.

Roles then, are a collection of tasks and files needed to set up a service that is provided by the role of the servers you are running the role on.

By default, roles are stored in the /etc/ansible/roles directory. Roles have a directory structure, similar to roles and manifests in Puppet. Within the Role directory you would create a new directory with the role name and underneath the role name would be subdirectories for files, handlers, meta, templates, tasks, vars, as below:

cd /etc/ansible/roles  
mkdir nginx  
cd nginx  
mkdir files handlers meta templates tasks vars  

Normally in all of these directories except Files and Templates will contain a main.yml file, when a playbook is run that references the role the contents of these main.yml files within the role subdirectories will also be added. So, Roles are a different way to organise tasks when used with a playbook and could be much cleaner than having a playbook with a big bunch of tasks listed inside.

Playbooks

Playbooks are in yaml format, and are similar to Puppet Manifests but also so much more. They link the relationship between hosts and roles. A playbook contains hosts to tell Ansible which hosts to apply the playbook to and tasks or roles that will apply to those hosts. You can also use custom variables to specify variables as well as handlers and more. You run playbooks by running the command ansible-playbook and specifying the playbook file.

A very simple and clean playbook looks similar to this:

- hosts: mail_prod
  roles:
    - postfix

- hosts: db_prod
  roles:
    - mysql

- hosts: web_prod
  roles:
    - apache

The above tells Ansible to perform tasks contained within the roles above. However, tasks may also be run directly in the playbook rather than in the Role. This depends on the needs of the user running the playbook and whether these are one-off task that are separate to the role.

Facts

Facts in Ansible are like Facts in Puppet or other Automation tools. Usually facts describe things such as OS and build of a host, you can then run tasks based on this information. For example, facts gathered about a RedHat server determines that Yum will be needed instead of Apt to install packages as Apt is used on Debian-based OS's such as Ubuntu.

The Ansible module "setup" gathers facts about hosts. Ansible runs this module automatically when you run a task but you can also have custom facts, that are specific to your organisation, this allows you to manage your hosts the way you want.

You can manually get facts by running the following command manually:

ansible all -m setup -i hosts --ask-pass

Vault

The Vault in Ansible is a secure section that allows you to keep secure information such as passwords safe. This is used to store data securely for Ansible to use without it being compromised.

comments powered by Disqus