Ansible Playbook: Core Elements and Examples

1. Core Elements of a Playbook:

hosts List of remote hosts to execute on

tasks Set of tasks

variables Built-in or custom variables called in the playbook

templates Files that can replace variables in template files and implement some simple logic

handlers Used in conjunction with notify, triggered by specific conditions, executed if conditions are met, otherwise not executed

tags Tags that specify which task to execute, used to select parts of the playbook to run; Ansible will automatically skip unchanged parts. Example: ansible-playbook -t tagname name.yml

remote_user: Can be used in hosts and tasks, and can specify to execute tasks on the remote host via sudo; applicable globally for the play or for a specific task.

Additionally, you can even use sudo_user to specify the user to switch to when using sudo.

Example:

- hosts: websrvs
    remote_user: root      # Optional, defaults to root. Connects as root.
    tasks:

– name: test connection

               ping:

remote_user: test

               sudo: yes
               sudo_user: dev

tasks: Tasks

Example:

tasks:
    - name: taskname          # Name the task
       command: /bin/bash /root/test.sh     # Module name: execute the corresponding parameters of this module

ansible-playbook –check name.yml Check for possible changes without actually executing; –check is the same as -C

ansible-playbook –list-hosts name.yml List the hosts that will run the tasks

ansible-playbook –list-tags name.yml List the tags

ansible-playbook –list-tasks name.yml List the tasks

ansible-playbook –limit host list name.yml Execute only on hosts in the host list

-v -vv -vvv Show execution process; the more v, the more detailed

Playbook to Install httpd

- hosts: all
   remote_user: root      
   tasks:     
        - name: "Install Apache"       
            yum: name=httpd       # yum module: install httpd     
        - name: "Copy configuration file"       
            copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/   # copy module: copy file     
         - name: "Copy configuration file"       
            copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/       
         - name: "Start Apache and set to start on boot"       
            service: name=httpd state=started enabled=yes    # service module: start service 

Playbook to Create User

- hosts: all   
   remote_user: root   
   tasks:     
        - name: create mysql user       
           user: name=mysql system=yes uid=36     
        - name: create a group       
           group: name=httpd system=yes

Example Playbook to Install httpd Service

- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: Install httpd       
          yum: name=httpd state=present     
        - name: Install configuration file       
           copy: src=files/httpd.conf dest=/etc/httpd/conf/     
        - name: start service       
           service: name=httpd state=started enabled=yes

Example Playbook to Install nginx Service

- hosts: all   
   remote_user: root   
   tasks:     
       - name: add group nginx       
          group: name=nginx state=present     
       - name: add user nginx       
          user: name=nginx state=present group=nginx     
       - name: Install Nginx       
          yum: name=nginx state=present     
        - name: Start Nginx       
           service: name=nginx state=started enabled=yes

Using Handlers in Playbooks

- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: Install httpd       
          yum: name=httpd state=present     
       - name: Install configuration file       
          copy: src=files/httpd.conf dest=/etc/httpd/conf/       
          notify: restart httpd     
       - name: ensure apache is running       
          service: name=httpd state=started enabled=yes      
   handlers:     
        - name: restart httpd       
           service: name=httpd state=restarted
- hosts: webnodes   
   vars:     
       http_port: 80     
       max_clients: 256   
    remote_user: root      
    tasks:     
        - name: ensure apache is at the latest version       
           yum: name=httpd state=latest     
        - name: ensure apache is running       
           service: name=httpd state=started     
        - name: Install configuration file       
           copy: src=files/httpd.conf dest=/etc/httpd/conf/       
           notify: restart httpd                   # First way to use notify
    handlers:       
        - name: restart httpd          
           service: name=httpd state=restarted
- hosts: websrvs   
   remote_user: root      
   tasks:     
       - name: add group nginx       
          tags: user       
          group: name=nginx state=present     
       - name: add user nginx       
          user: name=nginx state=present group=nginx     
       - name: Install Nginx       
          yum: name=nginx state=present     
       - name: config       
          copy: src=/root/config.txt dest=/etc/nginx/nginx.conf       
           notify:            # Second way to use notify
                - Restart Nginx         
                - Check Nginx Process      
   handlers:     
        - name: Restart Nginx       
           service: name=nginx state=restarted enabled=yes     
        - name: Check Nginx process       
           shell: killall -0 nginx > /tmp/nginx.log

Using Tags in Playbooks

Tags: Add tags

You can specify a tag for a specific task, and after adding a tag, you can execute a certain action using the tag.

Multiple actions can use the same tag.

vim httpd.yml
- hosts: websrvs   
   remote_user: root      
   tasks:     
       - name: Install httpd       
          yum: name=httpd state=present       
          tags: install      
        - name: Install configuration file       
           copy: src=files/httpd.conf dest=/etc/httpd/conf/       
           tags: conf     
        - name: start httpd service       
           tags: service       
           service: name=httpd state=started enabled=yes

ansible-playbook -t install,conf httpd.yml Specify to execute the install and conf tags

- hosts: hbhosts   
   remote_user: root      
   tasks:     
      - name: ensure heartbeat latest version       
         yum: name=heartbeat state=present     
      - name: authkeys configure file       
         copy: src=/root/hb_conf/authkeys dest=/etc/ha.d/authkeys     
      - name: authkeys mode 600       
         file: path=/etc/ha.d/authkeys mode=600       
         notify:         
              - restart heartbeat     
      - name: ha.cf configure file       
         copy: src=/root/hb_conf/ha.cf dest=/etc/ha.d/ha.cf       
         notify:         
              - restart heartbeat   
   handlers:     
      - name: restart heartbeat       
         service: name=heartbeat state=restarted

Using Tags in Playbooks

- hosts: testsrv   
   remote_user: root   
   tags: inshttpd      # Add tags for the entire playbook   
   tasks:     
       - name: Install httpd       
          yum: name=httpd state=present     
       - name: Install configuration file       
          copy: src=files/httpd.conf dest=/etc/httpd/conf/       
           tags: rshttpd       
           notify: restart httpd   
   handlers:     
       - name: restart httpd       
          service: name=httpd status=restarted       
ansible-playbook –t rshttpd httpd2.yml

Using Variables in Playbooks

Variable Sources:

1) The setup module can display a lot of system information in the system, returning system information for each host including: version, hostname, CPU, memory

ansible all -m setup -a 'filter="ansible_nodename"'      # Query hostname
ansible all -m setup -a 'filter="ansible_memtotal_mb"'    # Query memory size
ansible all -m setup -a 'filter="ansible_distribution_major_version"'    # Query system version
ansible all -m setup -a 'filter="ansible_processor_vcpus"'     # Query number of CPUs

2) Define variables in /etc/ansible/hosts (inventory)

Ordinary variables: Defined individually for hosts in the host group, with higher priority than public variables (single host)

Public (group) variables: Define unified variables for all hosts in the host group (same category of a group of hosts)

3) Specify variables via command line, with the highest priority

ansible-playbook -e varname=value

4) Define in the playbook

vars:         
    - var1:  value1         
    - var2:  value2

5) Define in a separate variable YAML file (vars.yml)

6) Define in roles

Variable Naming: Variable names can only consist of letters, numbers, and underscores, and must start with a letter.

Variable Definition: key=value

ep: http_port=80

Variable Calling Method:

1) Call variables using {{ var_name }}, ensuring there are spaces around the variable name; sometimes using "{{ var_name }}" is necessary for it to take effect.

2) Specify using the -e option

ansible-playbook test.yml -e "hosts=dev user=root"

Define variables in the inventory and use them in Ansible:

vim /etc/ansible/hosts
[appsrvs]
192.168.1.2 http_port=80 name=www
192.168.1.3 http_port=81 name=web
[appsrvs:vars]             # Variables defined for the host group
ansible_ssh_user=root
ansible_ssh_pass=123456
mark="-"

Calling Variables:

ansible appsrvs -m hostname -a "name={{ name }}"           # Change hostname to the defined name variable
ansible appsrvs -m hostname -a "name={{ name }}{{ mark }}{{ http_port }}"    # Concatenate variables

Write variables into a separate configuration file for reference:

vim vars.yml
pack: vsftpd
service: vsftpd

Reference Variable File:

vars_files:
    - vars.yml

Basic Elements of Ansible

Facts: Information returned by the remote target host that is communicating, stored in Ansible variables.

To obtain all facts supported by a specified remote host, use the following command:

ansible websrvs -m setup 

When running a playbook, you can also pass some variables for the playbook to use:

ansible-playbook test.yml -e "hosts=www user=magedu"

register: Define the output of a task as a variable for use in other tasks

Example:

tasks: 
    - name: shell
       shell: /usr/bin/foo   
       register: foo_result   
       ignore_errors: True

Example: Using Setup Variables

- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: create log file       
          file: name=/var/log/{{ ansible_fqdn }} state=touch

The ansible_fqdn variable can be viewed using:

ansible webserver -m setup | grep ansible_fqdn

Example: var.yml

- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: install package       
          yum: name={{ pkname }} state=present        

ansible-playbook –e “pkname=httpd” var.yml Variables passed via -e are separated by spaces.

Example: var.yml 
- hosts: websrvs   
   remote_user: root 
   vars:   
       - username: user1   
       - groupname: group1 
    tasks:   
        - name: create group     
           group: name={{ groupname }} state=present   
        - name: create user     
           user: name={{ username }} state=present 
ansible-playbook var.yml  # The created user and group are defined as user1 and group1 in the yml file
ansible-playbook -e "username=user2 groupname=group2” var2.yml  # Variables passed via command line have the highest priority, so the created user and group are user2 and group2

Using Variable Files

cat vars.yml 
var1: httpd 
var2: nginx
vim log_create.yml
- hosts: web   
   remote_user: root   
   vars_files:     
       - vars.yml   
   tasks:     
       - name: create httpd log       
          file: name=/app/{{ var1 }}.log state=touch     
       - name: create nginx log       
          file: name=/app/{{ var2 }}.log state=touch

Group Nesting:

Groups can also contain other groups, and variables can be specified for hosts within the group.

These variables can only be used in ansible-playbook, while the ansible command does not support them.

Example:     
[apache]     
httpd1.magedu.com     
httpd2.magedu.com          
[nginx]     
ngx1.magedu.com     
ngx2.magedu.com         
[websrvs:children]     
apache     
nginx         
[webservers:vars]     
ntp_server=ntp.magedu.com

Inventory Parameters

Inventory parameters: Used to define parameters for Ansible’s remote connection to target hosts, not variables passed to the playbook.

ansible_ssh_host     
ansible_ssh_port     
ansible_ssh_user     
ansible_ssh_pass     
ansbile_sudo_pass

Example:

cat /etc/ansible/hosts     
[websrvs]     
192.168.0.1 ansible_ssh_user=root ansible_ssh_pass=magedu     
192.168.0.2 ansible_ssh_user=root ansible_ssh_pass=magedu
host1 ansible_ssh_host=192.168.1.2 ansible_ssh_user=root ansible_ssh_pass=magedu  # host1 is an alias
host2 ansible_ssh_host=192.168.1.[3:10] ansible_ssh_user=root ansible_ssh_pass=magedu  # Can also represent multiple hosts

Template Example

Example: Use template to synchronize nginx configuration file

Prepare templates/nginx.conf.j2 file

vim temnginx.yml 
- hosts: websrvs   
   remote_user: root      
   tasks:     
       - name: template config to remote hosts       
          template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf 

Execute ansible-playbook temnginx.yml

Template Changes in Playbooks

Modify the file nginx.conf.j2 with the following line:

worker_processes {{ ansible_processor_vcpus }};

Algorithm Operations: Example:

vim nginx.conf.j2     
worker_processes {{ ansible_processor_vcpus**2 }};     
worker_processes {{ ansible_processor_vcpus+2 }};

Implementing Conditional Judgments with When

Example:

tasks:   
    - name: "shutdown RedHat flavored systems"     
       command: /sbin/shutdown -h now     
       when: ansible_os_family == "RedHat"      # Execute command module when the system belongs to the RedHat series

In the when statement, you can also use most of Jinja2’s “filters”. For example, to ignore errors from a previous statement and run subsequent specified statements based on its result (failed or success), you can use a form like this:

tasks:   
    - command: /bin/false     
       register: result     
       ignore_errors: True   
    - command: /bin/something     
       when: result|failed   
    - command: /bin/something_else     
       when: result|success   
    - command: /bin/still/something_else     
       when: result|skipped 

Additionally, the when statement can use facts or variables defined in the playbook.

Example: When Conditional Judgment

- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: add group nginx       
          tags: user       
          user: name=nginx state=present     
       - name: add user nginx       
          user: name=nginx state=present group=nginx     
       - name: Install Nginx       
          yum: name=nginx state=present     
        - name: restart Nginx       
           service: name=nginx state=restarted       
           when: ansible_distribution_major_version == "6"

Example:

tasks:   
    - name: install conf file to centos7     
       template: src=nginx.conf.c7.j2 dest=/etc/nginx/nginx.conf     
       when: ansible_distribution_major_version == "7"   
    - name: install conf file to centos6     
       template: src=nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf     
       when: ansible_distribution_major_version == "6"

When Conditional Judgment in Playbooks

--- 
- hosts: srv120   
   remote_user: root   
   tasks:     
       - name:       
          template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf       
          when: ansible_distribution_major_version == "7"

Iteration: with_items

Iteration: Similar to loops

Iteration: When there are tasks that need to be executed repeatedly, you can use the iteration mechanism.

> Reference to iteration items, fixed variable name is “item”

> To use in the task, specify the elements list to iterate with with_items

> List format:

Strings

Dictionaries

Example: Create users testuser1 and testuser2

- name: add several users   
   user: name={{ item }} state=present group=wheel   # {{ item }} is a user-defined variable   
   with_items:       # Define values and counts for {{ item }}     
       - testuser1     
       - testuser2

Items in with_items can also be hashes.

Example:

- name: add several users   
   user: name={{ item.name }} state=present group={{ item.groups }}   
   with_items:     
        - { name: 'testuser1', groups: 'wheel' }     
        - { name: 'testuser2', groups: 'root' } 

Ansible’s iteration mechanism has many more advanced features; please refer to the official documentation.

http://docs.ansible.com/playbooks_loops.html

Example: Copy multiple files to the controlled end.

--- 
- hosts: testsrv   
   remote_user: root   
   tasks:   
       - name: Create rsyncd config     
          copy: src={{ item }} dest=/etc/{{ item }}     
          with_items:   
               - rsyncd.secrets   
               - rsyncd.conf
- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: copy file       
          copy: src={{ item }} dest=/tmp/{{ item }}       
          with_items:     
              - file1     
              - file2     
              - file3 
       - name: yum install httpd   
          yum: name={{ item }} state=present   
          with_items:     
              - apr     
              - apr-util     
              - httpd 
- hosts: websrvs   
   remote_user: root   
   tasks:     
       - name: install some packages       
          yum: name={{ item }} state=present       
          with_items:         
              - nginx         
              - memcached         
              - php-fpm

with_items nested sub-variables.

Example:

--- 
- hosts: testweb   
   remote_user: root   
   tasks:     
      - name: add several users       
         user: name={{ item.name }} state=present group={{ item.groups }}       
         with_items:     
              - { name: 'testuser1' , groups: 'wheel'}     
              - { name: 'testuser2' , groups: 'root'}

Playbook Dictionary with_items

- name:     # Use ufw module to manage which ports need to be opened   
   ufw:   
   rule: "{{ item.rule }}"   
   port: "{{ item.port }}"   
   proto: "{{ item.proto }}"   
   with_items:     
       - { rule: 'allow', port: 22, proto: 'tcp' }     
       - { rule: 'allow', port: 80, proto: 'tcp' }     
       - { rule: 'allow', port: 123, proto: 'udp' }
- name:       # Configure default rules for network ingress and egress   
   ufw:   
   direction: "{{ item.direction }}"   
    policy: "{{ item.policy }}"   
    state: enabled   
    with_items:     
        - { direction: outgoing, policy: allow }     
        - { direction: incoming, policy: deny }

Template for If When Loop in Playbooks

{% for vhost in nginx_vhosts %} 
server {    # Repeat server code 
listen {{ vhost.listen | default('80 default_server') }}; 
{% if vhost.server_name is defined %} 
server_name {{ vhost.server_name }}; 
{% endif %} 
{% if vhost.root is defined %} 
root {{ vhost.root }}; 
{% endif %} 
{% endfor %}

Example

// temnginx.yml 
--- 
- hosts: testweb   
   remote_user: root   
   vars:      # Call variables     
       nginx_vhosts:       
           - listen: 8080  # List key-value pairs 
//templates/nginx.conf.j2 
{% for vhost in nginx_vhosts %}   
server {   
    listen {{ vhost.listen }}
 }
 {% endfor %}
Generated Result:
server { 
    listen 8080 
}

Example

// temnginx.yml 
--- 
- hosts: mageduweb   
   remote_user: root   
   vars:     
   nginx_vhosts:       
       - web1       
       - web2       
       - web3   
   tasks:     
       - name: template config       
          template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
// templates/nginx.conf.j2 
{% for vhost in nginx_vhosts %} 
server {
     listen {{ vhost }}
 }
 {% endfor %}
Generated Result:
server {
     listen web1 
}
 server {
     listen web2 
}
server {
     listen web3 
}

Leave a Comment