Skip to content

Instantly share code, notes, and snippets.

@ddimtirov
Last active May 29, 2017 08:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ddimtirov/b1f261070c9ba4de359b455de6e778d6 to your computer and use it in GitHub Desktop.
Save ddimtirov/b1f261070c9ba4de359b455de6e778d6 to your computer and use it in GitHub Desktop.
Ansible Summary

This is a text file, click on the 'Raw' button to view as intended

Ansible

classpath () { jinfo -sysprops $1 | grep java.class.path | awk '{ print $3 }' | sed 's/:/\n/g' ; }

jps -q | xargs -n 1 classpath

A trick to start stuff idempotently - start it and wait until the query returns expected results (possibly number of tagged instances/processes)

Plugins are automatically loaded when you have one of the following subfolders adjacent to your playbook or inside a role:

Control: Callback - enable you to hook into Ansible events for display or logging purposes. Strategy - control the flow of a play and execution logic. Vars - inject additional variable data into Ansible runs that did not come from an inventory, playbook, or the command line.

Comms: Connection - define how to communicate with inventory hosts. Cache - are used to keep a cache of ‘facts’ to avoid costly fact-gathering operations. Action - are front ends to modules and can execute actions on the controller before calling the modules themselves. Shell - deal with low-level commands and formatting for the different shells Ansible can encounter on remote hosts.

Template plugins: Lookup - are used to pull data from an external source. Filters - allow you to manipulate data inside Ansible plays and/or templates. Test - allow you to validate data inside Ansible plays and/or templates.

Callbacks: on_any (*args, **kwargs) playbook_on_import_for_host (host, imported_file) playbook_on_vars_prompt (varname, private=, prompt=, encrypt=, confirm=, salt_size=, salt=, default=) ----------------------------------------------------------------------v2---------------------------------------- playbook_on_notify (host, handler) | (result, handler) playbook_on_play_start (name) | (play) runner_on_no_hosts () | (task) runner_on_failed (host, res, ignore_errors=False) | (result, ignore_errors=False) playbook_on_start () | (playbook) on_file_diff (host, diff) | (result) runner_on_skipped (host, item=None) | (result) runner_on_ok (host, res) | (result) runner_on_unreachable (host, res) | (result) runner_on_async_failed (host, res, jid) | (result) runner_on_async_ok (host, res, jid) | (result) runner_on_async_poll (host, res, jid, clock) | (result) ---------------------------------------------------------------------------------------------------------------- playbook_on_cleanup_task_start_v2 (task) playbook_on_handler_task_start_v2 (task) playbook_on_include_v2 (included_file) runner_on_file_diff_v2 (result, diff) runner_item_on_failed_v2 (result) runner_item_on_ok_v2 (result) runner_item_on_skipped_v2 (result) ---------------------------------------------------------------------------------------------------------------- playbook_on_no_hosts_matched () playbook_on_no_hosts_remaining () playbook_on_not_import_for_host (host, missing_file) playbook_on_setup () playbook_on_stats (stats) playbook_on_task_start (name, is_conditional)

Lookups can be used for looping, also populating variables and params: - looping: playbook language constructs like “with_fileglob” and “with_items” are implemented via lookup plugins - to return values into a variable or parameter.


Dynamic Inventory Sources

http://docs.ansible.com/ansible/intro_dynamic_inventory.html http://docs.ansible.com/ansible/dev_guide/developing_inventory.htmlSNET

Script with argument --list => all the groups to be managed - one of: - a hash/dictionary containing - a list of each host/IP - (optional) child groups - (optional) group variables - a list of host/IP addresses, like so:

{ "webservers" : [ "host2.example.com", "host3.example.com" ], "databases" : { "hosts" : [ "host1.example.com", "host2.example.com" ], "vars" : { "a": true } }, "atlanta" : { "hosts" : [ "host1.example.com", "host4.example.com", "host5.example.com" ], "vars" : { "b": false }, "children": [ "marietta", "5points" ] }, "marietta" : [ "host6.example.com" ], "5points" : [ "host7.example.com" ] }

Script with argument --host => empty hash/dictionary (variables)

The stock inventory script system detailed above works for all versions of Ansible. If the inventory script returns a top level element called “_meta”, it is possible to return all of the host variables in one inventory script call.

When this meta element contains a value for “hostvars”, the inventory script will not be invoked with --host for each host. This results in a significant performance increase for large numbers of hosts, and also makes client side caching easier to implement for the inventory script.

The data to be added to the top level JSON dictionary looks like this:

{

# results of inventory script as above go here
# ...

"_meta" : {
   "hostvars" : {
      "moocow.example.com"     : { "asdf" : 1234 },
      "llama.example.com"      : { "asdf" : 5678 },
   }
}

}


Playbook - a flat list of plays

Each play defines a list of hosts, possibly remote user, vars, etc and perhaps a list of tasks

- hosts: all_snet
  ...

Inspection: --list-hosts --list-tags --list-tasks Use of -v when executing playbooks will show possible values for the results. Control: --tags/--skip-tags, --step (interactive confirmation for each task), --start-at-task="The name of my task"


strategy: debug

'p' to print p result p host p task p task.args p vars p vars["ansible_swapfree_mb"]

'r' - retry 'c' - skip failing task and continue

modify task's module's arguments and retry (mind single/double quotes): task.args['name'] = 'bash' redo


It is important to understand that, within a play, all hosts are going to get the same task directives. It is the purpose of a play to map a selection of hosts to tasks.

Plays/tasks are executed one at a time across the machine pool, which means that all machines are either rexecuting the same task or waiting for it to end.

Hosts with failed tasks are taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun.

All built-in modules are idempotent. All apart from command and shell.

All modules take structired params, apart from command and shell.

The command and shell module care about return codes, so if you have a command whose successful exit code is not zero, you may wish to do this:

    tasks:

      - shell: /usr/bin/somecommand || /bin/true

      - shell: /usr/bin/somecommand
        ignore_errors: True

If the action line is getting too long for comfort you can break it on a space and indent any continuation lines:

    tasks:
      - name: Copy ansible inventory file to client
        copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
                owner=root group=root mode=0644
```

For non-idempotent modules, use 'creates' flag

If non name is specified, the string fed to ‘action’ will be used for output.

Variables can be used in action lines and action params. 

When tasks are running one-by-one they may emit handler notification. Notified handlersfor each play are ran only once after all tasks in a section (pre_tasks, tasks, post_tasks) have been run.

If really needed, handlers can be flushed in the middle fo a section with a meta task "- meta: flush_handlers"

Handlers cana also "listen" to subjects, so we can decouple them from tasks and logically group them as needed.

   tasks:
     - block:
         - yum: name={{ item }} state=installed
           with_items:
             - httpd
             - memcached
         - template: src=templates/src.j2 dest=/etc/foo.conf
         - service: name=bar state=started enabled=True
       when: ansible_distribution == 'CentOS'                   # apply for the whole block
       become: true
       become_user: root
       rescue:                                                  # catch
         - debug: msg='I caught an error'
         - command: /bin/false
         - name: make sure all handlers run
           meta: flush_handlers
       always:                                                  # finally
         - debug: msg="this always executes"


######################################################################################################################################
######################################################################################################################################
######################################################################################################################################

{{ some_variable | default(5) }}
  file: dest={{item.path}} state=touch mode={{item.mode|default(omit)}}
- shell: echo {{ string_value | quote }}
{{ list | join(" ") }}
{{ path | basename }} # To expand a path containing a tilde (~) character (new in version 1.5):
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }} # convert "ansible" to "able"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }} # convert "foobar" to "bar"
{{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }} # convert "localhost:80" to "localhost, 80" using named groups
{{ '^f.*o(.*)$' | regex_escape() }} # convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$'
{{ ansible_mounts|map(attribute='mount')|join(',') }} # get a comma-separated list of the mount points (e.g. "/,/mnt/stuff") on a host
{{ (("2016-08-04 20:00:12"|to_datetime) - ("2015-10-06"|to_datetime('%Y-%d-%m'))).seconds  }}# get amount of seconds between two dates, default date format is %Y-%d-%m %H:%M:%S but you can pass your own one

tasks:
  - name: "shut down CentOS 6 and Debian 7 systems"
    command: /sbin/shutdown -t now
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or           # when doesn't need jinja braces to reference vars
          (ansible_distribution == "Debian" and ansible_distribution_major_version == "7")

tasks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when: # list means AND
      - ansible_distribution == "CentOS"                
      - ansible_distribution_major_version == "6"

tasks:
  - command: /bin/false
    register: result                    #<---- register JSON var
    ignore_errors: True

  - command: /bin/something
    when: result|failed                 # <--- use a filter to extract the status (there are others too: succeeded, skipped, etc.)

tasks: # runs for 6, 8, 10 only
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5

- command: echo {{ item }}                  # skip if mylist is not defined or empty
  with_items: "{{ mylist|default([]) }}"
  when: item > 5

tasks:
    - name: gather site specific fact data
      action: site_facts                        # <--- a custom module that returns a json result containing "ansible_facts": { .... }
    - command: /usr/bin/thingy
      when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'



- include: tasks/sometasks.yml
  when: "'reticulating splines' in output"      # All the tasks get evaluated, but the conditional is applied to each and every task:



  roles: ### Read up on the ‘group_by’ as a better way to do this
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }

### VAR INCLUDES ########################################################################################################################################

  vars_files:
    - /vars/external_vars.yml
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ] # try first then second

- name: template a file
  template: src={{ item }} dest=/etc/myapp/foo.conf
  with_first_found:
    - files:
       - {{ ansible_distribution }}.conf
       - default.conf
      paths:
       - search_location_one/somedir/
       - /opt/other_location/somedir/


### INCLUDES ############################################################################################################################################

  - include: tasks/foo.yml
  - include: wordpress.yml wp_user=timmy
  - include: wordpress.yml
    vars:
        wp_user: timmy
        ssh_keys:
          - keys/one.txt
          - keys/two.txt

Includes can also be used in the ‘handlers’ section, for instance, if you want to define how to restart apache, you only have to do that once for all of your playbooks. You might make a handlers.yml that looks like:
```handlers/handlers.yml
    ---
    - name: restart apache
      service: name=apache state=restarted
```
And in your main playbook file, just include it like so, at the bottom of a play:
``` playbook.yml
    ...
    handlers:
      - include: handlers/handlers.yml
```

Includes can also be used to import one playbook file into another. This inserts the plays at the specified place.
Note that you cannot do variable substitution when including one playbook inside another.
```
- name: this is a play at the top level of a file
  hosts: all
  tasks:
    ...
- include: webservers.yml
- include: dbservers.yml
```


loops on include statements, or use variables from any source with a dynamic include:
```
    - include: "{{inventory_hostname}}.yml"

    - include: foo.yml param={{item}}
      with_items:
      - 1
      - 2
      - 3

When using dynamic includes, it is important to keep these limitations in mind: You cannot use notify to trigger a handler name which comes from a dynamic include. You cannot use --start-at-task to begin execution at a task inside a dynamic include. Tags which only exist inside a dynamic include will not show up in –list-tags output. Tasks which only exist inside a dynamic include will not show up in –list-tasks output.

To work around these limitations, Ansible 2.1 introduces the static: boolean option for includes Includes default to static when the included file name does not use variables; no iteration for variables

ROLES

If roles/x/tasks/main.yml exists, tasks listed therein will be added to the play If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play If roles/x/vars/main.yml exists, variables listed therein will be added to the play If roles/x/defaults/main.yml exists, variables listed therein will be added to the play If roles/x/meta/main.yml exists, any role dependencies listed therein will be added to the list of roles (1.3 and later) Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely

Note, you are still allowed to list tasks, vars_files, and handlers “loose” in playbooks without using roles, but roles are a good organizational feature and are highly recommended. If there are loose things in the playbook, the roles are evaluated first. If the play still has a ‘tasks’ section, those tasks are executed after roles are applied.

Also, should you wish to parameterize roles, by adding variables, you can do so, like this:

    - hosts: webservers
      roles:
        - common
        - { role: foo_app_instance, dir: '/opt/a',  app_port: 5000 }
        - { role: foo_app_instance, dir: '/opt/b',  app_port: 5001 }

While it’s probably not something you should do often, you can also conditionally apply roles like so:

    - hosts: webservers
      roles:
        - { role: some_role, when: "ansible_os_family == 'RedHat'" }

-- If you want to define certain tasks to happen before AND after roles are applied, you can do this:

    - hosts: webservers

      pre_tasks:
        - shell: echo 'hello'

      roles:
        - { role: some_role }

      tasks:
        - shell: echo 'still busy'

      post_tasks:
        - shell: echo 'goodbye'

-- Role default variables have the lowest priority and can be easily overridden by any other variable, including inventory variables.

Role dependencies are stored in the meta/main.yml file contained within the role directory. This file should contain a list of roles and parameters to insert BEFORE the specified role By default, roles can also only be executed once -this behavior can be overridden by adding allow_duplicates: yes to the meta/main.yml file

For example, a role named ‘car’ could add a role named ‘wheel’ to its dependencies as follows: dependencies: - { role: wheel, n: 1 } - { role: wheel, n: 2 } - { role: wheel, n: 3 } - { role: wheel, n: 4 }

And the meta/main.yml for wheel contained the following: allow_duplicates: yes dependencies: - { role: tire } - { role: brake } The resulting order of execution would be as follows:

tire(n=1) brake(n=1) wheel(n=1) tire(n=2) brake(n=2) wheel(n=2) ... car

Modules go to ./library, plugins to ./filter_plugins, etc.

####################################################

Finally, you may wish to assign tags to the roles you specify. You can do so inline:

- hosts: webservers
  roles:
    - { role: foo, tags: ["bar", "baz"] }

Note that this tags all of the tasks in that role with the tags specified, overriding any tags that are specified inside the role. If you find yourself building a role with lots of tags and you want to call subsets of the role at different times, you should consider just splitting that role into multiple roles.

If using tags with tasks (described later as a means of only running part of a playbook), be sure to also tag your pre_tasks and post_tasks and pass those along as well, especially if the pre and post tasks are used for monitoring outage window control or load balancing.


If a remotely managed system has an /etc/ansible/facts.d directory, any files in this directory ending in .fact, can be JSON, INI, or executable files returning JSON, and these can supply local facts in Ansible.

If you have a playbook that is copying over a custom fact and then running it, making an explicit call to re-run the setup module can allow that fact to be used during that particular play. Otherwise, it will be available in the next play that gathers fact information. Here is an example of what that might look like:

In this pattern however, you could also write a fact module as well, and may wish to consider this as an option.

  • hosts: webservers tasks:
    • name: create directory for ansible custom facts file: state=directory recurse=yes path=/etc/ansible/facts.d
    • name: install custom impi fact copy: src=ipmi.fact dest=/etc/ansible/facts.d
    • name: re-read facts after adding custom fact setup: filter=ansible_local

Registered variables are valid on the host the remainder of the playbook run, which is the same as the lifetime of “facts” in Ansible. Effectively registered variables are just like facts.

-- {{ hostvars['some.other.host.com']['ansible_distribution'] }}

{% if 'webserver' in group_names %}

some part of a configuration file that only applies to webservers

{% endif %}

{% for host in groups['app_servers'] %} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %}

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo" ansible-playbook release.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}' --extra-vars "@some_file.json"

role defaults // Tasks in each role will see their own role’s defaults. Tasks defined outside of a role will see the last role’s defaults. inventory vars // Variables defined in inventory file or provided by dynamic inventory. inventory group_vars inventory host_vars playbook group_vars playbook host_vars host facts play vars play vars_prompt play vars_files registered vars set_facts role and include vars block vars (only for tasks in block) task vars (only for the task) extra vars (always win precedence)

Ansible has 3 main scopes: Global: config environment variables command line Play: each play and contained structures vars entries include_vars role defaults vars Host: variables directly associated to a host inventory facts registered task outputs

Inventory variables: child group override parent groups hosts always override all their groups

Role variables: variables set in one role are available to others, unless explicitly overridden in the role inclusion /defaults - just reasonable defaults - overridden by anything /vars - prevails over inventory vars // if you are sharing roles with others, putting variables in here might be bad. Nobody will be able to override them with inventory, but they still can by passing a parameter to the role.

====================================

Within templates can use full Jinja2 - i.e. conditionals and looping: {% for host in groups['db_servers'] %} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %}


How do I read a variable name programmatically?

{{ hostvars[inventory_hostname]['ansible_' + which_interface]['ipv4']['address'] }}


How do I access shell environment variables?

 local_home: "{{ lookup('env','HOME') }}"

Conditionals are always run through Jinja2 as to resolve the expression, so when:, failed_when: and changed_when: are always templated and you should avoid adding {{}}.

==================================== Grammar:

Play : accelerate, accelerate_ipv6, accelerate_port, always_run, any_errors_fatal, 
       become, become_flags, become_method, become_user, check_mode, connection, 
       environment, force_handlers, gather_facts, gather_subset, gather_timeout, 
       handlers, hosts, ignore_errors, max_fail_percentage, name, no_log, port, 
       post_tasks, pre_tasks, remote_user, roles, run_once, serial, strategy, 
       tags, tasks, vars, vars_files, vars_prompt, vault_password

Role : always_run, become, become_flags, become_method, become_user, check_mode, 
       connection, delegate_facts, delegate_to, environment, ignore_errors, no_log,
       port, remote_user, run_once, tags, vars, when

Block: always, always_run, any_errors_fatal, become, become_flags, become_method, 
       become_user, block, check_mode, connection, delegate_facts, delegate_to, 
       environment, ignore_errors, no_log, port, remote_user, rescue, run_once, 
       tags, vars, when

Task : action, always_run, any_errors_fatal, args, async, become, become_flags, 
       become_method, become_user, changed_when, check_mode, connection, delay, 
       delegate_facts, delegate_to, environment, failed_when, ignore_errors, 
       local_action, loop, loop_args, loop_control, name, no_log, notify, poll, 
       port, register, remote_user, retries, run_once, tags, until, vars, when,
       with_<lookup_plugin>

======================================

Both plays and tasks support a “tags:” attribute for this reason. You can apply tags in various scopes, but they are ultimately only affecting the tasks. Applying tags anywhere else is just a convenience so you don’t have to write it on every task.

You may also apply tags to roles:

roles:

  • { role: webserver, port: 5000, tags: [ 'web', 'foo' ] } And include statements:

  • include: foo.yml tags: [web,foo]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment