Ansible
An introduction to configuration and state management
How to Navigate Through this Presentation
- Space to progress incrementally
- Arrow keys to navigate
- Alt + Click to zoom in on a slide (Again to zoom out)
- Orange text is a link
Building Information
Important stuff like bathrooms/emergency exits and meeting points for anyone that hasn't visited before.
What we'll cover
- What is Ansible
- Foundation principals (building blocks)
- Making it work (Playbooks and Modules)
- Making it right (Refactoring)
- Troubleshooting and Validation
- Real World Examples (Time Allowing)
Questions!
What Is Ansible
Configuration Management
Stores the state of infrastructure as code.
- Reproducible
- Reduces Human Error
- Easier to find issues (peer review/automated testing/static analysis)
- Auditing (Who/What/When/Where)
Why not just use a script?
- Cross Platform
- Automation ready
- Templating and Variables
- Modular and Reusable
- Idempotence (State management)
- "Intuitive"
Why Ansible?
Ansible vs. other CM tools
Positives:
- Agentless
- SSH as connection protocol
- Playbooks can be run from anywhere, not just the "Master" server/s
- "Batteries Included"
Negatives:
- Performance on large scale
- Windows not supported as control.
Environment Setup
Topology
Control Machine Setup
- Installation via most package managers
- Also available via python-pip
- Windows as a control machine is not supported (except via Docker)
pip install ansible
Installation on Ubuntu
sudo apt-get update
sudo apt-get install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt-get install ansible -y
ansible --version # Should be > 2.9
Remote Host Setup
ssh-keygen -f $HOME/.ssh/id_rsa -t rsa -b 4096 -N ""
ssh-copy-id $REMOTE_HOST
ssh $REMOTE_HOST
- Ansible requires SSH access to remote hosts
- Can be password/less
- Possible to run in pull-mode
- Requires "python-simplejson" package installed
ansible all -b -K -m raw -a "apt-get install -y python-simplejson"
The Basics
Before we dive in
Ansible's configuration lives in "/etc/ansible".
sudo chown -R training:training /etc/ansible
In order to change files in this directory, we need to have permissions to do so
Inventory
- /etc/ansible/hosts
- Where we define the hosts we want to work on
- Can be static or dynamic
- Comprised of groups of entries of hostnames
- Can be in INI or YAML format
- Can also include extra detail if needed
ansible --list-hosts all
Hosts File Layout
- One host per line
- Either hostname or IP
- Can be grouped with a header in square brackets
[loadbalancer]
john-proxy01
[webserver]
john-web01
john-web02
[database]
john-database01
[control]
john-control01
More Hosts File Config
- Parent/Children Relationships
[demo_app:children]
loadbalancer
webserver
database
Variables
[control]
john-control01 ansible_connection=local
[loadbalancer]
johns-proxy01 # Is the same as
johns-proxy01.$DOMAIN # Is also the same as
192.168.0.10 # Is also the same as
some_stupid_hostname ansible_host=johns-proxy01
Referencing groups
- Default groups (all/ungrouped)
- Globbing works too!
- Reference your groups
- Individual hosts
- Multiple groups
- Indexing
- Negation
ansible --list-hosts all
ansible --list-hosts "*"
ansible --list-hosts control
ansible --list-hosts john-proxy01
ansible --list-hosts control,webserver
ansible --list-hosts webserver[0]
ansible --list-hosts \!control
Ad-hoc Commands
- Simple commands that complete a single action
- Great to learn how Ansible works
- Also for commands that don't need to run twice
- There is usually a better way (playbooks)
ansible all -m ping
ansible all -m command -a "hostname" # Is the same as
ansible all -a "hostname"
ansible all -a "/bin/false"
ansible $HOSTS -m $MODULE -a $MODULE_ARGUMENTS
More on Ad-Hoc
- More options are available
ansible control -b -K -m copy -a \
"dest=/etc/sudoers.d/sudo_nopasswd mode=0660 \
content='%sudo ALL=(ALL) NOPASSWD: ALL' validate='visudo -cf %s'"
ansible all -b -m ping
ansible control -m copy -a "dest=/tmp/foo content=''" -C
ansible control -m copy -a "dest=/tmp/foo content=''"
ansible control -m copy -a "dest=/tmp/foo content=''"
ansible control -m ping -b
- Let's fix that
The Playbook
What are Playbooks?
- The meat of Ansible!
- Everything is either in or referenced by a playbook
- Usually live in /etc/ansible/playbooks
- A way to group and order tasks
- Written in YAML
- Consists of the target hosts and the tasks you would like to run against those hosts.
YAML?
- File optionally starts with "---" and ends with "..."
- Consists of (In Ansible) dictionaries and lists
Lists with '-'
- 2 spaces on each new line
Dictionaries are denoted with ':'
---
Fruits:
- Apple
- Banana
- Pear
---
Key: Value
---
foo: "{{ variable }}"
Variables like '"{{ }}"'
My First Playbook
mkdir playbooks
vim playbooks/hostname.yml
ansible-playbook playbooks/hostname.yml
---
- hosts: all
tasks:
- name: Print server hostname
command: hostname
ansible all -a "hostname"
Static Checks
- "--syntax-check" checks a YAML file for compatibility with Ansible
- Doesn't run the playbook
- Great for use in CI testing
ansible-playbook playbooks/hostname.yml --syntax-check
Make It Work
But How?
As we write a playbook to provision each application, we'll approach it in 4 steps:
- Packages
- Service Management
- System Configuration
- Application Configuration
And at each step, we'll approach it in the same way:
- Pick a module
- Find what arguments it needs
- Try it!
Anatomy of a Module
Lets take a look at the 'apt' module as an example:
- Synopsis
- System Requirements
- Options
- Name
- Required?
- Default?
- Choices
- Comments
- Examples
Packages - apt
---
- hosts: loadbalancer
tasks:
- name: Install nginx
apt:
name: nginx
state: present
playbooks/loadbalancer.yml
---
- hosts: database
tasks:
- name: Install mysql-server
apt:
name: mysql-server
state: present
playbooks/database.yml
ansible-playbook playbooks/loadbalancer.yml
Become
playbooks/loadbalancer.yml
playbooks/database.yml
ansible-playbook playbooks/loadbalancer.yml
ansible-playbook playbooks/database.yml
---
- hosts: loadbalancer
become: true
tasks:
- name: Install nginx
apt:
name: nginx
state: present
---
- hosts: database
become: true
tasks:
- name: Install mysql-server
apt:
name: mysql-server
state: present
loop
playbooks/webserver.yml
ansible-playbook playbooks/webserver.yml
---
- hosts: webserver
become: true
tasks:
- name: Install apache2
apt:
name: apache2
state: present
---
- hosts: webserver
become: true
tasks:
- name: Install apache2
apt:
name: apache2
state: present
- name: Install libapache2-mod-wsgi
apt:
name: libapache2-mod-wsgi
state: present
- name: Install python-pip
apt:
name: python-pip
state: present
- name: Install python-virtualenv
apt:
name: python-virtualenv
state: present
- name: Install python-mysqldb
apt:
name: python-mysqldb
state: present
---
- hosts: webserver
become: true
tasks:
- name: Install web server packages
apt:
name: "{{ item }}"
state: present
loop:
- apache2
- libapache2-mod-wsgi
- python-pip
- python-virtualenv
- python-mysqldb
- virtualenv
Services
playbooks/loadbalancer.yml
---
- hosts: loadbalancer
become: true
tasks:
- name: Install nginx
apt:
name: nginx
state: present
---
- hosts: loadbalancer
become: true
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- name: Ensure nginx service is started
service:
name: nginx
state: started
enabled: yes
ansible-playbook playbooks/loadbalancer.yml
wget -qO- http://johns-proxy01
curl Is Better
playbooks/control.yml
---
- hosts: control
become: true
tasks:
- name: Install curl
apt:
name: curl
state: present
ansible-playbook playbooks/control.yml
curl http://johns-proxy01
Give this a go:
- Create a "control" playbook, which will apply to your ansible (local) machine.
- In this playbook, include a task to install the package "curl".
Finishing Services
playbooks/webserver.yml
ansible-playbook playbooks/database.yml
ansible-playbook playbooks/webserver.yml
---
- hosts: webserver
become: true
tasks:
- name: Install web server packages
apt:
name: "{{ item }}"
state: present
loop:
- apache2
- libapache2-mod-wsgi
- python-pip
- python-virtualenv
- name: Ensure apache2 service is started
service:
name: apache2
state: started
enabled: yes
---
- hosts: database
become: true
tasks:
- name: Install mysql-server
apt:
name: mysql-server
state: present
- name: Ensure mysql service is started
service:
name: mysql
state: started
enabled: yes
playbooks/database.yml
curl http://johns-web01
curl http://johns-web02
Apache2 Module
playbooks/webserver.yml
---
- hosts: webserver
become: true
tasks:
- name: Install web server packages
apt:
name: "{{ item }}"
state: present
loop:
- apache2
- libapache2-mod-wsgi
- python-pip
- python-virtualenv
- name: Ensure apache2 service is started
service:
name: apache2
state: started
enabled: yes
- name: Ensure mod-wsgi module is enabled
apache2_module:
name: wsgi
state: present
Handlers
playbooks/webserver.yml
tasks:
- name: Ensure mod-wsgi module is enabled
apache2_module:
name: wsgi
state: present
handlers:
- name: Restart apache2
service:
name: apache2
state: restarted
tasks:
- name: Ensure mod-wsgi module is enabled
apache2_module:
name: wsgi
state: present
notify: Restart apache2
handlers:
- name: Restart apache2
service:
name: apache2
state: restarted
ansible-playbook playbooks/webserver.yml
Prepare Files
mkdir -p demo/app
demo/app/demo.wsgi
activate_this = '/var/www/demo/.venv/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
import os
os.environ['DATABASE_URI'] = 'mysql://demo:demo@$IP_ADDRESS/demo'
import sys
sys.path.insert(0, '/var/www/demo')
from demo import app as application
demo/demo.conf
<VirtualHost *>
WSGIDaemonProcess demo threads=5
WSGIScriptAlias / /var/www/demo/demo.wsgi
<Directory /var/www/demo>
WSGIProcessGroup demo
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
demo/app/demo.py
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
import os, socket
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URI']
db = SQLAlchemy(app)
hostname = socket.gethostname()
@app.route('/')
def index():
return 'Hello, from sunny %s!\n' % hostname
@app.route('/db')
def dbtest():
try:
db.create_all()
except Exception as e:
return e.message + '\n'
return 'Database Connected from %s!\n' % hostname
if __name__ == '__main__':
app.run()
demo/app/requirements.txt
Flask==0.10.1
Flask-SQLAlchemy==2.0
Change the $IP_ADDRESS
Copy
playbooks/webserver.yml
tasks:
- name: Copy demo application source
copy:
src: ../demo/app/
dest: /var/www/demo
mode: 0755
notify: Restart apache2
ansible-playbook playbooks/webserver.yml
tasks:
- name: Copy demo application source
copy:
src: ../demo/app/
dest: /var/www/demo
mode: 0755
notify: Restart apache2
- name: Copy apache2 virtualhost configuration
copy:
src: ../demo/demo.conf
dest: /etc/apache2/sites-available
mode: 0755
notify: Restart apache2
pip
playbooks/webserver.yml
tasks:
- name: Install python-pip dependencies
pip:
requirements: /var/www/demo/requirements.txt
virtualenv: /var/www/demo/.venv
notify: Restart apache2
ansible-playbook playbooks/webserver.yml
File
playbooks/webserver.yml
tasks:
- name: De-activate default apache site
file:
path: /etc/apache2/sites-enabled/000-default.conf
state: absent
notify: Restart apache2
- name: Activate demo apache site
file:
src: /etc/apache2/sites-available/demo.conf
dest: /etc/apache2/sites-enabled/demo.conf
state: link
notify: Restart apache2
ansible-playbook playbooks/webserver.yml
curl http://johns-web01
curl http://johns-web02
curl http://johns-proxy01
Jinja2 Templates
templates/nginx.conf.j2
mkdir templates
vim templates/nginx.conf.j2
upstream demo {
server johns-web01;
server johns-web02;
}
server {
listen 80;
location / {
proxy_pass http://demo;
}
}
upstream demo {
{# Enter a server line for each host in the webservers group in Ansible #}
{% for server in groups.webserver %}
server {{ server }};
{% endfor %}
}
server {
listen 80;
location / {
proxy_pass http://demo;
}
}
Template Module
playbooks/loadbalancer.yml
---
tasks:
- name: Configure nginx site
template:
src: ../templates/nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
- name: De-activate default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart nginx
- name: Activate demo nginx site
file:
src: /etc/nginx/sites-available/demo
dest: /etc/nginx/sites-enabled/demo
state: link
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
ansible-playbook playbooks/loadbalancer.yml
curl http://johns-proxy01
#Run it a couple times!
---
tasks:
- name: Configure nginx site
template:
src: ../templates/nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
- name: De-activate default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart nginx
- name: Activate demo nginx site
file:
src: /etc/nginx/sites-available/demo
dest: /etc/nginx/sites-enabled/demo
state: link
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
lineinfile
playbooks/database.yml
ansible-playbook playbooks/database.yml
tasks:
- name: Ensure mysql is listening on all addresses
lineinfile:
dest: /etc/mysql/mysql.conf.d/mysqld.cnf
regexp: "^bind-address"
line: "bind-address = 0.0.0.0"
notify: Restart mysql
handlers:
- name: Restart mysql
service:
name: mysql
state: restarted
ssh -t johns-database01 "grep -R bind-address /etc/mysql"
curl http://johns-app01/db
curl http://johns-proxy01/db
mysql_db
playbooks/database.yml
ansible-playbook playbooks/database.yml
---
- hosts: database
become: true
tasks:
- name: Install mysql-server
apt:
name: mysql-server
state: present
update_cache: yes
---
- hosts: database
become: true
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
update_cache: yes
loop:
- mysql-server
- python-mysqldb
Give this a go:
- Modify the apt task in the database playbook to include the package "python-mysqldb" in the form of a loop.
- name: Create demo database
mysql_db:
name: demo
state: present
- name: Create demo user
mysql_user:
name: demo
password: demo
priv: demo.*:ALL
host: '%'
state: present
curl http://johns-proxy01/db
It Lives!
Make It Right
What's Not Right?
- One playbook for each tier
- Playbooks specific to our site
- What if another team wants to use our code?
- How much code could they use for their site?
- What about different port numbers?
- How many teams do you have?
- What if a vulnerability needs fixing for all teams?
Roles
- A role is a folder structure where we function specific
- Tasks
- Handlers
- Files
- Templates
- /etc/ansible/roles/$ROLE
- Compose our infrastructure as a collection of roles
- Allows for easier code reuse
- Know where to look to make a change/fix an issue
Creating Roles - galaxy init
ansible-galaxy init roles/control
ansible-galaxy init roles/nginx
Give this a go:
- Create skeleton roles for the rest of the infrastructure
ansible-galaxy init roles/control
ansible-galaxy init roles/nginx
ansible-galaxy init roles/apache2
ansible-galaxy init roles/mysql
ansible-galaxy init roles/demo_app
Importing Roles - Tasks
roles/control/tasks/main.yml
---
- name: Install curl
apt:
name: curl
state: present
playbooks/control.yml
---
- hosts: control
become: true
tasks:
- name: Install curl
apt:
name: curl
state: present
---
- hosts: control
become: true
roles:
- control
Importing Roles - Handlers
roles/mysql/tasks/main.yml
playbooks/database.yml
---
- hosts: database
become: true
roles:
- mysql
---
- hosts: database
become: true
tasks:
- name: Install packages
...
handlers:
- name: Restart mysql
service:
name: mysql
state: restarted
roles/mysql/handlers/main.yml
---
- name: Restart mysql
service:
name: mysql
state: restarted
---
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- mysql-server
- python-mysqldb
- name: Ensure mysql is listening on all addresses
lineinfile:
dest: /etc/mysql/mysql.conf.d/mysqld.cnf
regexp: "^bind-address"
line: "bind-address = 0.0.0.0"
notify: Restart mysql
- name: Ensure mysql service is started
service:
name: mysql
state: started
enabled: yes
- name: Create demo database
mysql_db:
name: demo
state: present
- name: Create demo user
mysql_user:
name: demo
password: demo
priv: demo.*:ALL
host: '%'
state: present
nginx Role
roles/nginx/tasks/main.yml
---
- name: Install nginx
apt:
name: nginx
state: present
- name: Configure nginx site
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
- name: De-activate default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart nginx
- name: Activate demo nginx site
file:
src: /etc/nginx/sites-available/demo
dest: /etc/nginx/sites-enabled/demo
state: link
notify: Restart nginx
- name: Ensure nginx service is started
service:
name: nginx
state: started
enabled: yes
playbooks/loadbalancer.yml
---
- hosts: loadbalancer
become: true
roles:
- nginx
roles/nginx/handlers/main.yml
---
- name: Restart nginx
service:
name: nginx
state: restarted
Give this a go:
- Import the loadbalancer playbook into a role
- Modify the playbook file to include the new role
Importing Roles - Templates
roles/nginx/tasks/main.yml
- name: Configure nginx site
template:
src: ../templates/nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
- name: Configure nginx site
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
mv templates/nginx.conf.j2 roles/nginx/templates
apache2 Role
roles/apache2/tasks/main.yml
roles/demo_app/tasks/main.yml
- name: Install web server packages
apt:
name: "{{ item }}"
state: present
loop:
- libapache2-mod-wsgi
- python-pip
- python-virtualenv
- name: Install web server packages
apt:
name: "{{ item }}"
state: present
loop:
- apache2
- name: Ensure apache2 service is started
service:
name: apache2
state: started
enabled: yes
- name: Ensure mod-wsgi module is enabled
apache2_module:
name: wsgi
state: present
notify: Restart apache2
- name: Copy demo application source
copy:
src: demo/app/
dest: /var/www/demo
mode: 0755
notify: Restart apache2
- name: Copy apache2 virtualhost configuration
copy:
src: demo/demo.conf
dest: /etc/apache2/sites-available
mode: 0755
notify: Restart apache2
- name: Install python-pip dependencies
pip:
requirements: /var/www/demo/requirements.txt
virtualenv: /var/www/demo/.venv
notify: Restart apache2
- name: Activate demo apache site
file:
src: /etc/apache2/sites-available/demo.conf
dest: /etc/apache2/sites-enabled/demo.conf
state: link
notify: Restart apache2
- name: De-activate default apache site
file:
path: /etc/apache2/sites-enabled/000-default.conf
state: absent
notify: Restart apache2
apache2 Role Continued
playbooks/webserver.yml
---
- hosts: webserver
become: true
roles:
- apache2
- demo_app
roles/apache2/handlers/main.yml
---
- name: Restart apache2
service:
name: apache2
state: restarted
roles/demo_app/handlers/main.yml
---
- name: Restart apache2
service:
name: apache2
state: restarted
Importing Roles - Files
mv demo/* roles/demo_app/files
roles/demo_app/tasks/main.yml
- name: Copy demo application source
copy:
src: app/
dest: /var/www/demo
mode: 0755
notify: Restart apache2
- name: Copy apache2 virtualhost configuration
copy:
src: demo.conf
dest: /etc/apache2/sites-available
mode: 0755
notify: Restart apache2
Site Playbook - Include
playbooks/site.yml
---
- hosts: control
become: true
roles:
- control
- hosts: database
become: true
roles:
- mysql
- hosts: loadbalancer
become: true
roles:
- nginx
- hosts: webserver
become: true
roles:
- apache2
- demo_app
ansible-playbook playbooks/site.yml
---
- import_playbook: database.yml
- import_playbook: webserver.yml
- import_playbook: loadbalancer.yml
Refactoring with Variables
Facts
roles/mysql/tasks/main.yml
- name: Ensure mysql is listening on all addresses
lineinfile:
dest: /etc/mysql/my.cnf
regexp: "^bind-address"
line: "bind-address = 0.0.0.0"
notify: Restart mysql
ansible -m setup john-database01
ansible-playbook playbooks/database.yml
- name: Ensure mysql is listening on all addresses
lineinfile:
dest: /etc/mysql/my.cnf
regexp: "^bind-address"
line: "bind-address = {{ ansible_default_ipv4.address }}"
notify: Restart mysql
Role Defaults
roles/mysql/tasks/main.yml
- name: Create demo database
mysql_db:
name: demo
state: present
- name: Create demo user
mysql_user:
name: demo
password: demo
priv: demo.*:ALL host='%'
state: present
- name: Create database
mysql_db:
name: demo
state: present
- name: Create user
mysql_user:
name: demo
password: demo
priv: demo.*:ALL host='%'
state: present
- name: Create database
mysql_db:
name: "{{ db_name }}"
state: present
- name: Create user
mysql_user:
name: demo
password: demo
priv: "{{ db_name }}.*:ALL host='%'"
state: present
- name: Create database
mysql_db:
name: "{{ db_name }}"
state: present
- name: Create user
mysql_user:
name: "{{ db_user }}"
password: "{{ db_password }}"
priv: "{{ db_name }}.*:ALL"
host: "{{ db_host }}"
state: present
roles/mysql/defaults/main.yml
---
db_name: mydb
db_user: mydbuser
db_password: mydbpass
db_host: mydbhost
group_vars
ansible-playbook playbooks/database.yml
group_vars/all/vars.yml
---
db_name: demo
db_user: demo
db_password: demo
db_host: "%"
mkdir -p group_vars/all
vim group_vars/all/vars.yml
group_vars - Continued
group_vars/all/vars.yml
---
db_name: demo
db_user: demo
db_password: demo
db_host: "%"
site_name: demo
Give this a go:
- Replace the references to our site "demo" in the loadbalancer role with a variable:
- Three references in the tasks file
- Two references in the template
- You can call the variable whatever you want to!
- Remember to add definitions for this variable in group_vars
upstream {{ site_name }} {
{% for server in groups.webserver %}
server {{ server }};
{% endfor %}
}
server {
listen 80;
location / {
proxy_pass http://{{ site_name }};
}
}
- name: Configure demo nginx site
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/demo
mode: 0644
notify: Restart nginx
- name: De-activate default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart nginx
- name: Activate demo nginx site
file:
src: /etc/nginx/sites-available/demo
dest: /etc/nginx/sites-enabled/demo
state: link
notify: Restart nginx
roles/nginx/tasks/main.yml
- name: Configure nginx site
template:
src: nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ site_name }}"
mode: 0644
notify: Restart nginx
- name: De-activate default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Restart nginx
- name: Activate nginx site
file:
src: "/etc/nginx/sites-available/{{ site_name }}"
dest: "/etc/nginx/sites-enabled/{{ site_name }}"
state: link
notify: Restart nginx
roles/nginx/templates/nginx.conf.j2
upstream demo {
{% for server in groups.webserver %}
server {{ server }};
{% endfor %}
}
server {
listen 80;
location / {
proxy_pass http://demo;
}
}
ansible-vault
ansible-vault create group_vars/all/vault.yml
group_vars/all/vault.yml
...
vault_password_file = ~/.ansible/.vault_password_file
...
~/.ansible_vault_password
really_super_secure_password_here
ansible.cfg
---
vault_db_password: demo
group_vars/all/vars.yml
---
db_name: demo
db_user: demo
db_password: "{{ vault_db_password }}"
db_host: "%"
Troubleshooting
Troubleshooting
--check (-C)/--diff (-D)
- "--check" does a dry run of the playbook (doesn't change anything).
- Great for continuous integration checks before merging new code into master branch
- "--diff" shows the difference in the configuration
- Most effective when used with --check
--verbose (-v)
ansible-playbook playbooks/control.yml -vvvv
- Simply shows more verbose output of the tasks as they are run.
- More verbose output can be requested by adding extra "v" characters:
- "-v" shows stdout and stderr
- "-vv" does the same as -v (?)
- "-vvv" shows the commands run by Ansible
- "-vvvv" shows connection debugging
We're all done!
We're all done!
Extra Topics
- Ansible Galaxy
- Real world examples
Time Allowing
Ansible
By mwadman
Ansible
An introduction to configuration and state management using Ansible.
- 948