zot24
6/23/2012 - 6:53 PM

Insanely complete Ansible playbook, showing off all the options

Insanely complete Ansible playbook, showing off all the options

---
# ^^^ YAML documents must begin with the document separator "---"
#
#### Example docblock, I like to put a descriptive comment at the top of my 
#### playbooks.
#
# Overview: Playbook to bootstrap a new host for configuration management.
# Applies to: production
# Description: 
#   Ensures that a host is configured for management with Ansible.
# 
###########
#
#
# Note:
# YAML, like Python, cares about whitespace.  Indent consistently throughout.
# Be aware! Unlike Python, YAML refuses to allow the tab character for
# indentation, so always use spaces.
#
# Two-space indents feel comfortable to me, but do whatever you like.
# vim:ff=unix ts=2 sw=2 ai expandtab
#
# If you're new to YAML, keep in mind that YAML documents, like XML
# documents, represent a tree-like structure of nodes and text. More
# familiar with JSON?  Think of YAML as a strict and more flexible JSON
# with fewer significant characters (e.g., :, "", {}, [])
#
# The curious may read more about YAML at:
# http://www.yaml.org/spec/1.2/spec.html
#


### 
# Notice the minus on the line below -- this starts the playbook's record
# in the YAML document. Only one playbook is allowed per YAML file.  Indent
# the body of the playbook.
-

  hosts: all
  ###########
  # Playbook attribute: hosts
  # Required: yes
  # Description:
  #   The name of a host or group of hosts that this playbook should apply to.
  #
  ## Example values:
  #   hosts: all -- applies to all hosts
  #   hosts: hostname -- apply ONLY to the host 'hostname'
  #   hosts: groupname -- apply to all hosts in groupname
  #   hosts: group1,group2 -- apply to hosts in group1 & group2
  #   hosts: group1,host1 -- mix and match hosts
  #   hosts: *.mars.nasa.gov wildcard matches work as expected
  #
  ## Using a variable value for 'hosts'
  #
  # You can, in fact, set hosts to a variable, for example:
  #
  #   hosts: $groups -- apply to all hosts specified in the variable $groups
  #
  # This is handy for testing playbooks, running the same playbook against a
  # staging environment before running it against production, occasional
  # maintenance tasks, and other cases where you want to run the playbook
  # against just a few systems rather than a whole group.
  #
  # If you set hosts as shown above, then you can specify which hosts to
  # apply the playbook to on each run as so:
  #
  #   ansible-playbook playbook.yml --extra-vars="groups=staging"
  #
  # Use --extra-vars to set $groups to any combination of groups, hostnames,
  # or wildcards just like the examples in the previous section.
  #

  sudo: True
  ###########
  # Playbook attribute: sudo
  # Default: False
  # Required: no
  # Description:
  #   If True, always use sudo to run this playbook, just like passing the
  #   --sudo (or -s) flag to ansible or ansible-playbook.

  user: remoteuser
  ###########
  # Playbook attribute:  user
  # Default: "root'
  # Required: no
  # Description
  #   Remote user to execute the playbook as

  ###########
  # Playbook attribute: vars
  # Default: none
  # Required: no
  # Description:
  #  Set configuration variables passed to templates & included playbooks
  #  and handlers.  See below for examples.
  vars:
    color: brown

    web:
      memcache: 192.168.1.2
      httpd: apache
    # Tree-like structures work as expected, but be careful to surround
    #  the variable name with ${} when using.
    #
    # For this example, ${web.memcache} and ${web.apache} are both usable
    #  variables.

    ########
    # The following works in Ansible 0.5 and later, and will set $config_path
    # "/etc/ntpd.conf" as expected.
    #
    # In older versions, $config_path will be set to the string "/etc/$config"
    #
    config: ntpd.conf
    config_path: /etc/$config

    ########
    # Variables can be set conditionally. This is actually a tiny snippet
    # of Python that will get filled in and evaluated during playbook execution.
    # This expressioun should always evaluate to True or False.
    #
    # In this playbook, this will always evaluate to False, because 'color'
    #  is set to 'brown' above.
    #
    # When ansible interprets the following, it will first expand $color to
    # 'brown' and then evaluate 'brown' == 'blue' as a Python expression.
    is_color_blue: "'$color' == 'blue'"

    #####
    # Builtin Variables
    #
    # Everything that the 'setup' module provides can be used in the
    # vars section.  Ansible native, Facter, and Ohai facts can all be
    # used.
    #
    # Run the setup module to see what else you can use:
    # ansible -m setup -i /path/to/hosts.ini host1
    main_vhost: ${ansible_fqdn}
    public_ip:  ${ansible_eth0.ipv4.address}
    
    # vars_files is better suited for distro-specific settings, however...
    is_ubuntu: "'${ansible_distribution}' == 'ubuntu'"


  ##########
  # Playbook attribute: vars_files
  # Required: no
  # Description:
  #   Specifies a list of YAML files to load variables from.
  #
  #   Always evaluated after the 'vars' section, no matter which section
  #   occurs first in the playbook.  Examples are below.
  #
  #   Example YAML for a file to be included by vars_files:
  #   ---
  #   monitored_by: phobos.mars.nasa.gov
  #   fish_sticks: "good with custard"
  #   # (END OF DOCUMENT)
  #
  #   A 'vars' YAML file represents a list of variables. Don't use playbook
  #   YAML for a 'vars' file.
  #
  #   Remove the indentation & comments of course, the '---' should be at
  #   the left margin in the variables file.
  #
  vars_files:
    # Include a file from this absolute path
    - /srv/ansible/vars/vars_file.yml

    # Include a file from a path relative to this playbook
    - vars/vars_file.yml

    # By the way, variables set in 'vars' are available here.
    - vars/$hostname.yml

    # It's also possible to pass an array of files, in which case
    # Ansible will loop over the array and include the first file that
    # exists.  If none exist, ansible-playbook will halt with an error.
    #
    # An excellent way to handle platform-specific differences.
    - [ vars/$platform.yml, vars/default.yml ]

    # Files in vars_files process in order, so later files can
    # provide more specific configuration:
    - [ vars/$host.yml ]

    # Hey, but if you're doing host-specific variable files, you might
    # consider setting the variable for a group in your hosts.ini and
    # adding your host to that group. Just a thought.


  ##########
  # Playbook attribute: vars_prompt
  # Required: no
  # Description:
  #   A list of variables that must be manually input each time this playbook
  #   runs.  Used for sensitive data and also things like release numbers that
  #   vary on each deployment.  Ansible always prompts for this value, even
  #   if it's passed in through the inventory or --extra-vars.
  #
  #   The input won't be echoed back to the terminal.  Ansible will always
  #   prompt for the variables in vars_prompt, even if they're passed in via
  #   --extra-vars or group variables.
  #
  #   TODO: I think that the value is supposed to show as a prompt but this
  #   doesn't work in the latest devel
  #
  vars_prompt:
    passphrase: "Please enter the passphrase for the SSL certificate"

    # Not sensitive, but something that should vary on each playbook run.
    release_version: "Please enter a release tag"

  ##########
  # Playbook attribute: tasks
  # Required: yes
  # Description:
  # A list of tasks to perform in this playbook.
  tasks:
    ##########
    # The simplest task
    # Each task must have a name & action.
    - name: Check that the server's alive
      action: ping

    ##########
    # Ansible modules do the work!
    - name: Enforce permissions on /tmp/secret
      action: file path=/tmp/secret mode=0600 owner=root group=root
    #
    # Format 'action' like above:
    # <modulename> <module parameters>
    #
    # Test your parameters using:
    #   ansible -m <module> -a "<module parameters>"
    #
    # Documentation for the stock modules:
    # http://ansible.github.com/modules.html

    ##########
    # Use variables in the task!
    #
    # Variables expand in both name and action
    - name: Paint the server $color
      action: command echo $color


    ##########
    # Trigger handlers when things change!
    #
    # Ansible detects when an action changes something.  For example, the
    # file permissions change, a file's content changed, a package was
    # just installed (or removed), a user was created (or removed).  When 
    # a change is detected, Ansible can optionally notify one or more
    # Handlers.  Handlers can take any action that a Task can. Most
    # commonly they are used to restart a service when its configuration
    # changes. See "Handlers" below for more about handlers.
    #
    # Handlers are called by their name, which is very human friendly.

    # This will call the "Restart Apache" handler whenever 'copy' alters
    # the remote httpd.conf.
    - name: Update the Apache config
      action: copy src=httpd.conf dest=/etc/httpd/httpd.conf
      notify: Restart Apache

    # Here's how to specify more than one handler
    - name: Update our app's configuration
      action: copy src=myapp.conf dest=/etc/myapp/production.conf
      notify:
        - Restart Apache
        - Restart Redis

    ##########
    # Include tasks from another file!
    #
    # Ansible can include a list of tasks from another file. The included file
    # must represent a list of tasks, which is different than a playbook.
    #
    # Task list format:
    #   ---
    #   - name: create user
    #     action: user name=$user color=$color
    #
    #   - name: add user to group
    #     action: user name=$user groups=$group append=true
    #   # (END OF DOCUMENT)
    #
    #   A 'tasks' YAML file represents a list of tasks. Don't use playbook
    #   YAML for a 'tasks' file.
    #
    #   Remove the indentation & comments of course, the '---' should be at
    #   the left margin in the variables file.

    # In this example $user will be 'sklar'
    #  and $color will be 'red' inside new_user.yml
    - include: tasks/new_user.yml user=sklar color=red

    # In this example $user will be 'mosh'
    #  and $color will be 'mauve' inside new_user.yml
    - include: tasks/new_user.yml user=mosh color=mauve

    # Variables expand before the include is evaluated:
    - include: tasks/new_user.yml user=chris color=$color


    ##########
    # Run a task on each thing in a list!
    #
    # Ansible provides a simple loop facility. If 'with_items' is provided for
    # a task, then the task will be run once for each item in the 'with_items'
    # list.  $item changes each time through the loop.
    - name: Create a file named $item in /tmp
      action: command touch /tmp/$item
      with_items:
        - tangerine
        - lemon

    ##########
    # Choose between files or templates!
    #
    # Sometimes you want to choose between local files depending on the
    # value of the variable.  first_available_file checks for each file
    # and, if the file exists calls the action with $item={filename}.
    #
    # Mostly useful for 'template' and 'copy' actions.  Only examines local
    # files.
    #
    - name: Template a file
      action: template src=$item dest=/etc/myapp/foo.conf
      first_available_file:
        # ansible_distribution will be "ubuntu", "debian", "rhel5", etc.
        - templates/myapp/${ansible_distribution}.conf

        # If we couldn't find a distribution-specific file, use default.conf:
        - templates/myapp/default.conf

    ##########
    # Conditionally execute tasks!
    #
    # Sometimes you only want to run an action when a under certain conditions.
    # Ansible will 'only_if' as a Python expression and will only run the
    # action when the expression evaluates to True.
    #
    # If you're trying to run an task only when a value changes,
    # consider rewriting the task as a handler and using 'notify' (see below).
    #
    - name: "shutdown all ubuntu"
      action: command /sbin/shutdown -t now
      only_if: "$is_ubuntu"

    - name: "shutdown the government"
      action: command /sbin/shutdown -t now
      only_if: "'$ansible_hostname' == 'the_government'"

    ##########
    # Notify handlers when things change!
    # 
    # Each task can optionally have one or more handlers that get called
    # when the task changes something -- creates a user, updates a file,
    # etc.
    #
    # Handlers have human-readable names and are defined in the 'handlers'
    #  section of a playbook.  See below for the definitions of 'Restart nginx'
    #  and 'Restart application'
    - name: update nginx config
      action: file src=nginx.conf dest=/etc/nginx/nginx.conf
      notify: Restart nginx

    - name: roll out new code
      action: git repo=git://codeserver/myapp.git dest=/srv/myapp version=HEAD branch=release
      notify:
        - Restart nginx
        - Restart application


    ##########
    # Run things as other users!
    #
    # Each task has an optional 'user' and 'sudo' flag to indicate which
    # user a task should run as and whether or not to use 'sudo' to switch
    # to that user.
    - name: dump all postgres databases
      action: pg_dumpall -w -f /tmp/backup.psql
      user: postgres
      sudo: False

    ##########
    # Run things locally!
    #
    # Each task also has a 'connection' setting to control whether a local
    # or remote connection is used.  The only valid options now are 'local'
    # or 'paramiko'.  'paramiko' is assumed by the command line tools.
    #
    # This can also be set at the top level of the playbook.
    - name: create tempfile
      action: dd if=/dev/urandom of=/tmp/random.txt count=100
      connection: local

  ##########
  # Playbook attribute: handlers
  # Required: no
  # Description:
  #   Handlers are tasks that run when another task has changed something.
  #   See above for examples.  The format is exactly the same as for tasks.
  #   Note that if multiple tasks notify the same handler in a playbook run
  #   that handler will only run once.
  #
  #   Handlers are referred to by name. They will be run in the order declared
  #   in the playbook.  For example: if a task were to notify the
  #   handlers in reverse order like so:
  #
  #   - task: touch a file
  #     action: file name=/tmp/lock.txt
  #     notify:
  #     - Restart application
  #     - Restart nginx
  #
  #   The "Restart nginx" handler will still run before the "Restart
  #   application" handler because it is declared first in this playbook.
  handlers:
    - name: Restart nginx
      action: service name=nginx state=restarted

    # Any module can be used for the handler action
    - name: Restart application
      action: command /srv/myapp/restart.sh

    # It's also possible to include handlers from another file.  Structure is
    # the same as a tasks file, see the tasks section above for an example.
    - include: handlers/site.yml