Hosts¶
Each play will require a hosts
line. This can be a host pattern, host group, host, and can be separated by colons. The section looks like this. all
can be replaced with something like *.example.com
as well.
- hosts: all
Users¶
In playbooks we also can specify which user we want to use for the play. To do this we will use the remote_user
value. For example to use the user centos
we would use this example.
- hosts: all
remote_user: centos
This allows us to modify the remote user instead of using our current logged in user on the control machine (the machine we run Ansible from).
Priviledge Escalation¶
Often times we may try to make a change that would require sudo access. By default this priviledge isn’t granted to Ansible. We will need to provide it. To do this we will of course need to provide your user sudo on the remote machine. This will have to be done manually, or you will need to run Ansible with a root
or different account that has the access to modify your sudoers priviledge.
Once the remote machine is configured properly we have a series of directives that we can use.
- become
- when set to
true
oryes
will escalate the priviledge - become_user
- choose which user will be used to escalate priviledge
- become_method
- overrides the default in your ansible configuration file, can use
sudo
,su
,pbrun
,pfexec
,doas
,dzdo
,ksu
Below is an example of using priviledge escalation:
- hosts: all
remote_user: centos
become: yes
We can also apply this to specific tasks throughout the play too!
- hosts: all
remote_user: centos
tasks:
- service: name=firewalld status=stopped enabled=no
become: yes
We can also use other methods like su
to escalate priviledge.
- hosts: all
remote_user: centos
become: yes
become_method: su
When using become method and not using a SSH key, you will need to provide the ansible-playbook
command the password for priviledge escalation. To do this you can use the option --ask-become-pass
, followed by the password. If you run the playbook and it hangs it’s possibly stuck at the priviledge escalation prompt, you can use Control-C to quit and then try again with a valid password.
Tasks¶
Playbooks also will include a list of tasks. Each are run in the order they are listed. Tasks can also include vars specific to the task itself, as well as specific arguments documented in the module.
Tasks will look like this:
tasks:
- name: Stop the Firewalld service and disable it from boot
service: name=firewalld status=stopped enabled=no
We can also specify the task like this:
tasks:
- name: Stop the Firewalld service and disable it from boot
service:
name: firewalld
status: stopped
enabled: no
It’s really up to you, however the first is usually cleaner on some modules, while the second can be useful for modules with many values. The second will also use YAML for everything, the first will likely need specific json formating for complex values.
If a task fails please keep in mind the playbook will stop. You will need to fix the task, then you will need to rerun the playbook. Because of this idempotency is extremely important. If you do not ensure idempotency of your tasks you will possibly run the same command twice.
When using shell
or command
modules they will run the command again. To prevent this you should use a creates
flag or use when
and have a previous task register if the task needs to run again.
Note
command
and shell
modules are the only modules that do not follow key=value format. They are in the free form format of shell: cat myfile
or command: cat myfile
.
You can also ignore errors if your command task results in a 1
or if a module fails. To ignore errors simply add ignore_errors: True
to your task.
tasks:
- name: get contents of myfile
shell: cat myfile
ignore_errors: True
You can also use previously defined variables in your tasks.
vars:
filename: myfile
tasks:
- name: get contents of {{ filename }}
shell: cat {{ filename }}
Handlers¶
Ansible also has an event system which allows tasks to trigger actions. To take advantage of this we have “Handlers”. Handlers can be called using the notify
option on tasks. A nice benefit to this is when you have multiple files that when edited need to restart a service, will notify the hander task which will signal it to run at the end of a play. If multiple files need to restart the same service, it will only restart the service once at the end of the play (instead of multiple times). An example of this is below:
handlers:
- name: restart service
service: name=service state=restarted
tasks:
- name: modify config file
template: src=config.j2 dest=/etc/config.conf
notify: restart service
This will tell Ansible that at the end of the play it will restart the service.
For more information on handlers please visit: http://docs.ansible.com/ansible/playbooks_intro.html#handlers-running-operations-on-change
Roles¶
Roles are a component of Ansible that allow you to reuse tasks, and other components by putting them in a role. Which can be distributed to other people via Ansible Galaxy, or shared internally to allow reusing sets of tasks, vars, etc, to deploy your applications.
An example role can look like this:
---
- hosts: controllers
roles:
- role: avinetworks.avicontroller
con_controller_ip: 10.10.27.101
con_cores: 4
con_memory_gb: 12
To explain this playbook we will show we have a role: avinetworks.avicontroller
, which has variables con_controller_ip
and con_cores
, and con_memory_gb
specified. There are many others possible, but since we are just evaluating the example we will use this. The variables are then passed into the roll to replace any defaults, or simply provide variables that require values. These are referred by roles as “Role Variables”, and lists of possible options are usually in the documentation of the role README for example: https://galaxy.ansible.com/avinetworks/avicontroller/ click on the “README” tab of the Galaxy Role.
Include¶
Play Include¶
There are two types of includes in Ansible. There are Play includes, and Task includes. Play includes will include other plays in your playbook. For example if we have a playbook playbook1.yml
and we want to include that playbook in another playbook, such as master_play.yml
, master_play.yml
would look like this.
---
- include: playbook1.yml
- name: Master play playbook
hosts: all
tasks:
- debug: mg="Extra Task"
This playbook will execute everything in playbook1.yml
and then will continue with the debug task in the next play in the master_play.yml
.
Task Include¶
The second type of include is Task include. Task includes are used to include other files with tasks in them, and can help break one giant set of tasks into others, as well as control when the other tasks are ran, such as a group of tasks you only want ran when a specific condition is met.
For example, here is Ubuntu.yml, this file has a few tasks specific to Ubuntu distributions. If you notice we don’t need tasks:
at the top of the file.
- name: Docker | CE | APT | Add Docker GPG Key
apt_key:
id: 0EBFCD88
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Docker | CE | APT | Configure Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Docker | CE | APT | Enable Edge repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} edge"
state: present
when: docker_channel == "edge"
notify: Docker | CE | APT | Upgrade to Edge
Because these use APT and the repo is for Ubuntu we only need these to run on Ubuntu. So here’s how we would include this in the playbook as a task include.
- name: Master play playbook
hosts: all
tasks:
- name: Docker | CE | APT | Ubuntu
include: Ubuntu.yml
when: ansible_distribution == "Ubuntu"
This can make complex task executions much easier and faster as if all the tasks in Ubuntu.yml
don’t need to run (system doesn’t match as Ubuntu) then it will skip the entire set of tasks in Ubuntu.yml
.
You can also reuse includes and change variables in them. For example lets create message.yml
:
- name: Give a message
debug: msg={{ message }}
Let’s call this in the master playbook and change the message.
- name: Master play playbook
hosts: all
tasks:
- include: message.yml message="This is my message to you"
- include: message.yml message="This is my second message to you"
Static and Dynamic Includes¶
In the previous examples I covered static includes. But now in Ansible 2.0 and later we have Dynamic includes, which allow us to use variables in our includes. For example:
- name: Master play playbook
hosts: all
tasks:
- include: message.yml message={{ item }}
with_items:
- This is my message to you
- This is my second message to you
We can also use other variables in a dynamic include. Previously we did this:
- name: Master play playbook
hosts: all
tasks:
- name: Docker | CE | APT | Ubuntu
include: Ubuntu.yml
when: ansible_distribution == "Ubuntu"
When we wanted the Ubuntu file to run when the OS matched. Now we can take this a step farther. Let’s say we have multiple operating systems, Ubuntu, CentOS, RedHat, etc. We can create task files for each of those, then use the following to dynamically select which one we want to run.
- name: Master play playbook
hosts: all
tasks:
- name: Docker | CE | Repo
include: "{{ ansible_distribution }}.yml"
For more information regarding Dynamic and Static includes please visit: http://docs.ansible.com/ansible/playbooks_roles.html#dynamic-versus-static-includes