Ansible PDF
Ansible PDF
Ansible PDF
#ansible
Table of Contents
About 1
Remarks 2
Examples 2
Hello, World 2
Inventory 3
ansible.cfg 4
Examples 11
Examples 13
Examples 15
Introduction 16
Examples 16
Examples 18
with_items - dictionary 19
Nested loops 19
Examples 21
Common use 21
All Lists 21
When Condition 22
Basic Usage 22
Single condition 23
Boolean Filter 23
Multiple Conditions 23
Introduction 26
Syntax 26
Examples 26
Only in a task 26
Remarks 27
Examples 27
Examples 29
Examples 30
Basic commands 30
Chapter 12: How To Create A DreamHost Cloud Server From An Ansible Playbook 31
Examples 31
Introduction 33
Examples 33
Alternative solution: 36
Examples 37
Overview 37
Playbook's structure 37
Play's structure 38
Tags 39
Parameters 40
Examples 41
Hosts file 43
Chapter 16: Loops 45
Examples 45
Examples 46
Using roles 46
Role dependencies 47
Remarks 49
Examples 49
Remarks 51
Examples 51
How to start EC2 instance from official Amazon AMIs, modify it and store it as new AMI 51
Introduction 56
Parameters 56
Remarks 56
Examples 56
Credits 62
About
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: ansible
It is an unofficial and free ansible ebook created for educational purposes. All the content is
extracted from Stack Overflow Documentation, which is written by many hardworking individuals at
Stack Overflow. It is neither affiliated with Stack Overflow nor official ansible.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/ 1
Chapter 1: Getting started with ansible
Remarks
This section provides an overview of what ansible is, and why a developer might want to use it.
It should also mention any large subjects within ansible, and link out to the related topics. Since
the Documentation for ansible is new, you may need to create initial versions of those related
topics.
Examples
Hello, World
mkdir ansible-helloworld-playbook
Create a file hosts and add remote systems how want to manage. As ansible relies on ssh to
connect the machines, you should make sure they are already accessible to you in ssh from your
computer.
192.168.1.1
192.168.1.2
Test connection to your remote systems using the Ansible ping module.
https://riptutorial.com/ 2
Test sudo access with
Inventory
Inventory is the Ansible way to track all the systems in your infrastructure. Here is a simple static
inventory file containing a single system and the login credentials for Ansible.
[targethost]
192.168.1.1 ansible_user=mrtuovinen ansible_ssh_pass=PassW0rd
Write these lines for example to hosts file and pass the file to ansible or ansible-playbook command
with -i/--inventory-file flag.
We can provision remote systems with Ansible. You should have an SSH key-pair and you should
take your SSH public key to the machine ~/.ssh/authorized_keys file. The porpuse is you can login
without any authorization.
Prerequisites:
• Ansible
You need an Inventory file (for ex.: development.ini) where you determine the host what you want
to use:
[MACHINE_NAME]
MACHINE_NAME hostname=MACHINE_NAME ansible_ssh_host=IP_ADDRESS ansible_port=SSH_PORT
ansible_connection=ssh ansible_user=USER ansible_ssh_extra_args="-o StrictHostKeyChecking=no -
o UserKnownHostsFile=/dev/null"
https://riptutorial.com/ 3
connection
• StrictHostKeyChecking - It can ask a key checking what waiting for a yes or no. The Ansible
can't answer this question then throw an error, the host not available.
• UserKnownHostsFile - Needed for StrictHostKeyChecking option.
If you have this inventory file you can write a test playbook.yml:
---
- hosts: MACHINE_NAME
tasks:
- name: Say hello
debug:
msg: 'Hello, World'
ansible.cfg
[defaults]
#inventory = /etc/ansible/hosts
#library = /usr/share/my_modules/
#remote_tmp = $HOME/.ansible/tmp
#local_tmp = $HOME/.ansible/tmp
#forks = 5
#poll_interval = 15
#sudo_user = root
#ask_sudo_pass = True
#ask_pass = True
#transport = smart
#remote_port = 22
#module_lang = C
#module_set_locale = False
https://riptutorial.com/ 4
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit
# SSH timeout
#timeout = 10
https://riptutorial.com/ 5
#module_name = command
# if set, always use this private key file for authentication, same as
# if passing --private-key to ansible or ansible-playbook
#private_key_file = /path/to/file
# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False
# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
https://riptutorial.com/ 6
#system_warnings = True
# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
#deprecation_warnings = True
# (as of 1.8), Ansible can optionally warn when usage of the shell and
# command module appear to be simplified by using a default Ansible module
# instead. These warnings can be silenced by adjusting the following
# setting or adding warn=yes or warn=no to the end of the command line
# parameter string. This will for example suggest using the git module
# instead of shelling out to the git command.
# command_warnings = False
# by default callbacks are not loaded for /bin/ansible, enable this if you
# want, for example, a notification or logging callback to also apply to
# /bin/ansible runs
#bin_ansible_callbacks = False
# set which cowsay stencil you'd like to use by default. When set to 'random',
# a random stencil will be selected for each task. The selection will be filtered
# against the `cow_whitelist` option below.
#cow_selection = default
#cow_selection = random
# when using the 'random' option for cowsay, stencils will be restricted to this list.
# it should be formatted as a comma-separated list with no spaces between names.
# NOTE: line continuations here are for formatting purposes only, as the INI parser
# in python does not support them.
#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
# hellokitty,kitty,luke-
koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www
# if set to a persistent type (not 'memory', for example 'redis') fact values
# from previous runs in Ansible will be stored. This may be useful when
# wanting to use, for example, IP information from one group of servers
# without having to talk to them in the same playbook run to get their
https://riptutorial.com/ 7
# current IP information.
#fact_caching = memory
# retry files
# When a playbook fails by default a .retry file will be created in ~/
# You can disable this feature by setting retry_files_enabled to False
# and you can change the location of the files by setting retry_files_save_path
#retry_files_enabled = False
#retry_files_save_path = ~/.ansible-retry
# squash actions
# Ansible can optimise actions that call modules with list parameters
# when looping. Instead of calling the module once per with_ item, the
# module is called once with all items at once. Currently this only works
# under limited circumstances, and only with parameters named 'name'.
#squash_actions = apk,apt,dnf,package,pacman,pkgng,yum,zypper
# prevents logging of tasks, but only on the targets, data is still logged on the
master/controller
#no_target_syslog = False
# controls what compression method is used for new-style ansible modules when
# they are sent to the remote system. The compression types depend on having
# support compiled into both the controller's python and the client's python.
# The names should match with the python Zipfile compression types:
# * ZIP_STORED (no compression. available everywhere)
# * ZIP_DEFLATED (uses zlib, the default)
# These values may be set per host via the ansible_module_compression inventory
# variable
#module_compression = 'ZIP_DEFLATED'
# This controls the cutoff point (in bytes) on --diff for files
# set to 0 for unlimited (RAM may suffer!).
#max_diff_size = 1048576
[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
[paramiko_connection]
https://riptutorial.com/ 8
# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently
of the
# host key checking setting above.
#record_host_keys=False
# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment
this
# line to disable this behaviour.
#pty=False
[ssh_connection]
# if False, sftp will not use batch mode to transfer files. This may cause some
# types of file transfer failures impossible to catch however, and should
# only be disabled if your sftp version has problems with batch mode
#sftp_batch_mode = False
[accelerate]
#accelerate_port = 5099
#accelerate_timeout = 30
#accelerate_connect_timeout = 5.0
https://riptutorial.com/ 9
#accelerate_multi_key = yes
[selinux]
# file systems that require special treatment when dealing with security context
# the default behaviour that copies the existing context or uses the user default
# needs to be changed to use the file system dependent context.
#special_context_filesystems=nfs,vboxsf,fuse,ramfs
[colors]
#highlight = white
#verbose = blue
#warn = bright purple
#error = red
#debug = dark gray
#deprecate = purple
#skip = cyan
#unreachable = red
#ok = green
#changed = yellow
#diff_add = green
#diff_remove = red
#diff_lines = cyan
Put this configuration in the root of your role directories to change the behavior of Ansible when
using that role. For example, you can set it to stop create playbook.retry on failed playbook runs or
to point to secret vars that you don't want in your git repo.
https://riptutorial.com/ 10
Chapter 2: Ansible Architecture
Examples
Understanding Ansible Architecture
The idea is to have one or more control machines from where you can issue ad-hoc commands to
remote machines (via ansible tool) or run a sequenced instruction set via playbooks (via ansible-
playbook tool).
Basically, we use Ansible control machine, this will typically be your desktop, laptop or server.
Then from there, you use Ansible to push configuration changes out, via ssh.
The host inventory file determines the target machines where these plays will be executed. The
Ansible configuration file can be customized to reflect the settings in your environment.
https://riptutorial.com/ 11
Read Ansible Architecture online: https://riptutorial.com/ansible/topic/7659/ansible-architecture
https://riptutorial.com/ 12
Chapter 3: Ansible group variables
Examples
Group variables with static inventory
It is suggested that you define groups based on purpose of the host (roles) and also geography or
datacenter location (if applicable):
File inventory/production
[rogue-server]
192.168.1.1
[atlanta-webservers]
www-atl-1.example.com
www-atl-2.example.com
[boston-webservers]
www-bos-1.example.com
www-bos-2.example.com
[atlanta-dbservers]
db-atl-1.example.com
db-atl-2.example.com
[boston-dbservers]
db-bos-1.example.com
File group_vars/all
---
apache_port: 80
https://riptutorial.com/ 13
File group_vars/atlanta-webservers
---
apache_port: 1080
File group_vars/boston-webservers
---
apache_port: 8080
File host_vars/www-bos-2.example.com
---
apache_port: 8111
Address Port
192.168.1.1 80
www-atl-1.example.com 1080
www-atl-2.example.com 1080
www-bos-1.example.com 8080
www-bos-2.example.com 8111
https://riptutorial.com/ 14
Chapter 4: Ansible Group Vars
Examples
Example group_vars/development, and why
Project structure
project/
group_vars/
development
inventory.development
playbook.yaml
These variables will be applied to hosts under the development group due to the filename.
---
## Application
app_name: app
app_url: app.io
web_url: cdn.io
app_friendly: New App
env_type: production
app_debug: false
## SSL
ssl: true
ev_ssl: false
## Database
database_host: 127.0.0.1
database_name: app
database_user: sql
## Elasticsearch
elasticsearch_host: 127.0.0.1
https://riptutorial.com/ 15
Chapter 5: Ansible install mysql
Introduction
How use ansible to install mysql binary file
Examples
How use ansible to install mysql binary file
○ name: install the latest version of libselinux-python yum: name: libselinux-python state:
latest
○ name: remove the mysql-libs package yum: name: mysql-libs state: absent
- name: ldconfig
command: /sbin/ldconfig
https://riptutorial.com/ 16
- name: Copy mysql my.cnf
copy: src=/https/www.scribd.com/etc/my.cnf dest=/usr/local/mysql/my.cnf
https://riptutorial.com/ 17
Chapter 6: Ansible: Looping
Examples
with_items - simple list
From vars:
favorite_snacks:
- hotdog
- ice cream
- chips
If you are using Ansible 2.0+ you must use quotes around the call to the variable.
From vars:
packages:
- present: tree
- present: nmap
- absent: apache2
https://riptutorial.com/ 18
package: name={{ item.value }} state={{ item.key }}
with_items: '{{ packages }}'
vars:
packages:
- name: tree
state: present
- name: nmap
state: present
- name: apache2
state: absent
with_items - dictionary
Nested loops
from vars:
keys:
- key1
- key2
- key3
- key4
https://riptutorial.com/ 19
This task will loop over each user and populate their authorized_keys file with the 4 keys defined in
the list.
https://riptutorial.com/ 20
Chapter 7: Ansible: Loops and Conditionals
Remarks
Official docs explains playbook conditionals.
• http://docs.ansible.com/ansible/playbooks_conditionals.html
Ansible (github)
• https://github.com/marxwang/ansible-learn-resources
Examples
What kinds of conditionals to use?
• when [when:]
Task:
- name: run if operating system is debian
command: echo "I am a Debian Computer"
when: ansible_os_family == "Debian"
• loops [with_items:]
• loops [with_dicts:]
• Conditional imports
Common use
• when: ansible_os_family == "CentOS"
• when: ansible_os_family == "Redhat"
• when: ansible_os_family == "Darwin"
• when: ansible_os_family == "Debian"
• when: ansible_os_family == "Windows"
https://riptutorial.com/ 21
All Lists
based on discuss here http://comments.gmane.org/gmane.comp.sysutils.ansible/4685
OS_FAMILY = dict(
RedHat = 'RedHat',
Fedora = 'RedHat',
CentOS = 'RedHat',
Scientific = 'RedHat',
SLC = 'RedHat',
Ascendos = 'RedHat',
CloudLinux = 'RedHat',
PSBM = 'RedHat',
OracleLinux = 'RedHat',
OVS = 'RedHat',
OEL = 'RedHat',
Amazon = 'RedHat',
XenServer = 'RedHat',
Ubuntu = 'Debian',
Debian = 'Debian',
SLES = 'Suse',
SLED = 'Suse',
OpenSuSE = 'Suse',
SuSE = 'Suse',
Gentoo = 'Gentoo',
Archlinux = 'Archlinux',
Mandriva = 'Mandrake',
Mandrake = 'Mandrake',
Solaris = 'Solaris',
Nexenta = 'Solaris',
OmniOS = 'Solaris',
OpenIndiana = 'Solaris',
SmartOS = 'Solaris',
AIX = 'AIX',
Alpine = 'Alpine',
MacOSX = 'Darwin',
FreeBSD = 'FreeBSD',
HPUX = 'HP-UX'
)
When Condition
Basic Usage
Use the when condition to control whether a task or role runs or is skipped. This is normally used
to change play behavior based on facts from the destination system. Consider this playbook:
- hosts: all
tasks:
- include: Ubuntu.yml
when: ansible_os_family == "Ubuntu"
- include: RHEL.yml
when: ansible_os_family == "RedHat"
https://riptutorial.com/ 22
Where Ubuntu.yml and RHEL.yml include some distribution-specific logic.
Another common usage is to limit results to those in certain Ansible inventory groups. Consider
this inventory file:
[dbs]
mydb01
[webservers]
myweb01
- hosts: all
tasks:
- name: Restart Apache on webservers
become: yes
service:
name: apache2
state: restarted
when: webservers in group_names
when: (condition)
Example
Boolean Filter
Example
when: result|failed
Multiple Conditions
Syntax
https://riptutorial.com/ 23
Example (simple)
Example (complex)
Use parentheses for clarity or to control precedence. "AND" has a higher precedence than "OR".
when:
ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] and
(ansible_distribution_version|version_compare('7', '<') or
ansible_distribution_version|version_compare('8', '>='))
or
ansible_distribution == 'Fedora'
or
ansible_distribution == 'Ubuntu' and
ansible_distribution_version|version_compare('15.04', '>=')
Note the use of parentheses to group the "or" in the first distribution check.
We can get facts (ansible_os_family, ansible_pkg_mgr) with Ad-Hoc command of setup module and
filter.
• ansible_os_family:
• ansible_pkg_mgr:
Given:
---
variable_name: True
https://riptutorial.com/ 24
Then, these tasks with always run.
This is an example of using until/retries/delay to implement an alive check for a webapp that is
starting up. It assumes that there will be some period of time (up to 3 minutes) where the webapp
is refusing socket connections. After that, it checks the /alive page for the word "OK". It also
delegates the retrieval of the URL to the localhost running ansible. This makes sense as the final
task in a deployment playbook.
---
- hosts: my-hosts
tasks:
- action: uri url=http://{{ ansible_all_ipv4_addresses }}:8080/alive return_content=yes
delegate_to: localhost
register: result
until: "'failed' not in result and result.content.find('OK') != -1"
retries: 18
delay: 10
The until retry pattern can be used with any action; Ansible documentation provides an example of
waiting until a certain shell command returns a desired result:
http://docs.ansible.com/ansible/playbooks_loops.html#do-until-loops.
https://riptutorial.com/ 25
Chapter 8: Become (Privilege Escalation)
Introduction
Often you need to execute commands under a different user or get root privileges. Those options
allow you to become another user in the guest system.
Syntax
• become:can be set to true or yes and triggers the user escalation settings.
• become_user: set to the desired user in the remote host.
• become_method: specify the command used to make login and change user.
• become_flags: change login parameters. Mostly used when you want to change to a system
user without shell privileges.
Examples
Only in a task
- hosts: all
become: true
- hosts: all
roles:
- { role: myrole, become: yes }
- myrole2
https://riptutorial.com/ 26
Chapter 9: Dynamic inventory
Remarks
Environment variables in dynamic inventory won't work, f.e.
"ansible_ssh_private_key_file": $HOME/.ssh/key.pem"
If the dynamic inventory server side passes $HOME for example, replace the variable in the client
code (Python):
json_input.replace("$HOME", os.environ.get("HOME"))
Examples
Dynamic inventory with login credentials
{
"_meta": {
"hostvars": {
"10.1.0.10": {
"ansible_user": "vagrant",
"ansible_ssh_private_key_file": "/home/mrtuovinen/.ssh/id_rsa",
"ansible_port": 22
},
"10.1.0.11": {
"ansible_user": "ubuntu",
"ansible_ssh_private_key_file": "/home/mrtuovinen/.ssh/id_rsa",
"ansible_port": 22
},
"10.1.0.12": {
"ansible_user": "steve",
"ansible_ssh_private_key_file": "/home/mrtuovinen/.ssh/key.pem",
"ansible_port": 2222
}
}
},
"vagrantbox": [
"10.1.0.10"
],
"ubuntubox": [
"10.1.0.11"
],
"osxbox": [
https://riptutorial.com/ 27
"10.1.0.12"
]
}
https://riptutorial.com/ 28
Chapter 10: Galaxy
Examples
Sharing roles with Ansible Galaxy
It's also possible to easily share roles with the community or download roles that have been
created by other members of the community with Ansible Galaxy.
Ansible ships with a command line tool called ansible-galaxy that can be used to install roles in the
role directory defined in the ansible.cfg file:
You can also use the Ansible Galaxy tool to download roles from other locations such as GitHub
by creating a text file with the location defined as src:
- src: https://github.com/username/rolename
And then install the roles in the text file like so:
You can also use the ansible-galaxy tool to create role "scaffolding":
Once you've created a role and uploaded it to GitHub you can share it on Ansible Galaxy by
linking to your GitHub repo in Ansible Galaxy after signing in.
https://riptutorial.com/ 29
Chapter 11: Galaxy
Examples
Basic commands
More help
ansible-galaxy --help
https://riptutorial.com/ 30
Chapter 12: How To Create A DreamHost
Cloud Server From An Ansible Playbook
Examples
Install Shade library
Shade is a library developed by OpenStack to simplify interactions with OpenStack clouds, like
DreamHost.
The first part of the playbook is a list of hosts that your playbook will run on, we only have one,
localhost.
- hosts: localhost
Then we need to define a list of tasks to perform in this playbook. We will only have one that
launches an Ubuntu Xenial server on DreamCompute.
tasks:
- name: launch an Ubuntu server
Next part of the playbook uses the os_server (OpenStack Server) module. This defines what the
server has to look like in DreamCompute.
os_server:
auth:
auth_url: https://iad2.dream.io:5000
username: {username}
password: {password}
project_name: {project}
state: present
https://riptutorial.com/ 31
name: ansible-vm1
image: Ubuntu-16.04
key_name: {keyname}
flavor: 50
network: public
wait: yes
• state is the state of the server, possible values are present or absent
• name is the name of the server to create; can be any value
• image is the image to boot the server from; possible values are visible on DreamHost Cloud
web panel; the variable accepts either image name or UUID
• key_name is the name of the public key to add to the server once it is created; this can be any
key has already been added to DreamCompute.
• flavor is the flavor of server to boot; this defines how much RAM and CPU your server will
have; the variable accepts either the name of a flavor (gp1.semisonic) or the ID (50, 100,
200, etc)
• network is the network to put your server on. In DreamHost Cloud case it is the public
network.
• wait set to yes forces the playbook to wait for the server to be created before continuing.
$ ansible-playbook launch-server.yaml
PLAY [localhost]
***************************************************************
TASK [setup]
*******************************************************************
ok: [localhost]
PLAY RECAP
*********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
Now if you check the DreamHost Cloud dashboard you should see a new instance named
“ansible-vm1”
Read How To Create A DreamHost Cloud Server From An Ansible Playbook online:
https://riptutorial.com/ansible/topic/4689/how-to-create-a-dreamhost-cloud-server-from-an-ansible-
playbook
https://riptutorial.com/ 32
Chapter 13: Installation
Introduction
Installing Ansible in any OS, including Windows using Virtual Box and Vagrant. An alternate
solution is also available if you just want to practice ansible ad-hoc commands and playbooks and
do not wish to set up the local environment.
Examples
Installing Ansible on Ubuntu
Ansible maintains a PPA repository that can be used to install the Ansible binaries:
To install a specific version, use pip. The PPA may be out of date.
There are two main ways way to install Ansible on OS X, either using the Homebrew or Pip
package manager.
If you have homebrew, the latest Ansible can be installed using the following command:
To install using pip, use the following command: pip install ansible.
Ansible can be installed on CentOS or other Red Hat based systems. Firstly you should install the
prerequisites:
https://riptutorial.com/ 33
sudo yum -y update
sudo yum -y install gcc libffi-devel openssl-devel python-pip python-devel
I can recommend for you to upgrade the setuptools after the installation:
cd ~/Documents
git clone git://github.com/ansible/ansible.git --recursive
cd ansible
Finally, add the ansible initialization script line to your ~/.bashrc or ~/.zshrc :
source ~/Documents/ansible/hacking/env-setup
ansible --version
Amazon Linux is a RHEL variant, so the Red Hat instructions should work for the most part. There
is, however, at least one discrepancy.
There was an instance where the python27-devel package, as opposed to python-devel, was
explicitly necessary.
https://riptutorial.com/ 34
sudo yum -y update
sudo yum -y install python27 python27-devel openssl-devel libffi-devel gcc git
cd ansible
sudo python setup.py build
sudo python setup.py install
My laptop is having Windows 10. Here i am giving steps that you can follow to test and learn
Ansible.
SOME THEORY
For Ansible you need a Control Machine and a host(or hosts) to run the Playbook.
• Control Machine should be Linux based or MacOS(windows not allowed) and need Python
(2.6 or higher version). Here Ansible will be installed.
• Target machine (host/node) can be Linux/MacOS/windows. This needs only Python to be
installed. No agent software required.
SETUP
Virtual box is a software to create virtual computers of different OS. It is like having multiple
computers each or different OS and different versions.
Download Virtual Box according to the OS in your system and install it.
Vagrant is Command Line Interface to create virtual machines in virtual box. This makes things
easy. You need to learn basic Vagrant commands.
Open terminal and go to the path where you created folder, and run the following two commands.
You need to select Virtual Box. I am installing Ubuntu for example. You can choose anything from
the list. You need to run these two commands under "virtual box" category: vagrant init
ubuntu/trusty64 and vagrant up --provider virtualbox. Other categories might be: hyperv,
vmware_desktop etc. (this will take some time, as it will download the necessary files)
https://riptutorial.com/ 35
Alternative solution:
You can use Katacoda to practice ansible. No need to install or setup anything. Run two
commands given in step 2 and after that, you are good to go.
https://riptutorial.com/ 36
Chapter 14: Introduction to playbooks
Examples
Overview
In Ansible, a playbook is is a YAML file containing the definition of how a server should look. In a
playbook you define what actions Ansible should take to get the server in the state you want. Only
what you define gets done.
This is a basic Ansible playbook that installs git on every host belonging to the web group:
---
- name: Git installation
hosts: web
remote_user: root
tasks:
- name: Install Git
apt: name=git state=present
Playbook's structure
The format of a playbook is quite straightforward, but strict in terms of spacing and layout. A
playbook consists of plays. A play is a combination of targets hosts and the tasks we want to apply
on these hosts, so a drawing of a playbook is this:
https://riptutorial.com/ 37
To execute this playbook, we simply run:
Play's structure
Think of a play as the thing that connects hosts to tasks. In addition to specifying hosts and tasks,
plays also support a number of optional settings. Two common ones are:
• name: a comment that describes what the play is about. Ansible will print this out when the
https://riptutorial.com/ 38
play starts to run
• vars: a list of variables and values
Tags
Task with tag 'vim' will run when 'vim' is specified in tags. You can specify as many tags as you
want. It is useful to use tags like 'install' or 'config'. Then you can run playbook with specifying tags
or skip-tags. For
https://riptutorial.com/ 39
Chapter 15: Inventory
Parameters
Parameter Explanation
The name of the host to connect to, if different from the alias
ansible_host
you wish to give to it.
Private key file used by ssh. Useful if using multiple keys and
ansible_ssh_private_key_file
you don’t want to use SSH agent.
https://riptutorial.com/ 40
Parameter Explanation
The shell type of the target system. You should not use this
setting unless you have set the ansible_shell_executable to a
non-Bourne (sh) compatible shell. By default commands are
ansible_shell_type
formatted using sh-style syntax. Setting this to csh or fish will
cause commands executed on target systems to follow those
shell’s syntax instead.
The target host python path. This is useful for systems with
more than one Python or not located at /usr/bin/python such
as *BSD, or where /usr/bin/python is not a 2.X series
ansible_python_interpreter Python. We do not use the /usr/bin/env mechanism as that
requires the remote user’s path to be set right and also
assumes the python executable is named python, where the
executable might be named something like python2.6.
Works for anything such as ruby or perl and works just like
ansible_*_interpreter ansible_python_interpreter. This replaces shebang of modules
which will run on that host.
This sets the shell the ansible controller will use on the target
machine, overrides executable in ansible.cfg which defaults to
ansible_shell_executable /bin/sh. You should really only change it if is not possible to
use /bin/sh (i.e. /bin/sh is not installed on the target machine
or cannot be run from sudo.). New in version 2.1.
Examples
Inventory with username and password
Inventory is the Ansible way to track all the systems in your infrastructure. Here is a simple
inventory file containing a single system and the login credentials for Ansible.
[targethost]
192.168.1.1 ansible_user=mrtuovinen ansible_ssh_pass=PassW0rd
[targethost]
192.168.1.1 ansible_user=mrtuovinen ssh_private_key_file=~/.ssh/custom_key
https://riptutorial.com/ 41
[targethost]
192.168.1.1 ansible_user=mrtuovinen ansible_port=2222
project/
group_vars/
development
inventory.development
playbook.yaml
[development]
dev.fakename.io
[development:vars]
ansible_host: 192.168.0.1
ansible_user: dev
ansible_pass: pass
ansible_port: 2232
[api:children]
development
which lets you link to group_vars. Hold data 'specific' to that environment ...
---
app_name: NewApp_Dev
app_url: https://dev.fakename.io
app_key: f2390f23f01233f23f
that lets one run the following playbook AGAINST the inventory file:
---
- name: Install api.
hosts: api
gather_facts: true
sudo: true
https://riptutorial.com/ 42
tags:
- api
roles:
- { role: api, tags: ["api"] }
Hosts file
The host file is used to store connections for Anisble playbooks. There are options to define
connection parameters:
ansible_ssh_private_key_file if you need to use multiple keys that are specific to hosts
These are the most commonly used options. More can be found in the Ansible official
documentation.
[web-servers]
server1 ansible_host=192.168.0.1 ansible_port=1600
server2 ansible_host=192.168.0.2 ansible_port=1800
[offsite]
server3 ansible_host=10.160.40.1 ansible_port=22 ansible_user=root
server4 ansible_host=10.160.40.2 ansible_port=4300 ansible_user=root
[onsite]
server5 ansible_host=10.150.70.1 ansible_ssh_pass=password
[backup-servers]
server6 ansible_host=10.160.40.3 ansible_port=77
https://riptutorial.com/ 43
Read Inventory online: https://riptutorial.com/ansible/topic/1764/inventory
https://riptutorial.com/ 44
Chapter 16: Loops
Examples
Copy multiple files in a single task
https://riptutorial.com/ 45
Chapter 17: Roles
Examples
Using roles
Ansible uses the concept of roles to better allow modular code and avoid repeating yourself.
A role is simply a folder structure that Ansible knows where to load vars files, tasks and handlers
from. An example might look something like this:
apache/
├── defaults
│ └── main.yml
├── files
│ ├── mod-pagespeed-stable_current_i386.deb
│ ├── mod-pagespeed-stable_current_i386.rpm
│ ├── mod-pagespeed-stable_current_amd64.deb
| └── mod-pagespeed-stable_current_x86_64.rpm
├── tasks
│ ├── debian.yml
│ ├── main.yml
│ └── redhat.yml
├── templates
│ ├── httpd.conf.j2
│ └── sites-available
│ └── virthualhost.conf.j2
└── vars
├── debian
└── redhat
You can then use the role with a basic playbook that just looks like this:
- hosts: webservers
roles:
- apache
When you run Ansible against this playbook it will target all the hosts in the webservers group and
run the apache role defined above against it, automatically loading any default variables for the role
and running all the tasks included in tasks/main.yml. Ansible also knows to look for certain types of
files in role friendly locations:
• If roles/x/meta/main.yml exists, any role dependencies listed therein will be added to the list
of roles (1.3 and later)
https://riptutorial.com/ 46
• Any copy, script, template or include tasks (in the role) can reference files in
roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or
absolutely
Role dependencies
Roles also enable you to define other roles as a dependency by creating a meta/main.yml file with a
dependencies block:
dependencies:
- role: common
dependencies:
- { role: common, some_parameter: 3 }
dependencies:
- { role: common, some_parameter: 3 }
- { role: sshd, enable_sshd: false,
when: environment == 'production' }
Dependent roles are always executed before the roles that depend on them. Also, they are only
executed once. If two roles state the same one as their dependency, it is only executed the first
time.
Imagine the roles role1, role2 and role3 with the folling meta/main.yml's:
role1/meta/main.yml:
dependencies:
- role: role3
role2/meta/main.yml:
dependencies:
- role: role3
When executing role1 and role2 in the same playbook (with role1 called before role2), the
execution order would be the following:
You may override this behaviour by specifying allow_duplicates: yes in meta/main.yml of role1 and
role2. The resulting execution order would the be:
https://riptutorial.com/ 47
Separating distribution specific tasks and variables inside a role
We can easily separate distribution specific tasks and variables into different dedicated .yml files.
Ansible helps us to automatically identify the target hosts distribution via {{ ansible_distribution
}} and {{ ansible_distribution_version }}, so we just have to name the distribution dedicated .yml
files accordingly.
For Ubuntu Xenial the basic role dir tree would then look something like that:
role
├── tasks
│ ├── main.yml
│ └── Ubuntu16.04.yml
└── vars
└── Ubuntu16.04.yml
Inside the tasks/main.yml we can now automatically include the proper variables and tasks for the
target hosts distribution.
tasks/main.yml
---
Inside tasks/Ubuntu16.06.yml and vars/Ubuntu16.04.yml we can now define tasks and variables for
Ubuntu Xenial respectively.
https://riptutorial.com/ 48
Chapter 18: Secret encryption
Remarks
Ansible offers Vault (not to be mistaken with HashiCorp Vault!) to handle sensitive data encryption.
Vault primarily targets to encrypt any structured data such as variables, tasks, handlers.
Examples
Encrypting sensitive structured data
First, create a key file, e.g., vault_pass_file, which ideally contains a long sequence of random
characters. In linux systems you could use pwgen to create a random password file:
From now on, in order to run a playbook you need the vault_pass_file:
Note, you could also use the flag --vault-password-file vault_pass_file instead of setting the
ANSIBLE_VAULT_PASSWORD_FILE environment variable.
In order to edit or decrypt the secret on disk you can use ansible-vault edit and ansible-vault
decrypt respectively.
With Vault you can also encrypt non-structured data, such as private key files and still be able to
decrypt them in your play with the lookup module.
---
https://riptutorial.com/ 49
You can run a play which relies on vault-encrypted templates by using the local_action module.
---
Please note the changed_when: False. This is important in case you run idempotence tests with your
ansible roles - otherwise each time you run the playbook a change is signaled. In
group_vars/all.yml you could set a global decrypt command for reuse, e.g., as
view_encrypted_file_cmd.
group_vars/all.yml
---
Now, when running a play you need to set the ANSIBLE_VAULT_PASSWORD_FILE environment variable to
point to your vault password file (ideally with an absolute path).
https://riptutorial.com/ 50
Chapter 19: Using Ansible with Amazon Web
Services
Remarks
example-2: This is serves as an example so just don't copy/past it. Instead, to suit your needs, you
should customize its variables; ansible_key, security group rules etc..
example-1: To disable the ssh strict host key checking, a behavior we don't want when automating
tasks, we set it to no in ansible.cfg file. ie: StrictHostKeyChecking=no
The ec2.py file is a python script that executes and returns your AWS ressources based on the
ec2.ini which is the configuration file that you need to customize if you want to limit the scope of
your project to some particular regions, specific tags etc...
Examples
How to start EC2 instance from official Amazon AMIs, modify it and store it as
new AMI
This is a very common workflow when using Ansible for provisioning an AWS EC2 instance. This
post assumes a basic understand of Ansible and most importantly, assumes you've properly
configured it to connect to AWS.
1- ami_find to get the ami id based on which we will launch our EC2 instance.
3- code_deploy for modifying the instance; this could be anything so we will simply transfer a file
to the target machine.
4- build_ami to build our new image based on the running ec2 instance. This post assumes you
are at the top level of your Ansible project: my_ansible_project
In this role we are going to use the ec2_ami_find module and as an example, we will search for
the an Ubuntu machine and get its ami_id ( ami-xxxxxxxx ). Now edit
my_ansible_project/roles/ami_find/tasks/main.yml file:
---
https://riptutorial.com/ 51
- ec2_ami_find:
name: "ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"
sort: name
sort_order: descending
sort_end: 1
region: "{{ aws_region }}"
register: ami_find
- set_fact: ami_ubuntu="{{ ami_find.results[0].ami_id }}"
Here, we will use the ami_id we got from the first role and then launch our new instance based on
it:
In this role we are going to use most importantly the ec2_module to launch our instance. Now edit
my_ansible_project/roles/ec2_ami_creation/tasks/main.yml file:
---
- ec2_vpc_subnet_facts:
region: "{{aws_region}}"
register: vpc
- name: creation of security group of the ec2 instance
ec2_group:
name: example
description: an example EC2 group
region: "{{ aws_region }}"
rules:
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: 0.0.0.0/0
state: present
register: ec2_sg
- set_fact: id={{ec2.instances[0].id}}
- name: adding the newly created instance to a temporary group in order to access it later
from another play
add_host: name={{ item.public_ip }} groups=just_created
with_items: ec2.instances
https://riptutorial.com/ 52
The third role: code_deploy
Here, we will provision this instance, which was added to a group called just_created
In this role we are going to use the template_module to transfer a file & write the machine
hostname in it. Now edit my_ansible_project/roles/code_deploy/tasks/main.yml file:
---
- template: src=my_file.txt.j2 dest=/etc/my_file.txt
We will now create an image of the running instance using the ec2_ami module. Move to you
project folder and:
---
- ec2_ami:
instance_id: "{{ instance_id }}"
wait: yes
name: Base_Image
Now, I think you have been wondering how to orchestrate all of these roles. Am I right? If so,
continue reading.
We will write a playbook, composed of three plays: first play applicable on localhost will call our
first two roles, second play applicable on our just_created group. last role will be applicable on
localhost. Why localhost? When we want to manage some AWS ressources, we use our local
machine, as simple as that. Next, we will use a vars file in which we will put our variables:
ansible_key, aws_region, etc...
create infrastructure folder at the top of your project and add a file inside it called aws.yml:
---
aws_region: ap-southeast-2
ansible_key: ansible
instance_type: t2.small
https://riptutorial.com/ 53
So at the top of your project create build_base_image.yml and add this:
---
- hosts: localhost
connection: local
gather_facts: False
vars_files:
- infrastructure/aws.yml
roles:
- ami_find
- { role: ec2_creation, base_image: "{{ ami_ubuntu }}"}
- hosts: just_created
connection: ssh
gather_facts: True
become: yes
become_method: sudo
roles:
- code_deploy
- hosts: localhost
connection: local
gather_facts: False
vars_files:
- infrastructure/aws.yml
roles:
- { role: new_image, instance_id: "{{ id }}"}
That's it, Dont forget to delete your ressources after testing this, or why not create a role to delete
the running instance :-)
Managing AWS resources that scale up and down runs into the limits of the static inventory host
file, that's why we need something dynamic. And that's what the dynamic inventories are for. Let's
start:
Download these ec2.ini and ec2.py files to the your project folder:
cd my_ansible_project
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.py
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.ini
chmod +x ec2.py
Now, export your AWS Secret and Access key as environnment variables:
export AWS_ACCESS_KEY_ID='ABCDEFGHIJKLM'
export AWS_SECRET_ACCESS_KEY='NOPQRSTUVWXYZ'
To use the ec2.py script we need the Python AWS SDK, boto so you need to install it:
https://riptutorial.com/ 54
sudo pip install boto
To test if everything is good, try executing the ec2.py by listing your resources:
./ec2.py --list
{
"_meta": {
"hostvars": {}
}
}
Now we want to use the dynamic inventory along with our static hosts file. First, create a folder
called inventory, add ec2.py, ec2.ini and our hosts file to it then tell Ansible to use that folder as an
inventory file:
mkdir inventory
mv ec2.py inventory/ec2.py
mv ec2.ini inventory/ec2.ini
mv hosts inventory/hosts
Next we should define project level configuration for Ansible by creating an Ansible config file in
your project folder called ansible.cfg and adding this:
[defaults]
hostfile = inventory
[ssh_connection]
pipelining = False
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o StrictHostKeyChecking=no
Next we need to configure Ansible to use an SSH key to authenticate access to our EC2
instances. Using an SSH agent is the best way to authenticate with resources, as this makes it
easier to manage keys:
ssh-agent bash
ssh-add ~/.ssh/keypair.pem
That's it! If you followed this, you can test it by using the ping module and then, you will see your
running instances that have been configured to use your key responding with pong:
https://riptutorial.com/ 55
Chapter 20: Using Ansible with OpenStack
Introduction
OpenStack is an open-source software platform for cloud computing. Linux instances can be
launched/stopped manualy using the graphical web interface or automated thanks to ansible's
openstack cloud module.
Configuring ansible can be tricky, but once well configured using it is really easy and powerfull for
testing and Continuous Integration environment.
Parameters
parameters Comments
auth_url: https://openstack-
use V2.0 URL
identity.mycompany.com/v2.0
Remarks
• We put the authentication URL directly in the playbook, not in a variable. URL used in in vars
must be escaped.
• Be carefull with authentication URL version use V2.0 instead of V3 in https://openstack-
identity.mycompany.com/v2.0.
• In yml files, be very carefull when copy/paste from browser. Check twice the spaces as they
are taken into account.
• More details at: http://docs.ansible.com/ansible/list_of_cloud_modules.html#openstack
Examples
https://riptutorial.com/ 56
Check your Ansible version
• ansible >=2.0
• python >=2.6
• shade module for python
$ansible --version
ansible 2.2.0.0
$python --version
Python 2.7.5
Note : if you use a company proxy, it's always useful to know the right pip synthax
Authentication informations can be found in the openstack.rc file. this file can be downloaded
using the OpenStack webinterface in [access and security/API Access].
$cat openstack.rc
#!/bin/bash
https://riptutorial.com/ 57
# only for the Identity API served through keystone.
export OS_AUTH_URL=https://openstack-identity.mycompany.com/v3
Beware of authentication API version. By default v3 is activated, but ansible needs the v2.0. We
get the url and set V2.0 instead of V3 : https://openstack-identity.mycompany.com/v2.0
VM informations
Create an instance using the OpenStack web interface and get the name for image, flavor, key,
network, security group.
$vi ./group_vars/all
# Authentication
AuthUserName: UserTrainingIC
AuthPassword: PasswordTrainingIC
TenantName: TrainingIC
# VM infos
ImageName: CentOS-7-x86_64-GenericCloud-1607
FlavorName: m1.1cpu.1gb
InfraKey: KeyTrainingIC
NetworkName: NetPrivateTrainingIC
SecurityGroup: default
https://riptutorial.com/ 58
$vi launch_compute.yml
- name: launch a compute instance
hosts: localhost
gather_facts: False
tasks:
- name: Create and launch the VM
os_server:
auth:
auth_url: https://openstack-identity.mycompany.com/v2.0
username: "{{ AuthUserName }}"
password: "{{ AuthPassword }}"
project_name: "{{ TenantName }}"
state: present
validate_certs: False
name: "MyOwnPersonalInstance"
image: "{{ ImageName }}"
key_name: "{{ InfraKey }}"
timeout: 200
flavor: "{{ FlavorName }}"
security_groups: "{{ SecurityGroup }}"
network: "{{ NetworkName }}"
auto_ip: yes
$ ansible-playbook -s launch_compute.yml
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [launch a compute instance] ***********************************************
TASK [Create and launch the VM] ************************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0
$vi get_compute_info.yml
- name: Get and print instance IP
hosts: localhost
gather_facts: False
tasks:
- name: Get VM infos
os_server_facts:
auth:
auth_url: https://openstack-identity.mygroup/v2.0
username: "{{ AuthUserName }}"
password: "{{ AuthPassword }}"
project_name: "{{ TenantName }}"
validate_certs: False
server: "MyOwnPersonalInstance"
$ansible-playbook -s get_compute_info.yml
https://riptutorial.com/ 59
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [Get and print instance IP] ***********************************************
TASK [Get VM IP] ***************************************************************
ok: [localhost]
TASK [Affichage] ***************************************************************
ok: [localhost] => {
"openstack_servers": [
{
"OS-DCF:diskConfig": "MANUAL",
"OS-EXT-AZ:availability_zone": "fr",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
[...]
"volumes": []
}
]
}
This is very verbose. Lots of information is displayed. Usually only the IP address is needed to
access the new instance via SSH.
Instead of printing all the informations, we print only IP address of the first instance whose name is
"MyOwnPersonalInstance". It's usually all we need.
$vi get_compute_ip.yml
- name: Get and print instance IP
hosts: localhost
gather_facts: False
tasks:
- name: Get VM infos
os_server_facts:
auth:
auth_url: https://openstack-identity.mycompany.com/v2.0
username: "{{ AuthUserName }}"
password: "{{ AuthPassword }}"
project_name: "{{ TenantName }}"
validate_certs: False
server: "MyOwnPersonalInstance"
- name: Dump IP
debug:
var: openstack_servers[0].interface_ip
To delete our instance, reuse the os_server command with all authentication information and
simply replace ' state: present' by ' state: absent'.
$vi stop_compute.yml
- name: launch a compute instance
https://riptutorial.com/ 60
hosts: localhost
gather_facts: False
tasks:
- name: Create and launch the VM
os_server:
auth:
auth_url: https://openstack-identity.mygroup/v2.0
username: "{{ AuthUserName }}"
password: "{{ AuthPassword }}"
project_name: "{{ ProjectName }}"
state: absent
validate_certs: False
name: "{{ TPUser }}"
timeout: 200
https://riptutorial.com/ 61
Credits
S.
Chapters Contributors
No
Ansible group
3 mrtuovinen
variables
Ansible: Loops and A K, Chu-Siang Lai, Jordan Anderson, marx, Mike, mrtuovinen,
7
Conditionals Nick, Rob H, wolfaviators
Become (Privilege
8 Jordan Anderson, Willian Paixao
Escalation)
How To Create A
DreamHost Cloud
11 Stefano Maffulli
Server From An
Ansible Playbook
Introduction to
13 32cupo, Abdelaziz Dabebi, ydaetskcoR
playbooks
https://riptutorial.com/ 62
Using Ansible with
18 Amazon Web Abdelaziz Dabebi, another geek, ydaetskcoR
Services
https://riptutorial.com/ 63