SlideShare a Scribd company logo
blue-bag
ANSIBLE
&
DRUPAL
MEDICINE SHOW

(PART TWO)
DRUPAL BRISTOL CAMP JULY 2015
GEORGE BOOBYER

BLUE-BAG
blue-bag
BLUE-BAG
GEORGE BOOBYER

DRUPAL: iAUGUR

GEORGE@BLUE-BAG.COM
TWITTER: iBLUEBAG
www.blue-bag.com
Established in 2000

The year before Drupal 1.0
blue-bag
ANSIBLE & DRUPAL
Ansible
Basics
Remedies
Drupal
Take homes
Questions
blue-bag
ANSIBLE & DRUPAL: MEDICINE SHOW
w
Special formula

Requires prescription

Ingredients:

Forked Roles
Custom Roles

Custom Inventory & Vars

Featured multistage Playbook
w
Limited Edition

Ingredients:

multistage playbook

Multiple plays, Forked Roles
Custom Roles, Vault
Plugins & filters, 

Complex Inventory & Vars
w
Original Recipe

Available over the counter

Ingredients:

Galaxy Roles
Custom Vars

Simple Playbook

Documentation
blue-bag
ANSIBLE & DRUPAL: TOP 10
1. Docker
2. Kubernetes
3. Taiga
4. Apache Mesos
5. OpenStack
6. Ansible
7. ownCloud
8. Apache Hadoop
9. Drupal
10.OpenDaylight
http://opensource.com/business/14/12/
top-10-open-source-projects-2014
Top 10 open source projects in 2014
blue-bag
ANSIBLE BASICS
Agentless
Uses SSH
YAML for configuration
Modules
Open source
not just provisioning
“Ansible is a radically simple IT automation system. It handles
configuration-management, application deployment, cloud
provisioning, ad-hoc task-execution, and multinode
orchestration.
Python
Extensible
blue-bag
ANSIBLE GOALS
blue-bag
ANSIBLE GOALS
Don’t log into servers
blue-bag
ANSIBLE GOALS
Don’t log into servers
No need for bash scripts
blue-bag
ANSIBLE GOALS
Don’t log into servers
No need for bash scripts
Document build and configuration

(in source control)
blue-bag
ANSIBLE GOALS
Don’t log into servers
No need for bash scripts
Document build and configuration

(in source control)
Idempotent (idem-what’s-that-again?)
blue-bag
ANSIBLE GOALS
Don’t log into servers
No need for bash scripts
Document build and configuration

(in source control)
Idempotent (idem-what’s-that-again?)
Maintain configuration across inventory
blue-bag
ANSIBLE GOALS
Don’t log into servers
No need for bash scripts
Document build and configuration

(in source control)
Idempotent (idem-what’s-that-again?)
Maintain configuration across inventory
Retain & share knowledge
blue-bag
GET THE BOOK!
leanpub.com/
ansible-for-devops
blue-bag
ANSIBLE DOCUMENTATION
Github - Code and examples:

https://github.com/ansible/ansible

Ansible Documentation: http://docs.ansible.com/
Ansible best practices:

http://docs.ansible.com/playbooks_best_practices.html
Ansible Galaxy: - https://galaxy.ansible.com

Ansible Project - Google Groups:

https://groups.google.com/forum/#!forum/ansible-project
blue-bag
ANSIBLE BASICS
blue-bag
ANSIBLE BASICS
Modules
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
Autodiscovery
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
Autodiscovery
Tags
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
Autodiscovery
Tags
Variables
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
Autodiscovery
Tags
Variables
Vault
blue-bag
ANSIBLE BASICS
Modules
Vagrant

local setup
Ansible command
Playbooks
Inventory
Limiting tasks
Roles / Tasks
Autodiscovery
Tags
Variables
Vault
Testing
blue-bag
ANSIBLE BASICS: MODULES
Command
Shell
Lineinfile
File
Copy
Template
Apt / Yum / Homebrew
Unarchive
apache2_module
htpasswd
Git
Cloud: RAX, DO, EC3
blue-bag
ANSIBLE BASICS: MODULES
$ ansible-docs -l
…
alternatives Manages alternative programs for common commands
apache2_module enables/disables a module of the Apache2 webserver
apt Manages apt-packages
apt_key Add or remove an apt key
apt_repository Add and remove APT repositories
apt_rpm apt_rpm package manager
assemble Assembles a configuration file from fragments
assert Fail with custom message
at Schedule the execution of a command or script file via
the at command.
authorized_key Adds or removes an SSH authorized key
…
patch Apply patch files using the GNU patch tool.
blue-bag
ANSIBLE BASICS: MODULES
$ ansible-docs -s patch

- name: Apply patch files using the GNU patch tool.
action: patch
basedir # Path of a base directory in which the patch file will be applied. May be
omitted when `dest' option is specified, otherwise required.
dest # Path of the file on the remote machine to be patched. The names of the
files to be patched are usually taken from the patch file, but if there's
just one file to be patched it can specified with this
remote_src # If False, it will search for src at originating/master machine, if True it
will go to the remote/target machine for the src. Default is False.
src= # Path of the patch file as accepted by the GNU patch tool.
strip # Number that indicates the smallest prefix containing leading slashes that
will be stripped from each file name found in the patch file. For more
information see the strip parameter of the GNU patch to
Note old syntax!
- name: Apply patch files using the GNU patch tool
patch:
src: /tmp/index.html.patch
dest: /var/www/index.html
blue-bag
ANSIBLE / VAGRANT
"## demo
   "## Vagrantfile
   %## hosts
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.ssh.insert_key = false
config.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "256"]
end
# Application server 1.
config.vm.define "app1" do |app|
app.vm.hostname = "bbdemo-app1.dev"
app.vm.box = "chef/debian-7.4"
app.vm.network :private_network, ip: "192.168.100.120"
end
# Application server 2.
config.vm.define "app2" do |app|
app.vm.hostname = "bbdemo-app2.dev"
app.vm.box = "chef/debian-7.4"
app.vm.network :private_network, ip: "192.168.100.121"
end
# Database server.
config.vm.define "db" do |db|
db.vm.hostname = "bbdemo-db.dev"
db.vm.box = "chef/debian-7.4"
db.vm.network :private_network, ip: "192.168.100.122"
end
end
# Application servers
[webservers]
192.168.100.120
192.168.100.121
# Database server
[dbservers]
192.168.100.122
# Group 'multi' with all servers
[multi:children]
app
db
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
blue-bag
ANSIBLE BASICS
$ ansible all -i hosts -a "free -m" -u vagrant
blue-bag
ANSIBLE BASICS
$ ansible all -i hosts -a "free -m" -u vagrant
192.168.100.121 | success | rc=0 >>
total used free shared buffers cached
Mem: 244 74 170 0 6 34
-/+ buffers/cache: 32 212
Swap: 767 0 767
192.168.100.120 | success | rc=0 >>
total used free shared buffers cached
Mem: 244 74 170 0 6 34
-/+ buffers/cache: 32 212
Swap: 767 0 767
192.168.100.122 | success | rc=0 >>
total used free shared buffers cached
Mem: 244 74 170 0 6 34
-/+ buffers/cache: 32 212
Swap: 767 0 767
blue-bag
ANSIBLE BASICS: DEFAULTS
"## demo
   "## Vagrantfile
   "## ansible.cfg
   "## hosts
   %## log
%## ansible.log
[defaults]
log_path = log/ansible.log
hostfile = ./hosts
…
[multi]
ansible_ssh_user=vagrant
…
$ansible all -i hosts -a "free -m" -u vagrant
$ansible all -a "free -m"
blue-bag
ANSIBLE BASICS: SUCCINCT CALLS
$ ansible all -a “date"
192.168.100.122 | success | rc=0 >>
Tue Jan 13 10:14:15 UTC 2015
192.168.100.121 | success | rc=0 >>
Tue Jan 13 10:14:15 UTC 2015
192.168.100.120 | success | rc=0 >>
Tue Jan 13 10:14:15 UTC 2015
blue-bag
ANSIBLE BASICS: CALLING MODULES
$ansible all -s -m apt -a "pkg=ntp state=installed_
update_cache=yes"
192.168.100.121 | success >> {
"changed": true,
"stderr": "",
"stdout": "Reading package lists...nBuilding dependency tree...nReading state
information...nThe following extra packages will be installed:n libopts25nSuggested
packages:n ntp-docnThe following NEW packages will be installed:n libopts25 ntpn0
upgraded, 2 newly installed, 0 to remove and 72 not upgraded.nNeed to get 565 kB/638 kB of
archives.nAfter this operation, 1364 kB of additional disk space will be used.nGet:1
http://mirrors.kernel.org/debian/ wheezy/main ntp amd64 1:4.2.6.p5+dfsg-2+deb7u1 [565 kB]
nFetched 565 kB in 13s (40.8 kB/s)nSelecting previously unselected package libopts25.r
n(Reading database ... r(Reading database ... 5%r(Reading database ... 10%r(Reading
database ... 15%r(Reading database ... 20%r(Reading database ... 25%r(Reading
database ... 30%r(Reading database ... 35%r(Reading database ... 40%r(Reading
database ... 45%r(Reading database ... 50%r(Reading database ... 55%r(Reading
database ... 60%r(Reading database ... 65%r(Reading database ... 70%r(Reading
database ... 75%r(Reading database ... 80%r(Reading database ... 85%r(Reading
database ... 90%r(Reading database ... 95%r(Reading database ... 100%r(Reading
database ... 37414 files and directories currently installed.)rnUnpacking libopts25
(from .../libopts25_1%3a5.12-0.1_amd64.deb) ...rnSelecting previously unselected package
ntp.rnUnpacking ntp (from .../ntp_1%3a4.2.6.p5+dfsg-2+deb7u1_amd64.deb) ...rnProcessing
triggers for man-db ...rnSetting up libopts25 (1:5.12-0.1) ...rnSetting up ntp
(1:4.2.6.p5+dfsg-2+deb7u1) ...rnStarting NTP server: ntpd.rn"
}
blue-bag
ANSIBLE BASICS: THE PLAYBOOK
---
- hosts: all
gather_facts: true
sudo: true
tasks:
- name: NTP | Install NTP
apt:
name: ntp
state: installed
update_cache: yes
cache_valid_time: 3600
"## demo
   "## Vagrantfile
   "## ansible.cfg
   "## hosts
   %## log
%## ansible.log
"## playbook.yml
$ ansible-playbook playbook.yml
$ansible multi -s -m apt -a "pkg=ntp state=installed_ update_cache=yes"
blue-bag
ANSIBLE BASICS: ROLE STRUCTURE
Use Galaxy Init to create roles
blue-bag
ANSIBLE BASICS: ROLE STRUCTURE
$ansible-galaxy init 'myrole'
Use Galaxy Init to create roles
blue-bag
ANSIBLE BASICS: ROLE STRUCTURE
$ansible-galaxy init 'myrole'
"## myrole
"## README.md
"## defaults
& %## main.yml
"## files
"## handlers
& %## main.yml
"## meta
& %## main.yml
"## tasks
& %## main.yml
"## templates
%## vars
%## main.yml
Use Galaxy Init to create roles
blue-bag
ANSIBLE BASICS: PROJECT STRUCTURE
blue-bag
ANSIBLE BASICS: PROJECT STRUCTURE
Use a template for
Ansible projects.
Common structure
Autodiscovery
blue-bag
ANSIBLE BASICS: PROJECT STRUCTURE
https://github.com/iAugur/ansible-playbook-template
Use a template for
Ansible projects.
Common structure
Autodiscovery
blue-bag
ANSIBLE BASICS: PROJECT STRUCTURE
https://github.com/iAugur/ansible-playbook-template
"## LICENSE
"## README.md
"## ansible.cfg
"## common
& "## handlers
& & %## main.yml
& %## vars
& %## main.yml
"## group_vars
& %## all.yml
"## host_vars
& %## local.yml
"## hosts
"## log
"## playbook.yml
"## roles
%## base
"## README.md
"## defaults
& %## main.yml
"## files
"## handlers
& %## main.yml
"## meta
& %## main.yml
"## tasks
& %## main.yml
"## templates
%## vars
%## main.yml
Use a template for
Ansible projects.
Common structure
Autodiscovery
blue-bag
ANSIBLE BASICS: MASTER PLAY
---
#-------------------------- Basic core setup of servers
- include: ./playbooks/play-provision-core.yml
when: do_provision
tags: [provision_core]
#-------------------------- Basic core setup of webservers
- include: ./playbooks/play-provision-webserver.yml
when: do_provision
tags: [provision_webserver]
#-------------------------- Basic core setup of dbservers
- include: ./playbooks/play-provision-dbserver.yml
when: do_provision
tags: [provision_dbserver]
#-------------------------- setup of websites
...
./playbook.yml
blue-bag
ANSIBLE BASICS: HOSTS & GROUPS
blue-bag
# Application servers
[webservers]
192.168.100.120
192.168.100.121
# Database server
[dbservers]
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
ANSIBLE BASICS: HOSTS & GROUPS
blue-bag
# Application servers
[webservers]
192.168.100.120
192.168.100.121
# Database server
[dbservers]
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
# Application servers
[webservers]
bbdemo-app1.dev
bbdemo-app2.dev
192.168.100.120
192.168.100.121
# Database server
[dbservers]
bbdemo-db.dev
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
ANSIBLE BASICS: HOSTS & GROUPS
blue-bag
# Application servers
[webservers]
192.168.100.120
192.168.100.121
# Database server
[dbservers]
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
# Application servers
[webservers]
bbdemo-app1.dev
bbdemo-app2.dev
192.168.100.120
192.168.100.121
# Database server
[dbservers]
bbdemo-db.dev
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
# Application servers
[webservers]
bbdemo-app1.dev
bbdemo-app2.dev
# Database server
[dbservers]
bbdemo-db.dev
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
ANSIBLE BASICS: HOSTS & GROUPS
blue-bag
# Application servers
[webservers]
192.168.100.120
192.168.100.121
# Database server
[dbservers]
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
# Application servers
[webservers]
bbdemo-app1.dev
bbdemo-app2.dev
192.168.100.120
192.168.100.121
# Database server
[dbservers]
bbdemo-db.dev
192.168.100.122
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
# Variables that will be applied to all
servers"
[multi:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/
insecure_private_key
# Application servers
[webservers]
bbdemo-app1.dev
bbdemo-app2.dev
# Database server
[dbservers]
bbdemo-db.dev
# Group 'multi' with all servers
[multi:children]
webservers
dbservers
ANSIBLE BASICS: HOSTS & GROUPS
# ../host_vars/bbdemo-app1.dev
——-
ansible_ssh_host: 192.168.100.120
# ../host_vars/bbdemo-app2.dev
——-
ansible_ssh_host: 192.168.100.121
# ../host_vars/bbdemo-db.dev
——-
ansible_ssh_host: 192.168.100.122
# ../group_vars/all
——-
ansible_ssh_user: vagrant
ansible_ssh_private_key_file: ~/.vagrant.d/
insecure_private_key
# ../group_vars/webservers
——-
http_port: 80
# ../group_vars/dbservers
——-
db_port: 3306
blue-bag
ANSIBLE BASICS: LIMITING HOSTS
List Hosts

$ansible-playbook --list-hosts main.yml
Limit to Host

$ansible-playbook --limit app1 main.yml
Limit to group

$ansible-playbook -l webservers main.yml
Limit to set (note quotes!)

$ansible-playbook -l 'multi:!webservers' main.yml
More commonly in a playbook:

- hosts: multi:&dbservers

gather_facts: true

tags: [database_setup]

roles:

- { role: config-db , tags: ["dbserver-base"]}

blue-bag
ANSIBLE BASICS: TAGS
- hosts: servers
gather_facts: true
sudo: true
tags: [provision_core]
roles:
- { role: ansible-role-first5base , tags: ['base'], sudo: true }

- name: Set hostname
hostname: name={{ inventory_hostname_short }}
sudo: true
tags:
- configuration
- hosts

In a playbook
In a role / task
blue-bag
playbook: playbook.yml
play #1 (servers): TAGS: [provision_core]
TASK TAGS: [check_vars, common, provision, provision_core, user]
play #2 (servers): TAGS: [provision_core]
TASK TAGS: [base, check_vars, common, git, hosts, mail, newrelic, ntp,
provision_core, rootmail, ssh, timezone, user]
play #3 (webservers:&servers): TAGS: [provision_webserver]
TASK TAGS: [apache, base, drush, newrelic-php, php, provision_webserver, redis,
security, ssl, webserver-config]
play #4 (dbservers:&servers): TAGS: [provision_dbserver]
TASK TAGS: [base, mysql, provision_dbserver]
play #5 (webservers:!servers): TAGS: [sites_setup]
TASK TAGS: [base_setup, sites_setup, webserver-base]
play #6 (servers): TAGS: []
TASK TAGS: [reboot]
ANSIBLE BASICS: TAGS
List tags

$ansible-playbook main.yml --list-tags
blue-bag
ANSIBLE BASICS: TAGS
Limit to tags

$ansible-playbook playbook.yml --tags 'provision_core'
Skip tags (note quotes!)

$ansible-playbook playbook.yml --skip-tags 'apache'
Exclude tags set (note quotes!)

$ansible-playbook playbook.yml --tags 'git:!repo_pull'
Tag plays and roles:

- hosts: multi:&dbservers

gather_facts: true

tags: ['database_setup']

roles:

- { role: config-db , tags: ['dbserver-base']}

blue-bag
Variable types:

myvar: 'isastring'

mynumvar: 80

canref: "{{ myvar }}"

lists: 

- onevar

- twovar



dictionary:

onevar: 1,

twovar: 'two'



dictionarylist:

- {

onevar: 1,

twovar: 'two'
}
- {

threevar: 1,

fourvar: 'two'
}
truebool: True, true yes, 1
falsebool: False, false no, 0
ANSIBLE BASICS: VARIABLES
Variable naming conventions

start with a letter

don’t use hyphens

namespace - use your role names

- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
Inline or included files
ansible-playbook play.yml --extra-vars "user=vagrant"
Pass on command line
blue-bag
HOST VARSHOSTVARS (ANSIBLE GATHERED, group_vars, host_vars) :
ansible_all_ipv4_addresses: [10.0.2.15, 192.168.100.120]
ansible_all_ipv6_addresses: ['fe80::a00:27ff:fe8f:4176', 'fe80::a00:27ff:fe00:94a6']
ansible_architecture: x86_64
ansible_bios_date: 12/01/2006
ansible_bios_version: VirtualBox
ansible_cmdline: {BOOT_IMAGE: /vmlinuz-3.2.0-4-amd64, debian-installer: en_US, quiet: true,
ro: true, root: /dev/mapper/packer--debian--7-root}
ansible_date_time: {date: '2015-04-10', day: '10', epoch: '1428649901', hour: '07',
iso8601: '2015-04-10T07:11:41Z', iso8601_micro: '2015-04-10T07:11:41.551010Z', minute: '11',
month: '04', second: '41', time: '07:11:41', tz: UTC, tz_offset: '+0000', weekday: Friday,
year: '2015'}
ansible_default_ipv4: {address: 10.0.2.15, alias: eth0, gateway: 10.0.2.2, interface: eth0,
macaddress: '08:00:27:8f:41:76', mtu: 1500, netmask: 255.255.255.0, network: 10.0.2.0,
type: ether}
ansible_default_ipv6: {}
ansible_devices:
sda:
holders: []
host: 'IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)'
model: VBOX HARDDISK
partitions:
sda1: {sectors: '497664', sectorsize: 512, size: 243.00 MB, start: '2048'}
sda2: {sectors: '2', sectorsize: 512, size: 1.00 KB, start: '501758'}
sda5: {sectors: '83382272', sectorsize: 512, size: 39.76 GB, start: '501760'}
removable: '0'
rotational: '1'
scheduler_mode: cfq
sectors: '83886080'
sectorsize: '512'
size: 40.00 GB
support_discard: '0'
vendor: ATA
ansible_distribution: Debian
ansible_distribution_major_version: '7'
ansible_distribution_release: wheezy
ansible_distribution_version: '7.4'
...
gather_facts: true
blue-bag
ANSIBLE BASICS: VARIABLES
Variable Precedence
extra vars (-e in the command line) always win
then comes connection variables defined in inventory (ansible_ssh_user, etc)
then comes "most everything else" (command line switches, vars in play,
included vars, role vars, etc)
then comes the rest of the variables defined in inventory
then comes facts discovered about a system
then "role defaults", which are the most "defaulty" and lose in priority to
everything.
https://docs.ansible.com/playbooks_variables.html
Override once - put variables in sensible places
Role vars

Use defaults as a template for the variables to be overridden

Use vars/main.yml as constants that don't get overridden

e.g. Apache Daemon:

Debian is 'apache2' 

Centos is 'httpd'

Role authors - name vars with role name to avoid collisions.
blue-bag
ANSIBLE BASICS: VARIABLES
Better to put your vars in one correct place.
Common uses for precedence
role defaults (defaults/main.yml - apache_listen_port:80)
group_vars/all (http_port: 80)
group_vars/webservers (apache_listen_port: "{{http_port}}")
host_vars/web01.example.com (apache_listen_port: "8080")
command line extra vars -e (not so common)
Use Jinja filters (sparingly) - more later ...
blue-bag
ANSIBLE BASICS: VAULT
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
Ignore any plain text password
files

"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
Ignore any plain text password
files

Use separate files for role vars
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
Ignore any plain text password
files

Use separate files for role vars
Put all secrets in Ansible Vault

use a pattern: secrets*.yml
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
Ignore any plain text password
files

Use separate files for role vars
Put all secrets in Ansible Vault

use a pattern: secrets*.yml
Encrypt all secrets:
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Put all of your project files in GIT
Ignore any plain text password
files

Use separate files for role vars
Put all secrets in Ansible Vault

use a pattern: secrets*.yml
Encrypt all secrets:
"## all
&   "## all.yml
&   "## firewall.yml
&   "## first5base.yml
&   "## first5ntp.yml
&   "## first5ssh.yml
&   "## first5user.yml
&   "## newrelic.yml
&   "## secrets-s3cmd.yml
&   "## secrets-ssh.yml
&   "## secrets-sql.yml
&   %## secrets.yml
$ansible-vault encrypt secrets*.yml
blue-bag
ANSIBLE BASICS: VAULT
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
Place secrets in vault

$ansible-vault encrypt group_vars/all/secrets.yml

$ansible-vault decrypt group_vars/all/secrets.yml
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
Place secrets in vault

$ansible-vault encrypt group_vars/all/secrets.yml

$ansible-vault decrypt group_vars/all/secrets.yml
Automate vault

$ansible-playbook play.yml--ask-vault-pass

$ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
Place secrets in vault

$ansible-vault encrypt group_vars/all/secrets.yml

$ansible-vault decrypt group_vars/all/secrets.yml
Automate vault

$ansible-playbook play.yml--ask-vault-pass

$ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt
Add Vault password file location to ansible.cfg

[defaults]

vault_password_file = vault.txt
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
Place secrets in vault

$ansible-vault encrypt group_vars/all/secrets.yml

$ansible-vault decrypt group_vars/all/secrets.yml
Automate vault

$ansible-playbook play.yml--ask-vault-pass

$ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt
Add Vault password file location to ansible.cfg

[defaults]

vault_password_file = vault.txt
Run plays as normal

$ansible-playbook playbook.yml
blue-bag
ANSIBLE BASICS: VAULT
Create a vault Password file (vault_pass.txt)

gitignore vault password files

add single line with vault password in plain text
Add Sudo password to all/secrets.yml

ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
Place secrets in vault

$ansible-vault encrypt group_vars/all/secrets.yml

$ansible-vault decrypt group_vars/all/secrets.yml
Automate vault

$ansible-playbook play.yml--ask-vault-pass

$ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt
Add Vault password file location to ansible.cfg

[defaults]

vault_password_file = vault.txt
Run plays as normal

$ansible-playbook playbook.yml
Working with teams - use GPG: 

http://benincosa.com/?p=3235
blue-bag
ANSIBLE BASICS: VAULT
---
aws_access_key: 'ASKGHNSJYHRSUPERDUPERSECRETWGDFSG'
aws_secret_key: 'BaxBZqJhVreAllY!dpv4SNvFJd-X6l+cz'
$ANSIBLE_VAULT;1.1;AES256
31366634380a6566353532626532316339631366634380a6566353532626532316339631366634380a656635353
2626532316339631366634380a6566353532626532316339631366634380a656635353262653231633963136663
4380a6566353532626532316339631366634380a6566353532626532316339631366634380a6566353532626532
316339631366634380a6566353532626532316339631366634380a6566353532626532316339631366634380a65
66353532626532316339631366634380a6566353532626532316339631366634380a65663535326265323163396
31366634380a65663535326265323163396
Create a secrets file 

group_vars/all/secrets-s3.yml

Encrypt the secrets file 

ansible-vault encrypt group_vars/all/secrets-s3.yml

blue-bag
ANSIBLE BASICS: TESTING
blue-bag
ANSIBLE BASICS: TESTING
Test play books

$ansible-playbook --syntax-check --list-tasks playbook.yml
blue-bag
ANSIBLE BASICS: TESTING
Test play books

$ansible-playbook --syntax-check --list-tasks playbook.yml
Check mode

$ansible-playbook playbook.yml --check

$ansible-playbook playbook.yml --check --diff
blue-bag
ANSIBLE BASICS: TESTING
Test play books

$ansible-playbook --syntax-check --list-tasks playbook.yml
Check mode

$ansible-playbook playbook.yml --check

$ansible-playbook playbook.yml --check --diff
Test against Virtual machines

$ ansible-playbook playbook.yml --limit local
blue-bag
ANSIBLE: ORIGINAL RECIPE
w

Original Recipe
Availableoverthecounter

Contents:

Galaxy Roles

Custom Vars

Simple Playbook
x
Multipurpose in a jar

Want a quick solution?

Easy to maintain

Generic

Documented examples
blue-bag
---
- hosts: all



roles:
- geerlingguy.mysql
- geerlingguy.apache
- geerlingguy.php
A LAMP SERVER IN SIX LINES OF YAML
Jeff Geerling - Ansible for Devops
ANSIBLE: ORIGINAL RECIPE
blue-bag
For all local
development
use VLAD
http://git.io/DCB-Vlad http://www.drupalvm.com/
For all local development
use Drupal VM
s
ANSIBLE: ORIGINAL RECIPE OTHER BRANDS
blue-bag
ANSIBLE: SPECIAL FORMULA


w
Special Formula

Noprescription!

Contents:

Forked Roles
Custom Roles

Custom Inventory & Vars

Featured multistage playbook
x
Mostly Galaxy Roles
Customised to needs
Match local & production
Separate plays
blue-bag
ANSIBLE: SPECIAL FORMULA
Learning about lists & filters
Coding with plugins
Working with files
Creating a simple role
blue-bag
ANSIBLE: FILTERS AND LISTS
Ansible lists and loops

with_items

with_nested

with_first_found

with_indexed_items

with_nested

with_fileglob
Jinja Filters

default

join

union

list
Python Plugins

where you code
"## README.md
"## ansible.cfg
"## common
&   %## handlers
&      %## main.yml
"## group_vars
&   %## all.yml
"## host_vars
&   %## local.yml
"## hosts
"## plugins
&   "## filter_plugins
&      %## fetchlistfromdict.py
"## roles
[defaults]
log_path = log/ansible.log
hostfile = ./hosts
filter_plugins = ./plugins/filter_plugins
blue-bag
HOST VARS
HOSTVARS (ANSIBLE GATHERED, group_vars, host_vars) :
ansible_all_ipv4_addresses: [10.0.2.15, 192.168.100.120]
ansible_all_ipv6_addresses: ['fe80::a00:27ff:fe8f:4176', 'fe80::a00:27ff:fe00:94a6']
ansible_architecture: x86_64
ansible_default_ipv4: {address: 10.0.2.15, alias: eth0, gateway:
10.0.2.2, interface: eth0,
macaddress: '08:00:27:8f:41:76', mtu: 1500, netmask: 255.255.255.0,
network: 10.0.2.0,
type: ether}
ansible_distribution: Debian
ansible_distribution_major_version: '7'
ansible_distribution_release: wheezy
ansible_distribution_version: '7.4'
...
gather_facts: true
blue-bag
ANSIBLE: WORKING WITH LISTS
Get a list of the IPS from a dictionary & host group:
vars:
firewall_whitelist:
- { name: 'local', ip: "192.168.100.1" }
- { name: 'fred', ip: '192.168.100.120' }
- { name: 'bob', ip: '192.168.100.121' }
firewall_whitelistgroup: "webservers"
tasks:
- name: Setfact for allowed ips list for chain script
set_fact:
allowed_ips: "{{ firewall_whitelist|map(attribute='ip')|list|unique }}"
- name: Setfact list using group for var firewall_whitelistgroup
set_fact:
hosts_ips: "{{ groups[firewall_whitelistgroup] }}"


- name: Show the union of list 1 & 2
debug: msg="{{ allowed_ips|union(hosts_ips) }}"
blue-bag
ANSIBLE: WORKING WITH LISTS
Flattening and joining lists:
- name: Get the servers' ansible_all_ipv4_addresses

set_fact:

ip_list2: "{{hostvars|fetchlistfromdict(groups.multi)|
map(attribute='ansible_all_ipv4_addresses')|list }}"
TASK: [Show the list we obtained for ansible_all_ipv4_addresses]
**************
ok: [app1] => {
"msg": "[['10.0.2.15', '192.168.100.120'], ['10.0.2.15',
'192.168.100.121'], ['10.0.2.15', '192.168.100.122']]"
}
blue-bag
ANSIBLE: WORKING WITH LISTS
Your first Plugin! 

./plugins/filter_plugins/flatten_dict_values.py
# This function will take a dictionary composed of sub arrays
# and flatten it
# e.g.
# [[u'321.321.321.321', u'10.10.20.54'], [u'46.101.45.10', u'10.10.20.55']]
# to
# [u'321.321.321.321', u'10.10.20.54', u'123.123.123.123', u'10.10.20.55']
def flatten_dict_values(dictionary):
result = []
result = reduce(list.__add__, dictionary,[])
return result
class FilterModule (object):
def filters(self):
return {
"flatten_dict_values": flatten_dict_values
}
# http://feldboris.alwaysdata.net/blog/python-trick-how-to-flatten-dictionaries-values-composed-of-iterables.html
# http://stackoverflow.com/questions/15995/useful-code-which-uses-reduce-in-python
blue-bag
ANSIBLE: WORKING WITH FILES
Copy a file
Copy multiple files
Work with archives
Line in file
Block in file
Templates

blue-bag
ANSIBLE: WORKING WITH FILES
Copy a file
- name: Upload the exclude list for archives etc
copy:
src: exclude-list.txt
dest: "{{ proj_site_root }}/exclude-list.txt"
owner: "{{ user }}"
group: "{{ user_admin_group }}"
tags: [site_files]
blue-bag
ANSIBLE: WORKING WITH FILES
Copy multiple files
- name: Apache | Put up Holding page
copy:
src: "{{ item }}"
dest: "{{ apache_default_site }}"
owner: "{{ apache_admin_user }}"
group: "{{ apache_admin_group }}"
mode: 0644
with_fileglob:
- default-site/*
blue-bag
ANSIBLE: WORKING WITH FILES
Work with archives
- name: Upload & unarchive a set of files
unarchive:
src: "sitefiles/{{ proj_short_name }}.tar"
dest: "/tmp/{{ proj_short_name }}”
copy: yes
tags: [site_files]
blue-bag
ANSIBLE: WORKING WITH FILES
Work with rsync
- name: Rsync the uploaded files
synchronize:
src: "/tmp/{{ proj_short_name }}/files"
dest: "{{ site_folder }}/{{ site_docroot }}/sites/default"
archive: no
delete: no
recursive: yes
# times: yes
# perms: yes
checksum: yes
rsync_opts: "--include='.htaccess' --exclude-from '{{ proj_site_root }}/exclude-list.txt'"
delegate_to: "{{ inventory_hostname }}"
tags: [site_files]
notify:
- clear Drupal cache
- Remove sync files
blue-bag
ANSIBLE: WORKING WITH FILES
Line in file
- name: Update git config to ignore mode changes
lineinfile:
dest: "{{ site_folder }}/{{ site_docroot }}/.git/config"
regexp: ^[s]*filemode = true
line: " filemode = false"
tags: [repo_setup]
blue-bag
- name: Drupal | Write host specific aliases to project aliases
blockinfile:
dest: "/usr/local/share/drush/aliases/{{ proj_short_name }}.aliases.drushrc.php"
backup: no
marker: "## {mark} {{ proj_short_name }}.{{ stage }} ANSIBLE MANAGED BLOCK -->"
content: |
$aliases['{{ stage }}'] = array(
'parent' => '@{{ proj_short_name }}.common',
'root' => '{{ site_folder }}/{{ site_docroot }}',
'uri' => 'http{{ 's' if ssl_enabled else '' }}://{{ site_name }}',
'path-aliases' => array(
'%dump' => $_projroot . '/backups/{{ stage }}-' . date(Ymd-His) . '.sql',
'%files' => '{{ site_files_public }}',
),
);
insertafter: "## -- Host aliases"
tags: [drupal_setup]
ANSIBLE: WORKING WITH FILES
Block in file - Write block for Drush Alias
blue-bag
- name: Drupal | Write host specific aliases to project aliases
blockinfile:
dest: "/usr/local/share/drush/aliases/{{ proj_short_name }}.aliases.drushrc.php"
backup: no
marker: "## {mark} {{ proj_short_name }}.{{ stage }} ANSIBLE MANAGED BLOCK -->"
content: |
$aliases['{{ stage }}'] = array(
'parent' => '@{{ proj_short_name }}.common',
'root' => '{{ site_folder }}/{{ site_docroot }}',
'uri' => 'http{{ 's' if ssl_enabled else '' }}://{{ site_name }}',
'path-aliases' => array(
'%dump' => $_projroot . '/backups/{{ stage }}-' . date(Ymd-His) . '.sql',
'%files' => '{{ site_files_public }}',
),
);
insertafter: "## -- Host aliases"
tags: [drupal_setup]
## BEGIN project.staging ANSIBLE MANAGED BLOCK -->
$aliases['staging'] = array(
'parent' => '@project1.common',
'root' => '/var/www/project1/staging/htdocs',
'uri' => 'https://staging.project1.com',
'path-aliases' => array(
'%dump' => $_projroot . '/backups/staging-' . date(Ymd-His) . '.sql',
'%files' => '/var/www/project1/staging/htdocs/sites/default/files',
),
);
## END project.staging ANSIBLE MANAGED BLOCK -->
ANSIBLE: WORKING WITH FILES
Block in file - Write block for Drush Alias
blue-bag
ANSIBLE: WORKING WITH FILES
Templates
- name: Apache | test Updated hosts
template:
src: config-test.conf.j2
dest: "/tmp/config-test.conf"
mode: 0644
owner: root
group: root
validate: 'apachectl -t -f %s'
register: apache_result
when: vhost_updated.changed
ignore_errors: yes
sudo: true
tags: [apache]
# {{ ansible_managed }}
<VirtualHost *:{{ http_port }}>
ServerAdmin {{ apache_server_admin }}
ServerName {{ webserver_hostname }}
{% if webserver_hostname_alias is defined 

and webserver_hostname_alias|length > 0 %}%}
ServerAlias {{ webserver_hostname_alias }}
{% endif %}
DocumentRoot {{ apache_default_site }}
{% if apache_server_status_enabled %}
<Location /server-status>
SetHandler server-status
{% if apache_vhosts_version == '2.4' %}
{% for entry in incoming_ip_whitelist %}
Require ip {{ entry.ip }}
{% endfor %}
{% else %}
Order deny,allow
Deny from all
Allow from 127.0.0.1
{% for entry in incoming_ip_whitelist %}
Allow from {{ entry.ip }}
{% endfor %}
{% endif %}
</Location>
{% endif %}
blue-bag
A FIRST ROLE: BASIC SECURITY - FIRST 5
Lock down SSH
Create users and groups
Limit SSH to RSA key: 

(no passwords / no root)
Limit to AllowGroups
Update APT - unattended updates security
Configure hostname
Configure IPTables
Configure Fail2ban
blue-bag
Take what we know
Research
Create templates / vars
Consistent across infrastructure
Use tasks to implement
Idempotent
Version controlled
Documented
A FIRST ROLE: BASIC SECURITY - FIRST 5
blue-bag
FIRST5: SSH - EXAMPLE
"## README.md
"## defaults
%## main.yml
"## tasks
&   "## main.yml
&   %## ssh_config.yml
"## templates
&   %## ssh_config.j2
blue-bag
FIRST5: SSH - EXAMPLE
"## README.md
"## defaults
%## main.yml
"## tasks
&   "## main.yml
&   %## ssh_config.yml
"## templates
&   %## ssh_config.j2
# Package generated configuration file
# See the sshd_config(5) manpage for details
# What ports, IPs and protocols we listen for
Port 22
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes
# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 768
# Logging
SyslogFacility AUTH
LogLevel INFO
# Authentication:
LoginGraceTime 120
blue-bag
FIRST5: SSH - EXAMPLE
# {{ ansible_managed }}
# See the sshd_config(5) manpage for details
# What ports, IPs and protocols we listen for
Port {{ ssh_port }}
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes
# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits {{ ssh_server_key_bits }}
# Logging
SyslogFacility AUTH
LogLevel INFO
# Authentication:
LoginGraceTime {{ ssh_login_grace_time }}
templates/ssh_config.j2
blue-bag
FIRST5: SSH
"## README.md
"## defaults
%## main.yml
"## tasks
&   "## main.yml
&   %## ssh_config.yml
"## templates
&   %## ssh_config.j2
# SSH Settings
# optionally change port
ssh_port: 22
# reduce login grace time from 120 (2 minutes)
ssh_login_grace_time: 20
# increase the server key bit encryption default=768
ssh_server_key_bits: 4096
# control the parallelism
ssh_max_startups: 3:50:10
# control forwarding
ssh_allow_tcp_forwarding: “no”
ssh_allow_x11_forwarding: “no”
blue-bag
FIRST5: SSH
---
- name: SSH Config | add ssh config file
template:
src:ssh_config.j2
dest:/etc/ssh/sshd_config
backup:yes
notify: restart ssh
"## README.md
"## defaults
%## main.yml
"## tasks
&   "## main.yml
&   %## ssh_config.yml
"## templates
&   %## ssh_config.j2
blue-bag
BASIC SECURITY: FIRST 5---
- hosts: all
gather_facts: true
sudo: true
pre_tasks:
- name: Pretask | Update apt cache
apt: update_cache=yes cache_valid_time=3600
- name: Pretask | Run apt-get upgrade
apt: upgrade=dist
roles:
- { role: base , tags: ["base"] }
- { role: ntp , tags: ["ntp"] }
- { role: user, tags: ["user"] }
- { role: ssh , tags: ["ssh"] }
- { role: fail2ban, tags: ["fail2ban"] }
- { role: logwatch , tags: ["logwatch"] }
- { role: exim , tags: ["exim"] }
- { role: geerlingguy.firewall, tags: ["iptables"] }
handlers:
- include: common/handlers/main.yml
Don’t reinvent the wheel

Use Galaxy Roles

where relevant
blue-bag
BASIC SECURITY: SSL POODLE
- name: Apache security | SSL Poodle update
lineinfile:
dest=/etc/apache2/mods-available/ssl.conf
regexp="{{ item.regexp }}"
line="{{ item.line }}"
backup=yes
with_items:
- {
regexp: "^# enable only secure protocols:",
line: "# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3"
}
- {
regexp: "^SSLProtocol",
line: "SSLProtocol all -SSLv2 -SSLv3"
}
notify: restart apache
tags:
- poodle
blue-bag
BASIC SECURITY: SSL POODLE
- name: Apache security | SSL Poodle update
template: src=ssl-poodle.conf.j2 dest=/etc/apache2/conf.d/ssl.conf
notify: restart apache
tags:
- poodle
# {{ ansible_managed }}
<IfModule mod_ssl.c>
# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3
SSLProtocol all -SSLv2 -SSLv3
</IfModule>
SSL-POODLE.CONF.J2
blue-bag
BASIC SECURITY: DRUPAL SECURITY
Ansible role quickly address 

security issues such as 

SA-CORE-2014-005
Inventory of Drupal sites
Task to apply patch
Test for vulnerability
Re-use for future patches
blue-bag
BASIC SECURITY: DRUPAL SECURITY---
ansible_ssh_host: 123.123.123.123
webroot: "/var/www"
drupal7_sites:
- {
drupal_docroot: "{{ webroot }}/example.d7.site/live/htdocs”,
vhost_name: “live.example.d7.site”
}
- {
drupal_docroot: "{{ webroot }}/example.d7.site/dev/htdocs",
vhost_name: “dev.example.d7.site”
}
- {
drupal_docroot: "{{ webroot }}/example.d7.site/staging/htdocs",
vhost_name: “staging.example.d7.site”
}
drupal6_sites:
- {
drupal_docroot: "{{ webroot }}example.d6.site",
vhost_name: "example.d6.site"
}
host_vars: example.server.net
blue-bag
BASIC SECURITY: DRUPAL SECURITY
---
# tasks file for SA-CORE-2014-005-D7
- name: Drupal7 patch | get patch | SA-CORE-2014-005-D7.
get_url:
url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch
dest: /tmp/SA-CORE-2014-005-D7.patch
- name: Drupal7 patch | Apply the patch from the drupal docroot.
shell: "patch -p1 < /tmp/SA-CORE-2014-005-D7.patch chdir={{ item.drupal_docroot }}"
with_items:
- "{{ drupal7_sites }}"
- name: Drupal7 patch | Clear Drupal caches.
command: "drush cc all chdir={{ item.drupal_docroot }}"
with_items:
- "{{ drupal7_sites }}"
Role: SA-CORE-2014-005-D7 - main.yml
see blog post: http://www.midwesternmac.com/blogs/jeff-geerling/fixing-drupal-fast-using
blue-bag
BASIC SECURITY: DRUPAL SECURITY
- name: Drupal6 patch | get patch | SA-CORE-2014-005-D7.
copy:
src=dbtng.patch
dest=/tmp/SACORE2014005D6-dbtng.patch owner=root group=root mode=0644
- name: Drupal6 patch | Apply the patch from the drupal docroot.
shell: "patch -p1 < /tmp/SACORE2014005D6-dbtng.patch chdir={{ item.drupal_docroot }}"
with_items:
- "{{ drupal6_sites }}"
tags: D6
Role: SA-CORE-2014-005-D7 - main.yml - (cont..)
blue-bag
BASIC SECURITY: DRUPAL SECURITY
---
# tasks file for SA-CORE-2014-005-D7
- name: Drupal7 patch | get patch | SA-CORE-2014-005-D7.
get_url:
url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch
dest: /tmp/SA-CORE-2014-005-D7.patch
- name: Drupal7 patch | Apply the patch from the drupal docroot.
patch:
remote_src: true
src: /tmp/SA-CORE-2014-005-D7.patch
basedir: "{{ item.drupal_docroot }}”
with_items:
- "{{ drupal7_sites }}"
- name: Drupal7 patch | Clear Drupal caches.
command: "drush cc all chdir={{ item.drupal_docroot }}"
with_items:
- "{{ drupal7_sites }}"
Role: SA-CORE-2014-005-D7 - main.yml - UPDATED - not tested
blue-bag
ANSIBLE: COOK YOUR OWN


Limited Edition

Prescription only
Contents:

Featured multistage Playbook

Multiple plays

Forked Roles
Custom Roles
Vault
Plugins & filters

Complex Inventory & Vars

l
Drupal

x
Fully customisable
Extensible
Secure
Manage production
Manage host / server / db
assignment
Operating heavy machinery?

Non drowsy!
blue-bag
ANSIBLE: INVENTORY DRIVEN SETUP
Ansible iterates hosts
Not always one site per server
Simple to reassign sites / dbs to new servers
Keeps all vars together at appropriate 'level'
Structured
Inheritance of vars - e.g. Project vars
Separates sites & failures
Can use handlers for things like Drush CC

without cc all sites because one changed
blue-bag
ANSIBLE: INVENTORY DRIVEN SETUP
apache_vhosts:
- {
servername: "www.project1.com", 

documentroot: "/var/www/html",
}
- {
servername: "dev.project1.com", 

documentroot: "/var/www/html",
} ...
drupal sites:
- {
sitename: "www.project1.com"
db_name: "www_project1_com"
db_user: "dbusername"
drupal_salt: 'QPOrL...',
repo_branch: "master"
}
- {
sitename: "dev.project1.com"
db_name: "dev_project1_com"
db_user: "dbusername"
drupal_salt: 'QPOrL...',
repo_branch: "master"
}
host_vars/www.project1.com.yml
servername: "www.project1.com" 

documentroot: "{{ proj_root }}"
sitename: "www.project1.com"
db_name: "www_project1_com"
db_user: "{{ proj_db_user }}"
drupal_salt: "{{ proj_salt }}"
repo_branch: "master"
stage: live
host_vars/dev.project1.com.yml
servername: "dev.project1.com" 

documentroot: "{{ proj_root }}"
sitename: "dev.project1.com"
db_name: "dev_project1_com"
db_user: "{{ proj_db_user }}"
drupal_salt: "{{ proj_salt }}"
repo_branch: "dev"
stage: dev
OR
blue-bag
ANSIBLE: HACKING THE INVENTORY
#### inventory groups
# group for project
# /group_vars/project1.yml
[project1]
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
www_project1_co_uk
dev_project1_co_uk
staging_project1_co_uk
# group for project
[project2]
www.project2.co.uk
dev.project2.co.uk
staging.project2.co.uk
www_project2_co_uk
dev_project2_co_uk
staging_project2_co_uk
# group for server setup
[servers]
web01.myserver.net
web02.myserver.net
db01.myserver.net
# group for webserver vars
[webservers:children]
webserver01
# Group for DB server vars
[dbservers:children]
dbserver01
# Group to allocate 'sites to server 

the first entry pins the group to the actual
server
[webserver01]
web01.myserver.net
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
www.project2.co.uk
dev.project2.co.uk
staging.project2.co.uk
# Group to allocate 'dbs' to server 

the first entry pins the group to the actual
server
[dbserver01]
db01.myserver.net
www_project1_co_uk
dev_project1_co_uk
staging_project1_co_uk
www_project2_co_uk
dev_project2_co_uk
staging_project2_co_uk
blue-bag
ANSIBLE: HACKING THE INVENTORY
# group for server setup
[servers]
web01.myserver.net
db01.myserver.net
# group for webserver vars
[webservers:children]
webserver01
# Group for DB server vars
[dbservers:children]
dbserver01
[webserver01]
web01.myserver.net
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
[dbserver01]
db01.myserver.net
www_project1_co_uk
dev_project1_co_uk
staging_project1_co_uk
# group for server setup
[servers]
server01.myserver.net
# group for webserver vars
[webservers:children]

server01
[server01]
server01.myserver.net
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
www_project1_co_uk
dev_project1_co_uk
staging_project1_co_uk
# group for server setup
[servers]
web01.myserver.net
web02.myserver.net
db01.myserver.net
db02.myserver.net
# group for webserver vars
[webservers:children]
webserver01
webserver02
# Group for DB server vars
[dbservers:children]
dbserver01
dbserver02
[webserver01]
web01.myserver.net
www.project1.co.uk
[dbserver01]
db01.myserver.net
dev_project1_co_uk
staging_project1_co_uk
[webserver02]
web02.myserver.net
dev.project1.co.uk
staging.project1.co.uk
[dbserver02]
db02.myserver.net
www_project1_co_uk
# Group for DB server vars
[dbservers:children]
server01
Simple Lamp
Separate web / DB
Multiple Servers
# groups for assigning hosts to physical server
blue-bag
ANSIBLE: HACKING THE INVENTORY
[webserver01]
web01.myserver.net
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
[webserver01]
web01.myserver.net
www.project1.co.uk
Once setup - easy to assign sites to servers preserving
all vars
[webserver02]
web02.myserver.net
dev.project1.co.uk
staging.project1.co.uk
blue-bag
ANSIBLE: HACKING THE INVENTORY
"## host_vars
&   "## web01.myserver.net.yml
&   "## db01.myserver.net.yml
&   "## www.project1.co.uk.yml
&   "## www_project1_co_uk.yml
&   "## staging.project1.co.uk.yml
&   "## staging_project1_co_uk.yml
&   "## dev.project1.co.uk.yml
&   %## dev_project1_co_uk.yml
"## group_vars
&   "## all
&   &   "## all.yml
&   &   "## first5base.yml
&   &   %## secrets.yml
&   "## dbservers
&   &   "## common.yml
&   &   "## firewall.yml
&   &   "## mysql.yml
&   &   %## secrets-mysql.yml
&   "## project1
&   &   "## common.yml
&   &   %## secrets.yml
&   "## local.yml
&   "## webserver01.yml
&   %## webservers
&   "## apache.yml
&   "## common.yml
&   "## php.yml
&   %## redis.yml
blue-bag
ANSIBLE: HACKING THE INVENTORY
All
Group: Host
Project
Host
Group: Type
General defaults:

common ups, API keys, server role vars etc
Server type vars - (i.e. webservers)

eg. DB port, apache cfg, server type role vars
Project defaults:

repo, folder structure, Drupal version
Host details for Group Host

e.g. SSH details
Site / DB specific details

e.g. repo branch, vhost name, db name
blue-bag
ANSIBLE: INVENTORY DRIVEN SETUP
Ansible iterates hosts
Simple to reassign sites / dbs to new servers
Keeps all vars together at appropriate 'level'
Structured
Inheritance of vars - e.g. Project vars
Separates sites & failures
Can use handlers for things like Drush CC

without cc all sites because one changed
blue-bag
playbook: playbook.yml
play #1 (servers): TAGS: [provision_core, init]
play #2 (webservers:&servers): TAGS: [provision_webserver]
play #3 (dbservers:&servers): TAGS: [provision_dbserver]
play #4 (webservers:!servers): TAGS: [sites_setup]
# group for server setup
[servers]
web01.myserver.net
db01.myserver.net
# group for webserver vars
[webservers:children]
webserver01
# Group for DB server vars
[dbservers:children]
# groups for assigning hosts to physical server
[webserver01]
web01.myserver.net
www.project1.co.uk
dev.project1.co.uk
staging.project1.co.uk
[dbserver01]
db01.myserver.net
www_project1_co_uk
dev_project1_co_uk
staging_project1_co_uk
ANSIBLE: HACKING THE INVENTORY
blue-bag
ANSIBLE & DRUPAL
Put all sites in maintenance mode

(Note: better to use maintenance site and re-point traffic to that so you can still work
on your site on your ip)
Pull latest changes
Use in harmony with DrushClear caches and
other Drush actions

see https://github.com/jenitehan/drupal_update_check
Extend Drush / Drupal to output JSON

$drush pmi --format=json
Automate common tasks
blue-bag
ANSIBLE & DRUPAL: EXAMPLE 2
Use Case: Commerce site
Product Image updates
Replace the image(s)
Clear the Image Cache / styles
blue-bag
---
# vars file for base
files_src_path: “/path/to/local/updated/images”
files_dest_path: “/sites/default/files”
files_to_update:
- “fa9001.jpg"
- “fa9002.jpg"
image_cache_folders:
- “styles/thumbnail/public/product_images"
- "styles/product_gallery/public/product_images"
- "styles/medium_tall/public/product_images"
- "styles/product_full/public/product_images"
ANSIBLE & DRUPAL: EXAMPLE 2
./roles/updateimages/vars/main
blue-bag
- name: clear Drupal cache
command: "drush cc all chdir={{ site_folder }}/{{ site_docroot }}"
register: command_result
failed_when: "'cache was cleared' not in command_result.stderr"
ANSIBLE & DRUPAL: DRUSH
Create Drush handler
common/handlers/main.yml
blue-bag
- name: Image Update | Products
copy:
src: {{ files_src_path }}/{{ item[1] }}
dest: {{ site_docroot }}/{{ files_dest_path }}/product_images/{{ item }}
owner: www-data
group: www-data
mode: 0644
with_items:
- files_to_update
tags: images
notify: clear drupal cache
- name: Image Update | Update Cache
file:
path: {{ site_docroot }}/{{ files_dest_path }}/{{ item[0] }}/{{ item[1] }}

state: absent
with_nested:
- image_cache_folders
- files_to_update
tags: images
notify: clear drupal cache
ANSIBLE & DRUPAL: EXAMPLE 2
./roles/updateimages/tasks/main.yml
blue-bag
RUN UPDATE IMAGES PLAY
blue-bag
RUN UPDATE IMAGES PLAY
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Products] ****************
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Products] ****************
ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) =>
{"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/
var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path":
“/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size":
193018, "state": "file", "uid": 33}
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Products] ****************
ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) =>
{"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/
var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path":
“/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size":
193018, "state": "file", "uid": 33}
changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg'])
=> {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/
var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33",
"mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/
ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33}
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Products] ****************
ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) =>
{"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/
var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path":
“/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size":
193018, "state": "file", "uid": 33}
changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg'])
=> {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/
var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33",
"mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/
ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33}
changed: [server.example.com] => (item=['/var/www/mysite/dev/htdocs', 'fa9001.jpg']) =>
{"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/var/
www/mysite/dev/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33,
"group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33",
"mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/
ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33}
$ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
blue-bag
RUN UPDATE IMAGES PLAY
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}
changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state":
"absent"}
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}
changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state":
"absent"}
changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg",
"state": "absent"}
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}
changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state":
"absent"}
changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg",
"state": "absent"}
PLAY RECAP ********************************************************************
blue-bag
RUN UPDATE IMAGES PLAY
TASK: [updateimages | Image Update | Update Cache] **************************
ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed":
false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/
htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg",
"state": "absent"}
changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state":
"absent"}
changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) =>
{"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/
mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg",
"state": "absent"}
PLAY RECAP ********************************************************************
server.example.com : ok=2 changed=2 unreachable=0 failed=0
blue-bag
ANSIBLE TOOLS
Automate playbook runs
Collaborate
Log
Permissions
Scheduled runs
Push button deployment
Free for < 10 nodes
blue-bag
CONCLUSION / QUESTIONS
For all local
development
use VLAD
http://git.io/DCB-Vlad
For Ansible get the books
https://leanpub.com/
ansible-for-devops
http://www.ansible.com/
ansible-book
blue-bag
BLUE-BAG
GEORGE BOOBYER

DRUPAL: iAUGUR

GEORGE@BLUE-BAG.COM
TWITTER: iBLUEBAG
www.blue-bag.com
Established in 2000

The year before Drupal 1.0

More Related Content

What's hot

Making environment for_infrastructure_as_code
Making environment for_infrastructure_as_codeMaking environment for_infrastructure_as_code
Making environment for_infrastructure_as_code
Soshi Nemoto
 
Take control of your Jenkins jobs via job DSL.
Take control of your Jenkins jobs via job DSL.Take control of your Jenkins jobs via job DSL.
Take control of your Jenkins jobs via job DSL.
Łukasz Proszek
 
Using Test Kitchen for testing Chef cookbooks
Using Test Kitchen for testing Chef cookbooksUsing Test Kitchen for testing Chef cookbooks
Using Test Kitchen for testing Chef cookbooks
Timur Batyrshin
 
Troubleshooting Puppet
Troubleshooting PuppetTroubleshooting Puppet
Troubleshooting Puppet
Thomas Howard Uphill
 
Ansible tips & tricks
Ansible tips & tricksAnsible tips & tricks
Ansible tips & tricks
bcoca
 
Ansible : what's ansible & use case by REX
Ansible :  what's ansible & use case by REXAnsible :  what's ansible & use case by REX
Ansible : what's ansible & use case by REX
Saewoong Lee
 
AnsibleFest 2014 - Role Tips and Tricks
AnsibleFest 2014 - Role Tips and TricksAnsibleFest 2014 - Role Tips and Tricks
AnsibleFest 2014 - Role Tips and Tricks
jimi-c
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as code
Julian Simpson
 
How we use and deploy Varnish at Opera
How we use and deploy Varnish at OperaHow we use and deploy Varnish at Opera
How we use and deploy Varnish at Opera
Cosimo Streppone
 
Using docker for data science - part 2
Using docker for data science - part 2Using docker for data science - part 2
Using docker for data science - part 2
Calvin Giles
 
Chef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
Chef Workshop: Setup Environment with Chef,Vagrant, and BerkshelfChef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
Chef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
Jun Sakata
 
2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku
ronnywang_tw
 
Docker for data science
Docker for data scienceDocker for data science
Docker for data science
Calvin Giles
 
Using python and docker for data science
Using python and docker for data scienceUsing python and docker for data science
Using python and docker for data science
Calvin Giles
 
Puppet modules for Fun and Profit
Puppet modules for Fun and ProfitPuppet modules for Fun and Profit
Puppet modules for Fun and Profit
Alessandro Franceschi
 
DevOps Hackathon: Session 3 - Test Driven Infrastructure
DevOps Hackathon: Session 3 - Test Driven InfrastructureDevOps Hackathon: Session 3 - Test Driven Infrastructure
DevOps Hackathon: Session 3 - Test Driven Infrastructure
Antons Kranga
 
PuppetCamp SEA 1 - Use of Puppet
PuppetCamp SEA 1 - Use of PuppetPuppetCamp SEA 1 - Use of Puppet
PuppetCamp SEA 1 - Use of Puppet
Walter Heck
 
Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011
Puppet
 
PuppetCamp SEA 1 - Puppet Deployment at OnApp
PuppetCamp SEA 1 - Puppet Deployment  at OnAppPuppetCamp SEA 1 - Puppet Deployment  at OnApp
PuppetCamp SEA 1 - Puppet Deployment at OnApp
Walter Heck
 
Ansible, Simplicity, and the Zen of Python
Ansible, Simplicity, and the Zen of PythonAnsible, Simplicity, and the Zen of Python
Ansible, Simplicity, and the Zen of Python
toddmowen
 

What's hot (20)

Making environment for_infrastructure_as_code
Making environment for_infrastructure_as_codeMaking environment for_infrastructure_as_code
Making environment for_infrastructure_as_code
 
Take control of your Jenkins jobs via job DSL.
Take control of your Jenkins jobs via job DSL.Take control of your Jenkins jobs via job DSL.
Take control of your Jenkins jobs via job DSL.
 
Using Test Kitchen for testing Chef cookbooks
Using Test Kitchen for testing Chef cookbooksUsing Test Kitchen for testing Chef cookbooks
Using Test Kitchen for testing Chef cookbooks
 
Troubleshooting Puppet
Troubleshooting PuppetTroubleshooting Puppet
Troubleshooting Puppet
 
Ansible tips & tricks
Ansible tips & tricksAnsible tips & tricks
Ansible tips & tricks
 
Ansible : what's ansible & use case by REX
Ansible :  what's ansible & use case by REXAnsible :  what's ansible & use case by REX
Ansible : what's ansible & use case by REX
 
AnsibleFest 2014 - Role Tips and Tricks
AnsibleFest 2014 - Role Tips and TricksAnsibleFest 2014 - Role Tips and Tricks
AnsibleFest 2014 - Role Tips and Tricks
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as code
 
How we use and deploy Varnish at Opera
How we use and deploy Varnish at OperaHow we use and deploy Varnish at Opera
How we use and deploy Varnish at Opera
 
Using docker for data science - part 2
Using docker for data science - part 2Using docker for data science - part 2
Using docker for data science - part 2
 
Chef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
Chef Workshop: Setup Environment with Chef,Vagrant, and BerkshelfChef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
Chef Workshop: Setup Environment with Chef,Vagrant, and Berkshelf
 
2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku
 
Docker for data science
Docker for data scienceDocker for data science
Docker for data science
 
Using python and docker for data science
Using python and docker for data scienceUsing python and docker for data science
Using python and docker for data science
 
Puppet modules for Fun and Profit
Puppet modules for Fun and ProfitPuppet modules for Fun and Profit
Puppet modules for Fun and Profit
 
DevOps Hackathon: Session 3 - Test Driven Infrastructure
DevOps Hackathon: Session 3 - Test Driven InfrastructureDevOps Hackathon: Session 3 - Test Driven Infrastructure
DevOps Hackathon: Session 3 - Test Driven Infrastructure
 
PuppetCamp SEA 1 - Use of Puppet
PuppetCamp SEA 1 - Use of PuppetPuppetCamp SEA 1 - Use of Puppet
PuppetCamp SEA 1 - Use of Puppet
 
Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011
 
PuppetCamp SEA 1 - Puppet Deployment at OnApp
PuppetCamp SEA 1 - Puppet Deployment  at OnAppPuppetCamp SEA 1 - Puppet Deployment  at OnApp
PuppetCamp SEA 1 - Puppet Deployment at OnApp
 
Ansible, Simplicity, and the Zen of Python
Ansible, Simplicity, and the Zen of PythonAnsible, Simplicity, and the Zen of Python
Ansible, Simplicity, and the Zen of Python
 

Viewers also liked

Ansible project-deploy (NomadPHP lightning talk)
Ansible project-deploy (NomadPHP lightning talk)Ansible project-deploy (NomadPHP lightning talk)
Ansible project-deploy (NomadPHP lightning talk)
Ramon de la Fuente
 
Docker and configuration management
Docker and configuration managementDocker and configuration management
Docker and configuration management
Mukta Aphale
 
Ansible - Crash course
Ansible - Crash courseAnsible - Crash course
Ansible - Crash course
Simone Soldateschi
 
Introduction to Ansible
Introduction to AnsibleIntroduction to Ansible
Introduction to Ansible
Michael Bahr
 
Automation and Ansible
Automation and AnsibleAutomation and Ansible
Automation and Ansible
jtyr
 
Docker Introduction
Docker IntroductionDocker Introduction
Docker Introduction
Robert Reiz
 
Ansible Introduction
Ansible Introduction Ansible Introduction
Ansible Introduction
Robert Reiz
 

Viewers also liked (7)

Ansible project-deploy (NomadPHP lightning talk)
Ansible project-deploy (NomadPHP lightning talk)Ansible project-deploy (NomadPHP lightning talk)
Ansible project-deploy (NomadPHP lightning talk)
 
Docker and configuration management
Docker and configuration managementDocker and configuration management
Docker and configuration management
 
Ansible - Crash course
Ansible - Crash courseAnsible - Crash course
Ansible - Crash course
 
Introduction to Ansible
Introduction to AnsibleIntroduction to Ansible
Introduction to Ansible
 
Automation and Ansible
Automation and AnsibleAutomation and Ansible
Automation and Ansible
 
Docker Introduction
Docker IntroductionDocker Introduction
Docker Introduction
 
Ansible Introduction
Ansible Introduction Ansible Introduction
Ansible Introduction
 

Similar to Medicine show2 Drupal Bristol Camp 2015

Learn basic ansible using docker
Learn basic ansible using dockerLearn basic ansible using docker
Learn basic ansible using docker
Larry Cai
 
Zero Downtime Deployment with Ansible
Zero Downtime Deployment with AnsibleZero Downtime Deployment with Ansible
Zero Downtime Deployment with Ansible
Stein Inge Morisbak
 
Ansible not only for Dummies
Ansible not only for DummiesAnsible not only for Dummies
Ansible not only for Dummies
Łukasz Proszek
 
Using Ansible Dynamic Inventory with Amazon EC2
Using Ansible Dynamic Inventory with Amazon EC2Using Ansible Dynamic Inventory with Amazon EC2
Using Ansible Dynamic Inventory with Amazon EC2
Brian Schott
 
Puppet for Java developers - JavaZone NO 2012
Puppet for Java developers - JavaZone NO 2012Puppet for Java developers - JavaZone NO 2012
Puppet for Java developers - JavaZone NO 2012
Carlos Sanchez
 
Continuous Delivery: The Next Frontier
Continuous Delivery: The Next FrontierContinuous Delivery: The Next Frontier
Continuous Delivery: The Next Frontier
Carlos Sanchez
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
biicode
 
Docker perl build
Docker perl buildDocker perl build
Docker perl build
Workhorse Computing
 
OpenWRT guide and memo
OpenWRT guide and memoOpenWRT guide and memo
OpenWRT guide and memo
家榮 吳
 
Puppet: Eclipsecon ALM 2013
Puppet: Eclipsecon ALM 2013Puppet: Eclipsecon ALM 2013
Puppet: Eclipsecon ALM 2013
grim_radical
 
How I hack on puppet modules
How I hack on puppet modulesHow I hack on puppet modules
How I hack on puppet modules
Kris Buytaert
 
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
Carlos Sanchez
 
Vagrant for real codemotion (moar tips! ;-))
Vagrant for real codemotion (moar tips! ;-))Vagrant for real codemotion (moar tips! ;-))
Vagrant for real codemotion (moar tips! ;-))
Michele Orselli
 
Be a happier developer with Docker: Tricks of the trade
Be a happier developer with Docker: Tricks of the tradeBe a happier developer with Docker: Tricks of the trade
Be a happier developer with Docker: Tricks of the trade
Nicola Paolucci
 
A quick intro to Ansible
A quick intro to AnsibleA quick intro to Ansible
A quick intro to Ansible
Dan Vaida
 
How to make debian package from scratch (linux)
How to make debian package from scratch (linux)How to make debian package from scratch (linux)
How to make debian package from scratch (linux)
Thierry Gayet
 
From Dev to DevOps - Codemotion ES 2012
From Dev to DevOps - Codemotion ES 2012From Dev to DevOps - Codemotion ES 2012
From Dev to DevOps - Codemotion ES 2012
Carlos Sanchez
 
Be a Happier Developer with Docker: Tricks of the Trade
Be a Happier Developer with Docker: Tricks of the TradeBe a Happier Developer with Docker: Tricks of the Trade
Be a Happier Developer with Docker: Tricks of the Trade
Docker, Inc.
 
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet
 
Greenfield Puppet: Getting it right from the start
Greenfield Puppet: Getting it right from the startGreenfield Puppet: Getting it right from the start
Greenfield Puppet: Getting it right from the start
David Danzilio
 

Similar to Medicine show2 Drupal Bristol Camp 2015 (20)

Learn basic ansible using docker
Learn basic ansible using dockerLearn basic ansible using docker
Learn basic ansible using docker
 
Zero Downtime Deployment with Ansible
Zero Downtime Deployment with AnsibleZero Downtime Deployment with Ansible
Zero Downtime Deployment with Ansible
 
Ansible not only for Dummies
Ansible not only for DummiesAnsible not only for Dummies
Ansible not only for Dummies
 
Using Ansible Dynamic Inventory with Amazon EC2
Using Ansible Dynamic Inventory with Amazon EC2Using Ansible Dynamic Inventory with Amazon EC2
Using Ansible Dynamic Inventory with Amazon EC2
 
Puppet for Java developers - JavaZone NO 2012
Puppet for Java developers - JavaZone NO 2012Puppet for Java developers - JavaZone NO 2012
Puppet for Java developers - JavaZone NO 2012
 
Continuous Delivery: The Next Frontier
Continuous Delivery: The Next FrontierContinuous Delivery: The Next Frontier
Continuous Delivery: The Next Frontier
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Docker perl build
Docker perl buildDocker perl build
Docker perl build
 
OpenWRT guide and memo
OpenWRT guide and memoOpenWRT guide and memo
OpenWRT guide and memo
 
Puppet: Eclipsecon ALM 2013
Puppet: Eclipsecon ALM 2013Puppet: Eclipsecon ALM 2013
Puppet: Eclipsecon ALM 2013
 
How I hack on puppet modules
How I hack on puppet modulesHow I hack on puppet modules
How I hack on puppet modules
 
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
Continuous Delivery with Maven, Puppet and Tomcat - ApacheCon NA 2013
 
Vagrant for real codemotion (moar tips! ;-))
Vagrant for real codemotion (moar tips! ;-))Vagrant for real codemotion (moar tips! ;-))
Vagrant for real codemotion (moar tips! ;-))
 
Be a happier developer with Docker: Tricks of the trade
Be a happier developer with Docker: Tricks of the tradeBe a happier developer with Docker: Tricks of the trade
Be a happier developer with Docker: Tricks of the trade
 
A quick intro to Ansible
A quick intro to AnsibleA quick intro to Ansible
A quick intro to Ansible
 
How to make debian package from scratch (linux)
How to make debian package from scratch (linux)How to make debian package from scratch (linux)
How to make debian package from scratch (linux)
 
From Dev to DevOps - Codemotion ES 2012
From Dev to DevOps - Codemotion ES 2012From Dev to DevOps - Codemotion ES 2012
From Dev to DevOps - Codemotion ES 2012
 
Be a Happier Developer with Docker: Tricks of the Trade
Be a Happier Developer with Docker: Tricks of the TradeBe a Happier Developer with Docker: Tricks of the Trade
Be a Happier Developer with Docker: Tricks of the Trade
 
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
 
Greenfield Puppet: Getting it right from the start
Greenfield Puppet: Getting it right from the startGreenfield Puppet: Getting it right from the start
Greenfield Puppet: Getting it right from the start
 

Recently uploaded

AI in the Workplace Reskilling, Upskilling, and Future Work.pptx
AI in the Workplace Reskilling, Upskilling, and Future Work.pptxAI in the Workplace Reskilling, Upskilling, and Future Work.pptx
AI in the Workplace Reskilling, Upskilling, and Future Work.pptx
Sunil Jagani
 
Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!
Tobias Schneck
 
Y-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PPY-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PP
c5vrf27qcz
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
operationspcvita
 
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptxPRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
christinelarrosa
 
Discover the Unseen: Tailored Recommendation of Unwatched Content
Discover the Unseen: Tailored Recommendation of Unwatched ContentDiscover the Unseen: Tailored Recommendation of Unwatched Content
Discover the Unseen: Tailored Recommendation of Unwatched Content
ScyllaDB
 
AppSec PNW: Android and iOS Application Security with MobSF
AppSec PNW: Android and iOS Application Security with MobSFAppSec PNW: Android and iOS Application Security with MobSF
AppSec PNW: Android and iOS Application Security with MobSF
Ajin Abraham
 
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving
 
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance PanelsNorthern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving
 
"NATO Hackathon Winner: AI-Powered Drug Search", Taras Kloba
"NATO Hackathon Winner: AI-Powered Drug Search",  Taras Kloba"NATO Hackathon Winner: AI-Powered Drug Search",  Taras Kloba
"NATO Hackathon Winner: AI-Powered Drug Search", Taras Kloba
Fwdays
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Neo4j
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
LizaNolte
 
ScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking ReplicationScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking Replication
ScyllaDB
 
Introducing BoxLang : A new JVM language for productivity and modularity!
Introducing BoxLang : A new JVM language for productivity and modularity!Introducing BoxLang : A new JVM language for productivity and modularity!
Introducing BoxLang : A new JVM language for productivity and modularity!
Ortus Solutions, Corp
 
Christine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptxChristine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptx
christinelarrosa
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
UiPathCommunity
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
AstuteBusiness
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
Neo4j
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
DianaGray10
 
Must Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during MigrationMust Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during Migration
Mydbops
 

Recently uploaded (20)

AI in the Workplace Reskilling, Upskilling, and Future Work.pptx
AI in the Workplace Reskilling, Upskilling, and Future Work.pptxAI in the Workplace Reskilling, Upskilling, and Future Work.pptx
AI in the Workplace Reskilling, Upskilling, and Future Work.pptx
 
Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!
 
Y-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PPY-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PP
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
 
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptxPRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
PRODUCT LISTING OPTIMIZATION PRESENTATION.pptx
 
Discover the Unseen: Tailored Recommendation of Unwatched Content
Discover the Unseen: Tailored Recommendation of Unwatched ContentDiscover the Unseen: Tailored Recommendation of Unwatched Content
Discover the Unseen: Tailored Recommendation of Unwatched Content
 
AppSec PNW: Android and iOS Application Security with MobSF
AppSec PNW: Android and iOS Application Security with MobSFAppSec PNW: Android and iOS Application Security with MobSF
AppSec PNW: Android and iOS Application Security with MobSF
 
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024
 
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance PanelsNorthern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
 
"NATO Hackathon Winner: AI-Powered Drug Search", Taras Kloba
"NATO Hackathon Winner: AI-Powered Drug Search",  Taras Kloba"NATO Hackathon Winner: AI-Powered Drug Search",  Taras Kloba
"NATO Hackathon Winner: AI-Powered Drug Search", Taras Kloba
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
 
ScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking ReplicationScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking Replication
 
Introducing BoxLang : A new JVM language for productivity and modularity!
Introducing BoxLang : A new JVM language for productivity and modularity!Introducing BoxLang : A new JVM language for productivity and modularity!
Introducing BoxLang : A new JVM language for productivity and modularity!
 
Christine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptxChristine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptx
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
 
Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |Astute Business Solutions | Oracle Cloud Partner |
Astute Business Solutions | Oracle Cloud Partner |
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
 
Must Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during MigrationMust Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during Migration
 

Medicine show2 Drupal Bristol Camp 2015

  • 1.
  • 2. blue-bag ANSIBLE & DRUPAL MEDICINE SHOW
 (PART TWO) DRUPAL BRISTOL CAMP JULY 2015 GEORGE BOOBYER
 BLUE-BAG
  • 3. blue-bag BLUE-BAG GEORGE BOOBYER
 DRUPAL: iAUGUR
 GEORGE@BLUE-BAG.COM TWITTER: iBLUEBAG www.blue-bag.com Established in 2000
 The year before Drupal 1.0
  • 5. blue-bag ANSIBLE & DRUPAL: MEDICINE SHOW w Special formula
 Requires prescription
 Ingredients:
 Forked Roles Custom Roles
 Custom Inventory & Vars
 Featured multistage Playbook w Limited Edition
 Ingredients:
 multistage playbook
 Multiple plays, Forked Roles Custom Roles, Vault Plugins & filters, 
 Complex Inventory & Vars w Original Recipe
 Available over the counter
 Ingredients:
 Galaxy Roles Custom Vars
 Simple Playbook
 Documentation
  • 6. blue-bag ANSIBLE & DRUPAL: TOP 10 1. Docker 2. Kubernetes 3. Taiga 4. Apache Mesos 5. OpenStack 6. Ansible 7. ownCloud 8. Apache Hadoop 9. Drupal 10.OpenDaylight http://opensource.com/business/14/12/ top-10-open-source-projects-2014 Top 10 open source projects in 2014
  • 7. blue-bag ANSIBLE BASICS Agentless Uses SSH YAML for configuration Modules Open source not just provisioning “Ansible is a radically simple IT automation system. It handles configuration-management, application deployment, cloud provisioning, ad-hoc task-execution, and multinode orchestration. Python Extensible
  • 10. blue-bag ANSIBLE GOALS Don’t log into servers No need for bash scripts
  • 11. blue-bag ANSIBLE GOALS Don’t log into servers No need for bash scripts Document build and configuration
 (in source control)
  • 12. blue-bag ANSIBLE GOALS Don’t log into servers No need for bash scripts Document build and configuration
 (in source control) Idempotent (idem-what’s-that-again?)
  • 13. blue-bag ANSIBLE GOALS Don’t log into servers No need for bash scripts Document build and configuration
 (in source control) Idempotent (idem-what’s-that-again?) Maintain configuration across inventory
  • 14. blue-bag ANSIBLE GOALS Don’t log into servers No need for bash scripts Document build and configuration
 (in source control) Idempotent (idem-what’s-that-again?) Maintain configuration across inventory Retain & share knowledge
  • 16. blue-bag ANSIBLE DOCUMENTATION Github - Code and examples:
 https://github.com/ansible/ansible
 Ansible Documentation: http://docs.ansible.com/ Ansible best practices:
 http://docs.ansible.com/playbooks_best_practices.html Ansible Galaxy: - https://galaxy.ansible.com
 Ansible Project - Google Groups:
 https://groups.google.com/forum/#!forum/ansible-project
  • 23. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks
  • 24. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks
  • 25. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks Autodiscovery
  • 26. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks Autodiscovery Tags
  • 27. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks Autodiscovery Tags Variables
  • 28. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks Autodiscovery Tags Variables Vault
  • 29. blue-bag ANSIBLE BASICS Modules Vagrant
 local setup Ansible command Playbooks Inventory Limiting tasks Roles / Tasks Autodiscovery Tags Variables Vault Testing
  • 30. blue-bag ANSIBLE BASICS: MODULES Command Shell Lineinfile File Copy Template Apt / Yum / Homebrew Unarchive apache2_module htpasswd Git Cloud: RAX, DO, EC3
  • 31. blue-bag ANSIBLE BASICS: MODULES $ ansible-docs -l … alternatives Manages alternative programs for common commands apache2_module enables/disables a module of the Apache2 webserver apt Manages apt-packages apt_key Add or remove an apt key apt_repository Add and remove APT repositories apt_rpm apt_rpm package manager assemble Assembles a configuration file from fragments assert Fail with custom message at Schedule the execution of a command or script file via the at command. authorized_key Adds or removes an SSH authorized key … patch Apply patch files using the GNU patch tool.
  • 32. blue-bag ANSIBLE BASICS: MODULES $ ansible-docs -s patch
 - name: Apply patch files using the GNU patch tool. action: patch basedir # Path of a base directory in which the patch file will be applied. May be omitted when `dest' option is specified, otherwise required. dest # Path of the file on the remote machine to be patched. The names of the files to be patched are usually taken from the patch file, but if there's just one file to be patched it can specified with this remote_src # If False, it will search for src at originating/master machine, if True it will go to the remote/target machine for the src. Default is False. src= # Path of the patch file as accepted by the GNU patch tool. strip # Number that indicates the smallest prefix containing leading slashes that will be stripped from each file name found in the patch file. For more information see the strip parameter of the GNU patch to Note old syntax! - name: Apply patch files using the GNU patch tool patch: src: /tmp/index.html.patch dest: /var/www/index.html
  • 33. blue-bag ANSIBLE / VAGRANT "## demo    "## Vagrantfile    %## hosts # -*- mode: ruby -*- # vi: set ft=ruby : VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.ssh.insert_key = false config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "256"] end # Application server 1. config.vm.define "app1" do |app| app.vm.hostname = "bbdemo-app1.dev" app.vm.box = "chef/debian-7.4" app.vm.network :private_network, ip: "192.168.100.120" end # Application server 2. config.vm.define "app2" do |app| app.vm.hostname = "bbdemo-app2.dev" app.vm.box = "chef/debian-7.4" app.vm.network :private_network, ip: "192.168.100.121" end # Database server. config.vm.define "db" do |db| db.vm.hostname = "bbdemo-db.dev" db.vm.box = "chef/debian-7.4" db.vm.network :private_network, ip: "192.168.100.122" end end # Application servers [webservers] 192.168.100.120 192.168.100.121 # Database server [dbservers] 192.168.100.122 # Group 'multi' with all servers [multi:children] app db # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key
  • 34. blue-bag ANSIBLE BASICS $ ansible all -i hosts -a "free -m" -u vagrant
  • 35. blue-bag ANSIBLE BASICS $ ansible all -i hosts -a "free -m" -u vagrant 192.168.100.121 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767 192.168.100.120 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767 192.168.100.122 | success | rc=0 >> total used free shared buffers cached Mem: 244 74 170 0 6 34 -/+ buffers/cache: 32 212 Swap: 767 0 767
  • 36. blue-bag ANSIBLE BASICS: DEFAULTS "## demo    "## Vagrantfile    "## ansible.cfg    "## hosts    %## log %## ansible.log [defaults] log_path = log/ansible.log hostfile = ./hosts … [multi] ansible_ssh_user=vagrant … $ansible all -i hosts -a "free -m" -u vagrant $ansible all -a "free -m"
  • 37. blue-bag ANSIBLE BASICS: SUCCINCT CALLS $ ansible all -a “date" 192.168.100.122 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015 192.168.100.121 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015 192.168.100.120 | success | rc=0 >> Tue Jan 13 10:14:15 UTC 2015
  • 38. blue-bag ANSIBLE BASICS: CALLING MODULES $ansible all -s -m apt -a "pkg=ntp state=installed_ update_cache=yes" 192.168.100.121 | success >> { "changed": true, "stderr": "", "stdout": "Reading package lists...nBuilding dependency tree...nReading state information...nThe following extra packages will be installed:n libopts25nSuggested packages:n ntp-docnThe following NEW packages will be installed:n libopts25 ntpn0 upgraded, 2 newly installed, 0 to remove and 72 not upgraded.nNeed to get 565 kB/638 kB of archives.nAfter this operation, 1364 kB of additional disk space will be used.nGet:1 http://mirrors.kernel.org/debian/ wheezy/main ntp amd64 1:4.2.6.p5+dfsg-2+deb7u1 [565 kB] nFetched 565 kB in 13s (40.8 kB/s)nSelecting previously unselected package libopts25.r n(Reading database ... r(Reading database ... 5%r(Reading database ... 10%r(Reading database ... 15%r(Reading database ... 20%r(Reading database ... 25%r(Reading database ... 30%r(Reading database ... 35%r(Reading database ... 40%r(Reading database ... 45%r(Reading database ... 50%r(Reading database ... 55%r(Reading database ... 60%r(Reading database ... 65%r(Reading database ... 70%r(Reading database ... 75%r(Reading database ... 80%r(Reading database ... 85%r(Reading database ... 90%r(Reading database ... 95%r(Reading database ... 100%r(Reading database ... 37414 files and directories currently installed.)rnUnpacking libopts25 (from .../libopts25_1%3a5.12-0.1_amd64.deb) ...rnSelecting previously unselected package ntp.rnUnpacking ntp (from .../ntp_1%3a4.2.6.p5+dfsg-2+deb7u1_amd64.deb) ...rnProcessing triggers for man-db ...rnSetting up libopts25 (1:5.12-0.1) ...rnSetting up ntp (1:4.2.6.p5+dfsg-2+deb7u1) ...rnStarting NTP server: ntpd.rn" }
  • 39. blue-bag ANSIBLE BASICS: THE PLAYBOOK --- - hosts: all gather_facts: true sudo: true tasks: - name: NTP | Install NTP apt: name: ntp state: installed update_cache: yes cache_valid_time: 3600 "## demo    "## Vagrantfile    "## ansible.cfg    "## hosts    %## log %## ansible.log "## playbook.yml $ ansible-playbook playbook.yml $ansible multi -s -m apt -a "pkg=ntp state=installed_ update_cache=yes"
  • 40. blue-bag ANSIBLE BASICS: ROLE STRUCTURE Use Galaxy Init to create roles
  • 41. blue-bag ANSIBLE BASICS: ROLE STRUCTURE $ansible-galaxy init 'myrole' Use Galaxy Init to create roles
  • 42. blue-bag ANSIBLE BASICS: ROLE STRUCTURE $ansible-galaxy init 'myrole' "## myrole "## README.md "## defaults & %## main.yml "## files "## handlers & %## main.yml "## meta & %## main.yml "## tasks & %## main.yml "## templates %## vars %## main.yml Use Galaxy Init to create roles
  • 44. blue-bag ANSIBLE BASICS: PROJECT STRUCTURE Use a template for Ansible projects. Common structure Autodiscovery
  • 45. blue-bag ANSIBLE BASICS: PROJECT STRUCTURE https://github.com/iAugur/ansible-playbook-template Use a template for Ansible projects. Common structure Autodiscovery
  • 46. blue-bag ANSIBLE BASICS: PROJECT STRUCTURE https://github.com/iAugur/ansible-playbook-template "## LICENSE "## README.md "## ansible.cfg "## common & "## handlers & & %## main.yml & %## vars & %## main.yml "## group_vars & %## all.yml "## host_vars & %## local.yml "## hosts "## log "## playbook.yml "## roles %## base "## README.md "## defaults & %## main.yml "## files "## handlers & %## main.yml "## meta & %## main.yml "## tasks & %## main.yml "## templates %## vars %## main.yml Use a template for Ansible projects. Common structure Autodiscovery
  • 47. blue-bag ANSIBLE BASICS: MASTER PLAY --- #-------------------------- Basic core setup of servers - include: ./playbooks/play-provision-core.yml when: do_provision tags: [provision_core] #-------------------------- Basic core setup of webservers - include: ./playbooks/play-provision-webserver.yml when: do_provision tags: [provision_webserver] #-------------------------- Basic core setup of dbservers - include: ./playbooks/play-provision-dbserver.yml when: do_provision tags: [provision_dbserver] #-------------------------- setup of websites ... ./playbook.yml
  • 49. blue-bag # Application servers [webservers] 192.168.100.120 192.168.100.121 # Database server [dbservers] 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key ANSIBLE BASICS: HOSTS & GROUPS
  • 50. blue-bag # Application servers [webservers] 192.168.100.120 192.168.100.121 # Database server [dbservers] 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key # Application servers [webservers] bbdemo-app1.dev bbdemo-app2.dev 192.168.100.120 192.168.100.121 # Database server [dbservers] bbdemo-db.dev 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key ANSIBLE BASICS: HOSTS & GROUPS
  • 51. blue-bag # Application servers [webservers] 192.168.100.120 192.168.100.121 # Database server [dbservers] 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key # Application servers [webservers] bbdemo-app1.dev bbdemo-app2.dev 192.168.100.120 192.168.100.121 # Database server [dbservers] bbdemo-db.dev 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key # Application servers [webservers] bbdemo-app1.dev bbdemo-app2.dev # Database server [dbservers] bbdemo-db.dev # Group 'multi' with all servers [multi:children] webservers dbservers ANSIBLE BASICS: HOSTS & GROUPS
  • 52. blue-bag # Application servers [webservers] 192.168.100.120 192.168.100.121 # Database server [dbservers] 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key # Application servers [webservers] bbdemo-app1.dev bbdemo-app2.dev 192.168.100.120 192.168.100.121 # Database server [dbservers] bbdemo-db.dev 192.168.100.122 # Group 'multi' with all servers [multi:children] webservers dbservers # Variables that will be applied to all servers" [multi:vars] ansible_ssh_user=vagrant ansible_ssh_private_key_file=~/.vagrant.d/ insecure_private_key # Application servers [webservers] bbdemo-app1.dev bbdemo-app2.dev # Database server [dbservers] bbdemo-db.dev # Group 'multi' with all servers [multi:children] webservers dbservers ANSIBLE BASICS: HOSTS & GROUPS # ../host_vars/bbdemo-app1.dev ——- ansible_ssh_host: 192.168.100.120 # ../host_vars/bbdemo-app2.dev ——- ansible_ssh_host: 192.168.100.121 # ../host_vars/bbdemo-db.dev ——- ansible_ssh_host: 192.168.100.122 # ../group_vars/all ——- ansible_ssh_user: vagrant ansible_ssh_private_key_file: ~/.vagrant.d/ insecure_private_key # ../group_vars/webservers ——- http_port: 80 # ../group_vars/dbservers ——- db_port: 3306
  • 53. blue-bag ANSIBLE BASICS: LIMITING HOSTS List Hosts
 $ansible-playbook --list-hosts main.yml Limit to Host
 $ansible-playbook --limit app1 main.yml Limit to group
 $ansible-playbook -l webservers main.yml Limit to set (note quotes!)
 $ansible-playbook -l 'multi:!webservers' main.yml More commonly in a playbook:
 - hosts: multi:&dbservers
 gather_facts: true
 tags: [database_setup]
 roles:
 - { role: config-db , tags: ["dbserver-base"]}

  • 54. blue-bag ANSIBLE BASICS: TAGS - hosts: servers gather_facts: true sudo: true tags: [provision_core] roles: - { role: ansible-role-first5base , tags: ['base'], sudo: true }
 - name: Set hostname hostname: name={{ inventory_hostname_short }} sudo: true tags: - configuration - hosts
 In a playbook In a role / task
  • 55. blue-bag playbook: playbook.yml play #1 (servers): TAGS: [provision_core] TASK TAGS: [check_vars, common, provision, provision_core, user] play #2 (servers): TAGS: [provision_core] TASK TAGS: [base, check_vars, common, git, hosts, mail, newrelic, ntp, provision_core, rootmail, ssh, timezone, user] play #3 (webservers:&servers): TAGS: [provision_webserver] TASK TAGS: [apache, base, drush, newrelic-php, php, provision_webserver, redis, security, ssl, webserver-config] play #4 (dbservers:&servers): TAGS: [provision_dbserver] TASK TAGS: [base, mysql, provision_dbserver] play #5 (webservers:!servers): TAGS: [sites_setup] TASK TAGS: [base_setup, sites_setup, webserver-base] play #6 (servers): TAGS: [] TASK TAGS: [reboot] ANSIBLE BASICS: TAGS List tags
 $ansible-playbook main.yml --list-tags
  • 56. blue-bag ANSIBLE BASICS: TAGS Limit to tags
 $ansible-playbook playbook.yml --tags 'provision_core' Skip tags (note quotes!)
 $ansible-playbook playbook.yml --skip-tags 'apache' Exclude tags set (note quotes!)
 $ansible-playbook playbook.yml --tags 'git:!repo_pull' Tag plays and roles:
 - hosts: multi:&dbservers
 gather_facts: true
 tags: ['database_setup']
 roles:
 - { role: config-db , tags: ['dbserver-base']}

  • 57. blue-bag Variable types:
 myvar: 'isastring'
 mynumvar: 80
 canref: "{{ myvar }}"
 lists: 
 - onevar
 - twovar
 
 dictionary:
 onevar: 1,
 twovar: 'two'
 
 dictionarylist:
 - {
 onevar: 1,
 twovar: 'two' } - {
 threevar: 1,
 fourvar: 'two' } truebool: True, true yes, 1 falsebool: False, false no, 0 ANSIBLE BASICS: VARIABLES Variable naming conventions
 start with a letter
 don’t use hyphens
 namespace - use your role names
 - hosts: all remote_user: root vars: favcolor: blue vars_files: - /vars/external_vars.yml tasks: - name: this is just a placeholder command: /bin/echo foo Inline or included files ansible-playbook play.yml --extra-vars "user=vagrant" Pass on command line
  • 58. blue-bag HOST VARSHOSTVARS (ANSIBLE GATHERED, group_vars, host_vars) : ansible_all_ipv4_addresses: [10.0.2.15, 192.168.100.120] ansible_all_ipv6_addresses: ['fe80::a00:27ff:fe8f:4176', 'fe80::a00:27ff:fe00:94a6'] ansible_architecture: x86_64 ansible_bios_date: 12/01/2006 ansible_bios_version: VirtualBox ansible_cmdline: {BOOT_IMAGE: /vmlinuz-3.2.0-4-amd64, debian-installer: en_US, quiet: true, ro: true, root: /dev/mapper/packer--debian--7-root} ansible_date_time: {date: '2015-04-10', day: '10', epoch: '1428649901', hour: '07', iso8601: '2015-04-10T07:11:41Z', iso8601_micro: '2015-04-10T07:11:41.551010Z', minute: '11', month: '04', second: '41', time: '07:11:41', tz: UTC, tz_offset: '+0000', weekday: Friday, year: '2015'} ansible_default_ipv4: {address: 10.0.2.15, alias: eth0, gateway: 10.0.2.2, interface: eth0, macaddress: '08:00:27:8f:41:76', mtu: 1500, netmask: 255.255.255.0, network: 10.0.2.0, type: ether} ansible_default_ipv6: {} ansible_devices: sda: holders: [] host: 'IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)' model: VBOX HARDDISK partitions: sda1: {sectors: '497664', sectorsize: 512, size: 243.00 MB, start: '2048'} sda2: {sectors: '2', sectorsize: 512, size: 1.00 KB, start: '501758'} sda5: {sectors: '83382272', sectorsize: 512, size: 39.76 GB, start: '501760'} removable: '0' rotational: '1' scheduler_mode: cfq sectors: '83886080' sectorsize: '512' size: 40.00 GB support_discard: '0' vendor: ATA ansible_distribution: Debian ansible_distribution_major_version: '7' ansible_distribution_release: wheezy ansible_distribution_version: '7.4' ... gather_facts: true
  • 59. blue-bag ANSIBLE BASICS: VARIABLES Variable Precedence extra vars (-e in the command line) always win then comes connection variables defined in inventory (ansible_ssh_user, etc) then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc) then comes the rest of the variables defined in inventory then comes facts discovered about a system then "role defaults", which are the most "defaulty" and lose in priority to everything. https://docs.ansible.com/playbooks_variables.html Override once - put variables in sensible places Role vars
 Use defaults as a template for the variables to be overridden
 Use vars/main.yml as constants that don't get overridden
 e.g. Apache Daemon:
 Debian is 'apache2' 
 Centos is 'httpd'
 Role authors - name vars with role name to avoid collisions.
  • 60. blue-bag ANSIBLE BASICS: VARIABLES Better to put your vars in one correct place. Common uses for precedence role defaults (defaults/main.yml - apache_listen_port:80) group_vars/all (http_port: 80) group_vars/webservers (apache_listen_port: "{{http_port}}") host_vars/web01.example.com (apache_listen_port: "8080") command line extra vars -e (not so common) Use Jinja filters (sparingly) - more later ...
  • 61. blue-bag ANSIBLE BASICS: VAULT "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 62. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 63. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT Ignore any plain text password files
 "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 64. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT Ignore any plain text password files
 Use separate files for role vars "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 65. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT Ignore any plain text password files
 Use separate files for role vars Put all secrets in Ansible Vault
 use a pattern: secrets*.yml "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 66. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT Ignore any plain text password files
 Use separate files for role vars Put all secrets in Ansible Vault
 use a pattern: secrets*.yml Encrypt all secrets: "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml
  • 67. blue-bag ANSIBLE BASICS: VAULT Put all of your project files in GIT Ignore any plain text password files
 Use separate files for role vars Put all secrets in Ansible Vault
 use a pattern: secrets*.yml Encrypt all secrets: "## all &   "## all.yml &   "## firewall.yml &   "## first5base.yml &   "## first5ntp.yml &   "## first5ssh.yml &   "## first5user.yml &   "## newrelic.yml &   "## secrets-s3cmd.yml &   "## secrets-ssh.yml &   "## secrets-sql.yml &   %## secrets.yml $ansible-vault encrypt secrets*.yml
  • 69. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text
  • 70. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG'
  • 71. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG' Place secrets in vault
 $ansible-vault encrypt group_vars/all/secrets.yml
 $ansible-vault decrypt group_vars/all/secrets.yml
  • 72. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG' Place secrets in vault
 $ansible-vault encrypt group_vars/all/secrets.yml
 $ansible-vault decrypt group_vars/all/secrets.yml Automate vault
 $ansible-playbook play.yml--ask-vault-pass
 $ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt
  • 73. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG' Place secrets in vault
 $ansible-vault encrypt group_vars/all/secrets.yml
 $ansible-vault decrypt group_vars/all/secrets.yml Automate vault
 $ansible-playbook play.yml--ask-vault-pass
 $ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt Add Vault password file location to ansible.cfg
 [defaults]
 vault_password_file = vault.txt
  • 74. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG' Place secrets in vault
 $ansible-vault encrypt group_vars/all/secrets.yml
 $ansible-vault decrypt group_vars/all/secrets.yml Automate vault
 $ansible-playbook play.yml--ask-vault-pass
 $ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt Add Vault password file location to ansible.cfg
 [defaults]
 vault_password_file = vault.txt Run plays as normal
 $ansible-playbook playbook.yml
  • 75. blue-bag ANSIBLE BASICS: VAULT Create a vault Password file (vault_pass.txt)
 gitignore vault password files
 add single line with vault password in plain text Add Sudo password to all/secrets.yml
 ansible_sudo_pass: 'ECyDPIm%Jpw5JVIFI@FWX2hLuG' Place secrets in vault
 $ansible-vault encrypt group_vars/all/secrets.yml
 $ansible-vault decrypt group_vars/all/secrets.yml Automate vault
 $ansible-playbook play.yml--ask-vault-pass
 $ansible-playbook play.yml--vault-password-file ~/.vault_pass.txt Add Vault password file location to ansible.cfg
 [defaults]
 vault_password_file = vault.txt Run plays as normal
 $ansible-playbook playbook.yml Working with teams - use GPG: 
 http://benincosa.com/?p=3235
  • 76. blue-bag ANSIBLE BASICS: VAULT --- aws_access_key: 'ASKGHNSJYHRSUPERDUPERSECRETWGDFSG' aws_secret_key: 'BaxBZqJhVreAllY!dpv4SNvFJd-X6l+cz' $ANSIBLE_VAULT;1.1;AES256 31366634380a6566353532626532316339631366634380a6566353532626532316339631366634380a656635353 2626532316339631366634380a6566353532626532316339631366634380a656635353262653231633963136663 4380a6566353532626532316339631366634380a6566353532626532316339631366634380a6566353532626532 316339631366634380a6566353532626532316339631366634380a6566353532626532316339631366634380a65 66353532626532316339631366634380a6566353532626532316339631366634380a65663535326265323163396 31366634380a65663535326265323163396 Create a secrets file 
 group_vars/all/secrets-s3.yml
 Encrypt the secrets file 
 ansible-vault encrypt group_vars/all/secrets-s3.yml

  • 78. blue-bag ANSIBLE BASICS: TESTING Test play books
 $ansible-playbook --syntax-check --list-tasks playbook.yml
  • 79. blue-bag ANSIBLE BASICS: TESTING Test play books
 $ansible-playbook --syntax-check --list-tasks playbook.yml Check mode
 $ansible-playbook playbook.yml --check
 $ansible-playbook playbook.yml --check --diff
  • 80. blue-bag ANSIBLE BASICS: TESTING Test play books
 $ansible-playbook --syntax-check --list-tasks playbook.yml Check mode
 $ansible-playbook playbook.yml --check
 $ansible-playbook playbook.yml --check --diff Test against Virtual machines
 $ ansible-playbook playbook.yml --limit local
  • 81. blue-bag ANSIBLE: ORIGINAL RECIPE w
 Original Recipe Availableoverthecounter
 Contents:
 Galaxy Roles
 Custom Vars
 Simple Playbook x Multipurpose in a jar Want a quick solution? Easy to maintain Generic Documented examples
  • 82. blue-bag --- - hosts: all
 
 roles: - geerlingguy.mysql - geerlingguy.apache - geerlingguy.php A LAMP SERVER IN SIX LINES OF YAML Jeff Geerling - Ansible for Devops ANSIBLE: ORIGINAL RECIPE
  • 83. blue-bag For all local development use VLAD http://git.io/DCB-Vlad http://www.drupalvm.com/ For all local development use Drupal VM s ANSIBLE: ORIGINAL RECIPE OTHER BRANDS
  • 84. blue-bag ANSIBLE: SPECIAL FORMULA 
 w Special Formula
 Noprescription!
 Contents:
 Forked Roles Custom Roles
 Custom Inventory & Vars
 Featured multistage playbook x Mostly Galaxy Roles Customised to needs Match local & production Separate plays
  • 85. blue-bag ANSIBLE: SPECIAL FORMULA Learning about lists & filters Coding with plugins Working with files Creating a simple role
  • 86. blue-bag ANSIBLE: FILTERS AND LISTS Ansible lists and loops
 with_items
 with_nested
 with_first_found
 with_indexed_items
 with_nested
 with_fileglob Jinja Filters
 default
 join
 union
 list Python Plugins
 where you code "## README.md "## ansible.cfg "## common &   %## handlers &      %## main.yml "## group_vars &   %## all.yml "## host_vars &   %## local.yml "## hosts "## plugins &   "## filter_plugins &      %## fetchlistfromdict.py "## roles [defaults] log_path = log/ansible.log hostfile = ./hosts filter_plugins = ./plugins/filter_plugins
  • 87. blue-bag HOST VARS HOSTVARS (ANSIBLE GATHERED, group_vars, host_vars) : ansible_all_ipv4_addresses: [10.0.2.15, 192.168.100.120] ansible_all_ipv6_addresses: ['fe80::a00:27ff:fe8f:4176', 'fe80::a00:27ff:fe00:94a6'] ansible_architecture: x86_64 ansible_default_ipv4: {address: 10.0.2.15, alias: eth0, gateway: 10.0.2.2, interface: eth0, macaddress: '08:00:27:8f:41:76', mtu: 1500, netmask: 255.255.255.0, network: 10.0.2.0, type: ether} ansible_distribution: Debian ansible_distribution_major_version: '7' ansible_distribution_release: wheezy ansible_distribution_version: '7.4' ... gather_facts: true
  • 88. blue-bag ANSIBLE: WORKING WITH LISTS Get a list of the IPS from a dictionary & host group: vars: firewall_whitelist: - { name: 'local', ip: "192.168.100.1" } - { name: 'fred', ip: '192.168.100.120' } - { name: 'bob', ip: '192.168.100.121' } firewall_whitelistgroup: "webservers" tasks: - name: Setfact for allowed ips list for chain script set_fact: allowed_ips: "{{ firewall_whitelist|map(attribute='ip')|list|unique }}" - name: Setfact list using group for var firewall_whitelistgroup set_fact: hosts_ips: "{{ groups[firewall_whitelistgroup] }}" 
 - name: Show the union of list 1 & 2 debug: msg="{{ allowed_ips|union(hosts_ips) }}"
  • 89. blue-bag ANSIBLE: WORKING WITH LISTS Flattening and joining lists: - name: Get the servers' ansible_all_ipv4_addresses
 set_fact:
 ip_list2: "{{hostvars|fetchlistfromdict(groups.multi)| map(attribute='ansible_all_ipv4_addresses')|list }}" TASK: [Show the list we obtained for ansible_all_ipv4_addresses] ************** ok: [app1] => { "msg": "[['10.0.2.15', '192.168.100.120'], ['10.0.2.15', '192.168.100.121'], ['10.0.2.15', '192.168.100.122']]" }
  • 90. blue-bag ANSIBLE: WORKING WITH LISTS Your first Plugin! 
 ./plugins/filter_plugins/flatten_dict_values.py # This function will take a dictionary composed of sub arrays # and flatten it # e.g. # [[u'321.321.321.321', u'10.10.20.54'], [u'46.101.45.10', u'10.10.20.55']] # to # [u'321.321.321.321', u'10.10.20.54', u'123.123.123.123', u'10.10.20.55'] def flatten_dict_values(dictionary): result = [] result = reduce(list.__add__, dictionary,[]) return result class FilterModule (object): def filters(self): return { "flatten_dict_values": flatten_dict_values } # http://feldboris.alwaysdata.net/blog/python-trick-how-to-flatten-dictionaries-values-composed-of-iterables.html # http://stackoverflow.com/questions/15995/useful-code-which-uses-reduce-in-python
  • 91. blue-bag ANSIBLE: WORKING WITH FILES Copy a file Copy multiple files Work with archives Line in file Block in file Templates

  • 92. blue-bag ANSIBLE: WORKING WITH FILES Copy a file - name: Upload the exclude list for archives etc copy: src: exclude-list.txt dest: "{{ proj_site_root }}/exclude-list.txt" owner: "{{ user }}" group: "{{ user_admin_group }}" tags: [site_files]
  • 93. blue-bag ANSIBLE: WORKING WITH FILES Copy multiple files - name: Apache | Put up Holding page copy: src: "{{ item }}" dest: "{{ apache_default_site }}" owner: "{{ apache_admin_user }}" group: "{{ apache_admin_group }}" mode: 0644 with_fileglob: - default-site/*
  • 94. blue-bag ANSIBLE: WORKING WITH FILES Work with archives - name: Upload & unarchive a set of files unarchive: src: "sitefiles/{{ proj_short_name }}.tar" dest: "/tmp/{{ proj_short_name }}” copy: yes tags: [site_files]
  • 95. blue-bag ANSIBLE: WORKING WITH FILES Work with rsync - name: Rsync the uploaded files synchronize: src: "/tmp/{{ proj_short_name }}/files" dest: "{{ site_folder }}/{{ site_docroot }}/sites/default" archive: no delete: no recursive: yes # times: yes # perms: yes checksum: yes rsync_opts: "--include='.htaccess' --exclude-from '{{ proj_site_root }}/exclude-list.txt'" delegate_to: "{{ inventory_hostname }}" tags: [site_files] notify: - clear Drupal cache - Remove sync files
  • 96. blue-bag ANSIBLE: WORKING WITH FILES Line in file - name: Update git config to ignore mode changes lineinfile: dest: "{{ site_folder }}/{{ site_docroot }}/.git/config" regexp: ^[s]*filemode = true line: " filemode = false" tags: [repo_setup]
  • 97. blue-bag - name: Drupal | Write host specific aliases to project aliases blockinfile: dest: "/usr/local/share/drush/aliases/{{ proj_short_name }}.aliases.drushrc.php" backup: no marker: "## {mark} {{ proj_short_name }}.{{ stage }} ANSIBLE MANAGED BLOCK -->" content: | $aliases['{{ stage }}'] = array( 'parent' => '@{{ proj_short_name }}.common', 'root' => '{{ site_folder }}/{{ site_docroot }}', 'uri' => 'http{{ 's' if ssl_enabled else '' }}://{{ site_name }}', 'path-aliases' => array( '%dump' => $_projroot . '/backups/{{ stage }}-' . date(Ymd-His) . '.sql', '%files' => '{{ site_files_public }}', ), ); insertafter: "## -- Host aliases" tags: [drupal_setup] ANSIBLE: WORKING WITH FILES Block in file - Write block for Drush Alias
  • 98. blue-bag - name: Drupal | Write host specific aliases to project aliases blockinfile: dest: "/usr/local/share/drush/aliases/{{ proj_short_name }}.aliases.drushrc.php" backup: no marker: "## {mark} {{ proj_short_name }}.{{ stage }} ANSIBLE MANAGED BLOCK -->" content: | $aliases['{{ stage }}'] = array( 'parent' => '@{{ proj_short_name }}.common', 'root' => '{{ site_folder }}/{{ site_docroot }}', 'uri' => 'http{{ 's' if ssl_enabled else '' }}://{{ site_name }}', 'path-aliases' => array( '%dump' => $_projroot . '/backups/{{ stage }}-' . date(Ymd-His) . '.sql', '%files' => '{{ site_files_public }}', ), ); insertafter: "## -- Host aliases" tags: [drupal_setup] ## BEGIN project.staging ANSIBLE MANAGED BLOCK --> $aliases['staging'] = array( 'parent' => '@project1.common', 'root' => '/var/www/project1/staging/htdocs', 'uri' => 'https://staging.project1.com', 'path-aliases' => array( '%dump' => $_projroot . '/backups/staging-' . date(Ymd-His) . '.sql', '%files' => '/var/www/project1/staging/htdocs/sites/default/files', ), ); ## END project.staging ANSIBLE MANAGED BLOCK --> ANSIBLE: WORKING WITH FILES Block in file - Write block for Drush Alias
  • 99. blue-bag ANSIBLE: WORKING WITH FILES Templates - name: Apache | test Updated hosts template: src: config-test.conf.j2 dest: "/tmp/config-test.conf" mode: 0644 owner: root group: root validate: 'apachectl -t -f %s' register: apache_result when: vhost_updated.changed ignore_errors: yes sudo: true tags: [apache] # {{ ansible_managed }} <VirtualHost *:{{ http_port }}> ServerAdmin {{ apache_server_admin }} ServerName {{ webserver_hostname }} {% if webserver_hostname_alias is defined 
 and webserver_hostname_alias|length > 0 %}%} ServerAlias {{ webserver_hostname_alias }} {% endif %} DocumentRoot {{ apache_default_site }} {% if apache_server_status_enabled %} <Location /server-status> SetHandler server-status {% if apache_vhosts_version == '2.4' %} {% for entry in incoming_ip_whitelist %} Require ip {{ entry.ip }} {% endfor %} {% else %} Order deny,allow Deny from all Allow from 127.0.0.1 {% for entry in incoming_ip_whitelist %} Allow from {{ entry.ip }} {% endfor %} {% endif %} </Location> {% endif %}
  • 100. blue-bag A FIRST ROLE: BASIC SECURITY - FIRST 5 Lock down SSH Create users and groups Limit SSH to RSA key: 
 (no passwords / no root) Limit to AllowGroups Update APT - unattended updates security Configure hostname Configure IPTables Configure Fail2ban
  • 101. blue-bag Take what we know Research Create templates / vars Consistent across infrastructure Use tasks to implement Idempotent Version controlled Documented A FIRST ROLE: BASIC SECURITY - FIRST 5
  • 102. blue-bag FIRST5: SSH - EXAMPLE "## README.md "## defaults %## main.yml "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2
  • 103. blue-bag FIRST5: SSH - EXAMPLE "## README.md "## defaults %## main.yml "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 # Package generated configuration file # See the sshd_config(5) manpage for details # What ports, IPs and protocols we listen for Port 22 # Use these options to restrict which interfaces/protocols sshd will bind to #ListenAddress :: #ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key HostKey /etc/ssh/ssh_host_ecdsa_key #Privilege Separation is turned on for security UsePrivilegeSeparation yes # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits 768 # Logging SyslogFacility AUTH LogLevel INFO # Authentication: LoginGraceTime 120
  • 104. blue-bag FIRST5: SSH - EXAMPLE # {{ ansible_managed }} # See the sshd_config(5) manpage for details # What ports, IPs and protocols we listen for Port {{ ssh_port }} # Use these options to restrict which interfaces/protocols sshd will bind to #ListenAddress :: #ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key HostKey /etc/ssh/ssh_host_ecdsa_key #Privilege Separation is turned on for security UsePrivilegeSeparation yes # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits {{ ssh_server_key_bits }} # Logging SyslogFacility AUTH LogLevel INFO # Authentication: LoginGraceTime {{ ssh_login_grace_time }} templates/ssh_config.j2
  • 105. blue-bag FIRST5: SSH "## README.md "## defaults %## main.yml "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2 # SSH Settings # optionally change port ssh_port: 22 # reduce login grace time from 120 (2 minutes) ssh_login_grace_time: 20 # increase the server key bit encryption default=768 ssh_server_key_bits: 4096 # control the parallelism ssh_max_startups: 3:50:10 # control forwarding ssh_allow_tcp_forwarding: “no” ssh_allow_x11_forwarding: “no”
  • 106. blue-bag FIRST5: SSH --- - name: SSH Config | add ssh config file template: src:ssh_config.j2 dest:/etc/ssh/sshd_config backup:yes notify: restart ssh "## README.md "## defaults %## main.yml "## tasks &   "## main.yml &   %## ssh_config.yml "## templates &   %## ssh_config.j2
  • 107. blue-bag BASIC SECURITY: FIRST 5--- - hosts: all gather_facts: true sudo: true pre_tasks: - name: Pretask | Update apt cache apt: update_cache=yes cache_valid_time=3600 - name: Pretask | Run apt-get upgrade apt: upgrade=dist roles: - { role: base , tags: ["base"] } - { role: ntp , tags: ["ntp"] } - { role: user, tags: ["user"] } - { role: ssh , tags: ["ssh"] } - { role: fail2ban, tags: ["fail2ban"] } - { role: logwatch , tags: ["logwatch"] } - { role: exim , tags: ["exim"] } - { role: geerlingguy.firewall, tags: ["iptables"] } handlers: - include: common/handlers/main.yml Don’t reinvent the wheel
 Use Galaxy Roles
 where relevant
  • 108. blue-bag BASIC SECURITY: SSL POODLE - name: Apache security | SSL Poodle update lineinfile: dest=/etc/apache2/mods-available/ssl.conf regexp="{{ item.regexp }}" line="{{ item.line }}" backup=yes with_items: - { regexp: "^# enable only secure protocols:", line: "# enable only secure protocols: TLSv1, but not SSLv2 & SSLv3" } - { regexp: "^SSLProtocol", line: "SSLProtocol all -SSLv2 -SSLv3" } notify: restart apache tags: - poodle
  • 109. blue-bag BASIC SECURITY: SSL POODLE - name: Apache security | SSL Poodle update template: src=ssl-poodle.conf.j2 dest=/etc/apache2/conf.d/ssl.conf notify: restart apache tags: - poodle # {{ ansible_managed }} <IfModule mod_ssl.c> # enable only secure protocols: TLSv1, but not SSLv2 & SSLv3 SSLProtocol all -SSLv2 -SSLv3 </IfModule> SSL-POODLE.CONF.J2
  • 110. blue-bag BASIC SECURITY: DRUPAL SECURITY Ansible role quickly address 
 security issues such as 
 SA-CORE-2014-005 Inventory of Drupal sites Task to apply patch Test for vulnerability Re-use for future patches
  • 111. blue-bag BASIC SECURITY: DRUPAL SECURITY--- ansible_ssh_host: 123.123.123.123 webroot: "/var/www" drupal7_sites: - { drupal_docroot: "{{ webroot }}/example.d7.site/live/htdocs”, vhost_name: “live.example.d7.site” } - { drupal_docroot: "{{ webroot }}/example.d7.site/dev/htdocs", vhost_name: “dev.example.d7.site” } - { drupal_docroot: "{{ webroot }}/example.d7.site/staging/htdocs", vhost_name: “staging.example.d7.site” } drupal6_sites: - { drupal_docroot: "{{ webroot }}example.d6.site", vhost_name: "example.d6.site" } host_vars: example.server.net
  • 112. blue-bag BASIC SECURITY: DRUPAL SECURITY --- # tasks file for SA-CORE-2014-005-D7 - name: Drupal7 patch | get patch | SA-CORE-2014-005-D7. get_url: url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch dest: /tmp/SA-CORE-2014-005-D7.patch - name: Drupal7 patch | Apply the patch from the drupal docroot. shell: "patch -p1 < /tmp/SA-CORE-2014-005-D7.patch chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal7_sites }}" - name: Drupal7 patch | Clear Drupal caches. command: "drush cc all chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal7_sites }}" Role: SA-CORE-2014-005-D7 - main.yml see blog post: http://www.midwesternmac.com/blogs/jeff-geerling/fixing-drupal-fast-using
  • 113. blue-bag BASIC SECURITY: DRUPAL SECURITY - name: Drupal6 patch | get patch | SA-CORE-2014-005-D7. copy: src=dbtng.patch dest=/tmp/SACORE2014005D6-dbtng.patch owner=root group=root mode=0644 - name: Drupal6 patch | Apply the patch from the drupal docroot. shell: "patch -p1 < /tmp/SACORE2014005D6-dbtng.patch chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal6_sites }}" tags: D6 Role: SA-CORE-2014-005-D7 - main.yml - (cont..)
  • 114. blue-bag BASIC SECURITY: DRUPAL SECURITY --- # tasks file for SA-CORE-2014-005-D7 - name: Drupal7 patch | get patch | SA-CORE-2014-005-D7. get_url: url: https://www.drupal.org/files/issues/SA-CORE-2014-005-D7.patch dest: /tmp/SA-CORE-2014-005-D7.patch - name: Drupal7 patch | Apply the patch from the drupal docroot. patch: remote_src: true src: /tmp/SA-CORE-2014-005-D7.patch basedir: "{{ item.drupal_docroot }}” with_items: - "{{ drupal7_sites }}" - name: Drupal7 patch | Clear Drupal caches. command: "drush cc all chdir={{ item.drupal_docroot }}" with_items: - "{{ drupal7_sites }}" Role: SA-CORE-2014-005-D7 - main.yml - UPDATED - not tested
  • 115. blue-bag ANSIBLE: COOK YOUR OWN 
 Limited Edition
 Prescription only Contents:
 Featured multistage Playbook
 Multiple plays
 Forked Roles Custom Roles Vault Plugins & filters
 Complex Inventory & Vars
 l Drupal
 x Fully customisable Extensible Secure Manage production Manage host / server / db assignment Operating heavy machinery?
 Non drowsy!
  • 116. blue-bag ANSIBLE: INVENTORY DRIVEN SETUP Ansible iterates hosts Not always one site per server Simple to reassign sites / dbs to new servers Keeps all vars together at appropriate 'level' Structured Inheritance of vars - e.g. Project vars Separates sites & failures Can use handlers for things like Drush CC
 without cc all sites because one changed
  • 117. blue-bag ANSIBLE: INVENTORY DRIVEN SETUP apache_vhosts: - { servername: "www.project1.com", 
 documentroot: "/var/www/html", } - { servername: "dev.project1.com", 
 documentroot: "/var/www/html", } ... drupal sites: - { sitename: "www.project1.com" db_name: "www_project1_com" db_user: "dbusername" drupal_salt: 'QPOrL...', repo_branch: "master" } - { sitename: "dev.project1.com" db_name: "dev_project1_com" db_user: "dbusername" drupal_salt: 'QPOrL...', repo_branch: "master" } host_vars/www.project1.com.yml servername: "www.project1.com" 
 documentroot: "{{ proj_root }}" sitename: "www.project1.com" db_name: "www_project1_com" db_user: "{{ proj_db_user }}" drupal_salt: "{{ proj_salt }}" repo_branch: "master" stage: live host_vars/dev.project1.com.yml servername: "dev.project1.com" 
 documentroot: "{{ proj_root }}" sitename: "dev.project1.com" db_name: "dev_project1_com" db_user: "{{ proj_db_user }}" drupal_salt: "{{ proj_salt }}" repo_branch: "dev" stage: dev OR
  • 118. blue-bag ANSIBLE: HACKING THE INVENTORY #### inventory groups # group for project # /group_vars/project1.yml [project1] www.project1.co.uk dev.project1.co.uk staging.project1.co.uk www_project1_co_uk dev_project1_co_uk staging_project1_co_uk # group for project [project2] www.project2.co.uk dev.project2.co.uk staging.project2.co.uk www_project2_co_uk dev_project2_co_uk staging_project2_co_uk # group for server setup [servers] web01.myserver.net web02.myserver.net db01.myserver.net # group for webserver vars [webservers:children] webserver01 # Group for DB server vars [dbservers:children] dbserver01 # Group to allocate 'sites to server 
 the first entry pins the group to the actual server [webserver01] web01.myserver.net www.project1.co.uk dev.project1.co.uk staging.project1.co.uk www.project2.co.uk dev.project2.co.uk staging.project2.co.uk # Group to allocate 'dbs' to server 
 the first entry pins the group to the actual server [dbserver01] db01.myserver.net www_project1_co_uk dev_project1_co_uk staging_project1_co_uk www_project2_co_uk dev_project2_co_uk staging_project2_co_uk
  • 119. blue-bag ANSIBLE: HACKING THE INVENTORY # group for server setup [servers] web01.myserver.net db01.myserver.net # group for webserver vars [webservers:children] webserver01 # Group for DB server vars [dbservers:children] dbserver01 [webserver01] web01.myserver.net www.project1.co.uk dev.project1.co.uk staging.project1.co.uk [dbserver01] db01.myserver.net www_project1_co_uk dev_project1_co_uk staging_project1_co_uk # group for server setup [servers] server01.myserver.net # group for webserver vars [webservers:children]
 server01 [server01] server01.myserver.net www.project1.co.uk dev.project1.co.uk staging.project1.co.uk www_project1_co_uk dev_project1_co_uk staging_project1_co_uk # group for server setup [servers] web01.myserver.net web02.myserver.net db01.myserver.net db02.myserver.net # group for webserver vars [webservers:children] webserver01 webserver02 # Group for DB server vars [dbservers:children] dbserver01 dbserver02 [webserver01] web01.myserver.net www.project1.co.uk [dbserver01] db01.myserver.net dev_project1_co_uk staging_project1_co_uk [webserver02] web02.myserver.net dev.project1.co.uk staging.project1.co.uk [dbserver02] db02.myserver.net www_project1_co_uk # Group for DB server vars [dbservers:children] server01 Simple Lamp Separate web / DB Multiple Servers # groups for assigning hosts to physical server
  • 120. blue-bag ANSIBLE: HACKING THE INVENTORY [webserver01] web01.myserver.net www.project1.co.uk dev.project1.co.uk staging.project1.co.uk [webserver01] web01.myserver.net www.project1.co.uk Once setup - easy to assign sites to servers preserving all vars [webserver02] web02.myserver.net dev.project1.co.uk staging.project1.co.uk
  • 121. blue-bag ANSIBLE: HACKING THE INVENTORY "## host_vars &   "## web01.myserver.net.yml &   "## db01.myserver.net.yml &   "## www.project1.co.uk.yml &   "## www_project1_co_uk.yml &   "## staging.project1.co.uk.yml &   "## staging_project1_co_uk.yml &   "## dev.project1.co.uk.yml &   %## dev_project1_co_uk.yml "## group_vars &   "## all &   &   "## all.yml &   &   "## first5base.yml &   &   %## secrets.yml &   "## dbservers &   &   "## common.yml &   &   "## firewall.yml &   &   "## mysql.yml &   &   %## secrets-mysql.yml &   "## project1 &   &   "## common.yml &   &   %## secrets.yml &   "## local.yml &   "## webserver01.yml &   %## webservers &   "## apache.yml &   "## common.yml &   "## php.yml &   %## redis.yml
  • 122. blue-bag ANSIBLE: HACKING THE INVENTORY All Group: Host Project Host Group: Type General defaults:
 common ups, API keys, server role vars etc Server type vars - (i.e. webservers)
 eg. DB port, apache cfg, server type role vars Project defaults:
 repo, folder structure, Drupal version Host details for Group Host
 e.g. SSH details Site / DB specific details
 e.g. repo branch, vhost name, db name
  • 123. blue-bag ANSIBLE: INVENTORY DRIVEN SETUP Ansible iterates hosts Simple to reassign sites / dbs to new servers Keeps all vars together at appropriate 'level' Structured Inheritance of vars - e.g. Project vars Separates sites & failures Can use handlers for things like Drush CC
 without cc all sites because one changed
  • 124. blue-bag playbook: playbook.yml play #1 (servers): TAGS: [provision_core, init] play #2 (webservers:&servers): TAGS: [provision_webserver] play #3 (dbservers:&servers): TAGS: [provision_dbserver] play #4 (webservers:!servers): TAGS: [sites_setup] # group for server setup [servers] web01.myserver.net db01.myserver.net # group for webserver vars [webservers:children] webserver01 # Group for DB server vars [dbservers:children] # groups for assigning hosts to physical server [webserver01] web01.myserver.net www.project1.co.uk dev.project1.co.uk staging.project1.co.uk [dbserver01] db01.myserver.net www_project1_co_uk dev_project1_co_uk staging_project1_co_uk ANSIBLE: HACKING THE INVENTORY
  • 125. blue-bag ANSIBLE & DRUPAL Put all sites in maintenance mode
 (Note: better to use maintenance site and re-point traffic to that so you can still work on your site on your ip) Pull latest changes Use in harmony with DrushClear caches and other Drush actions
 see https://github.com/jenitehan/drupal_update_check Extend Drush / Drupal to output JSON
 $drush pmi --format=json Automate common tasks
  • 126. blue-bag ANSIBLE & DRUPAL: EXAMPLE 2 Use Case: Commerce site Product Image updates Replace the image(s) Clear the Image Cache / styles
  • 127. blue-bag --- # vars file for base files_src_path: “/path/to/local/updated/images” files_dest_path: “/sites/default/files” files_to_update: - “fa9001.jpg" - “fa9002.jpg" image_cache_folders: - “styles/thumbnail/public/product_images" - "styles/product_gallery/public/product_images" - "styles/medium_tall/public/product_images" - "styles/product_full/public/product_images" ANSIBLE & DRUPAL: EXAMPLE 2 ./roles/updateimages/vars/main
  • 128. blue-bag - name: clear Drupal cache command: "drush cc all chdir={{ site_folder }}/{{ site_docroot }}" register: command_result failed_when: "'cache was cleared' not in command_result.stderr" ANSIBLE & DRUPAL: DRUSH Create Drush handler common/handlers/main.yml
  • 129. blue-bag - name: Image Update | Products copy: src: {{ files_src_path }}/{{ item[1] }} dest: {{ site_docroot }}/{{ files_dest_path }}/product_images/{{ item }} owner: www-data group: www-data mode: 0644 with_items: - files_to_update tags: images notify: clear drupal cache - name: Image Update | Update Cache file: path: {{ site_docroot }}/{{ files_dest_path }}/{{ item[0] }}/{{ item[1] }}
 state: absent with_nested: - image_cache_folders - files_to_update tags: images notify: clear drupal cache ANSIBLE & DRUPAL: EXAMPLE 2 ./roles/updateimages/tasks/main.yml
  • 131. blue-bag RUN UPDATE IMAGES PLAY $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 132. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Products] **************** $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 133. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Products] **************** ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) => {"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/ var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path": “/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size": 193018, "state": "file", "uid": 33} $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 134. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Products] **************** ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) => {"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/ var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path": “/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size": 193018, "state": "file", "uid": 33} changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg']) => {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/ var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33", "mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/ ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33} $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 135. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Products] **************** ok: [server.example.com] => (item=['/var/www/mysite/live/htdocs', 'fa9001.jpg']) => {"changed": false, "checksum": "9b36d8209ee8287385b1ce6af48bb033ade468a3", "dest": "/ var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "mode": "0644", "owner": "www-data", "path": “/var/www/mysite/live/htdocs/sites/default/files/product_images/fa9001.jpg", "size": 193018, "state": "file", "uid": 33} changed: [server.example.com] => (item=['/var/www/mysite/staging/htdocs', 'fa9001.jpg']) => {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/ var/www/mysite/staging/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33", "mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/ ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33} changed: [server.example.com] => (item=['/var/www/mysite/dev/htdocs', 'fa9001.jpg']) => {"changed": true, "checksum": "e2d70c097b6fb5900045774e710cfbc9dcadde1a", "dest": "/var/ www/mysite/dev/htdocs/sites/default/files/product_images/fa9001.jpg", "gid": 33, "group": "www-data", "item": "fa9001.jpg", "md5sum": "10df9698fd4f743fd199372255b15f33", "mode": "0644", "owner": "www-data", "size": 63598, "src": "/home/georgeb/.ansible/tmp/ ansible-tmp-1420799222.5-262099501965035/source", "state": "file", "uid": 33} $ ansible-playbook main.yml -l server.example.com -u ansible -K -v --tags "updateimages"
  • 137. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] **************************
  • 138. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”}
  • 139. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"}
  • 140. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state": "absent"}
  • 141. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg", "state": "absent"}
  • 142. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg", "state": "absent"} PLAY RECAP ********************************************************************
  • 143. blue-bag RUN UPDATE IMAGES PLAY TASK: [updateimages | Image Update | Update Cache] ************************** ok: [server.example.com] => (item=['thumbnail/public/product_images', 'fa9001.jpg']) => {"changed": false, "item": ["thumbnail/public/product_images", "fa9001.jpg"], "path": "/var/www/mysite/live/ htdocs/sites/default/files/styles/thumbnail/public/product_images/fa9001.jpg", "state": “absent”} changed: [server.example.com] => (item=['product_gallery/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_gallery/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_gallery/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['medium_tall/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["medium_tall/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/medium_tall/public/product_images/fa9001.jpg", "state": "absent"} changed: [server.example.com] => (item=['product_full/public/product_images', 'fa9001.jpg']) => {"changed": true, "item": ["product_full/public/product_images", "fa9001.jpg"], "path": "/var/www/ mysite/live/htdocs/sites/default/files/styles/product_full/public/product_images/fa9001.jpg", "state": "absent"} PLAY RECAP ******************************************************************** server.example.com : ok=2 changed=2 unreachable=0 failed=0
  • 144. blue-bag ANSIBLE TOOLS Automate playbook runs Collaborate Log Permissions Scheduled runs Push button deployment Free for < 10 nodes
  • 145. blue-bag CONCLUSION / QUESTIONS For all local development use VLAD http://git.io/DCB-Vlad For Ansible get the books https://leanpub.com/ ansible-for-devops http://www.ansible.com/ ansible-book
  • 146. blue-bag BLUE-BAG GEORGE BOOBYER
 DRUPAL: iAUGUR
 GEORGE@BLUE-BAG.COM TWITTER: iBLUEBAG www.blue-bag.com Established in 2000
 The year before Drupal 1.0