Skip to content

Ansible

Run command with JSON output, save output to variable, show varialbe

- name: Localhost | Yandex Cloud Create network
  ansible.builtin.command: "yc vpc network create --name network-01 --format json"
  register: network_created

- name: Localhost | Set fact
  ansible.builtin.set_fact:
    network_created_json: "{{ network_created.stdout | from_json }}"

- name: Print fact
  ansible.builtin.debug:
    msg: "Created {{ network_created_json['name'] }}"

Ansible restart service with specific name

- name: PHP| Restart services
  ansible.builtin.systemd:
    name: "{{ item }}"
    state: restarted
  loop: "{{ ansible_facts.services | select('search', '(apache|httpd|php)') | list }}"
  ignore_errors: true

Ansible sync file between hosts

This task runs on delegate_to host:

  • Copies file from local /tmp directory
  • Pastes file to remote inventory_hostname host's /opt directory
- name: "Sync database backup from app-test to {{ inventory_hostname }}"
  delegate_to: "app-test.company.com"
  ansible.builtin.synchronize:
    src: "/tmp/app-demo-{{ ansible_date_time.date }}.sql"
    dest: "/opt/app/app/db/backups"
    verify_host: false
    copy_links: True
    set_remote_user: False
    partial: true
    rsync_opts:
      - "--chown=postgres:postgres"

Ansible debug template deploy

  • debug-template.yml playbook
- name: Deploy debug-template.yml.j2 file
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Copy debug-template.yml.j2 file
      template:
        src: debug-template.yml.j2
        dest: /tmp/debug-template.yml
  • debug-template.yml.j2 jinja2 template

    ---
    FRA Hosts:
      skip_if_host_in_group: {{ groups | select('match', '^pvefra.*_host_for|esx32_host_for') | list }}
    

  • Apply Ansible playbook

    ansible-playbook debug-template.yml
    

  • See deployed template

    cat /tmp/debug-template.yml
    

This template selects Ansible groups from the Ansible inventory and make a list

Ansible task copy file from remote host to remote host

- name: Copy Ansible inventory sample for Kubespray
  ansible.builtin.copy:
    src: /home/ubuntu/kubespray/inventory/sample/
    dest: /home/ubuntu/kubespray/inventory/mycluster/
    remote_src: yes

Ansible playbook find text in multiple files

This is when there are symbolic links exists. Look for Kerberos keytab path in Apache config. Set file permission on found keytab files.

---
# Apply Ansible playbook
## ansible-playbook do_not_push_playbook.yml
- name: Test playbook. Do not push.
  hosts: <Host_Group_From_Ansible_Inventory>
  become: True
  gather_facts: True
  tasks:
    - name: Set variable apache_conf_path
      set_fact:
        apache_conf_path: "/etc/apache2"
      when: ansible_os_family == "Debian"

    - name: Set variable apache_conf_path
      set_fact:
        apache_conf_path: "/etc/httpd/conf.d"
      when: ansible_os_family == "RedHat"

    - name: Find Apache config files for Debian
      ansible.builtin.find:
        paths: "{{ apache_conf_path }}/sites-enabled"
        patterns: '*.conf'
        follow: true
        recurse: true
        file_type: any
      register: deb_conf_files
      when: ansible_os_family == "Debian"

    - name: Find Apache config files for RedHat
      ansible.builtin.find:
        paths: "{{ apache_conf_path }}"
        patterns: '*.conf'
        follow: true
        recurse: true
        file_type: any
      register: rh_conf_files
      when: ansible_os_family == 'RedHat'

    - name: Combine conf files results
      set_fact:
        conf_files: "{{ (deb_conf_files.files|default([])) + (rh_conf_files.files|default([])) }}"

    - name: Read file contents while following symlinks
      ansible.builtin.slurp:
        src: "{{ item.path }}"
      register: file_contents
      loop: "{{ conf_files }}"

    - name: Find keytab paths
      set_fact:
        keytab_path: "{{ (item.content | b64decode | regex_findall('\\s*Krb5KeyTab\\s*(\\S*)', ignorecase=True)) }}"
      loop: "{{ file_contents.results }}"
      register: keytab_paths_set

    - name: Combine results into list (remove empty elements and select unique)
      set_fact:
        keytab_paths: "{{ keytab_paths_set.results | map(attribute='ansible_facts.keytab_path') | flatten | reject('none') | unique | list }}"

    - name: Print fact keytab_paths
      ansible.builtin.debug:
        msg: "{{ keytab_paths }}" 

    - name: Grant access of keytab file to Apache group for Debian
      ansible.builtin.file:
        path: "{{ item }}"
        owner: "root"
        group: "www-data"
        mode: "0640"
      loop: "{{ keytab_paths }}"
      when: ansible_os_family == "Debian"

    - name: Grant access of keytab file to Apache group for RedHat
      ansible.builtin.file:
        path: "{{ item }}"
        owner: "root"
        group: "apache"
        mode: "0640"
      loop: "{{ keytab_paths }}"
      when: ansible_os_family == "RedHat"

Ansible RESP API HTTP request with authentication

- name: Authorize and move build-agent to TeamCity's server pool
  ansible.builtin.uri:
    url: "https://{{ teamcity_server }}/app/rest/agents/{{ item }}/authorized"
    method: PUT
    return_content: true
    headers:
      Authorization: "Bearer {{ auth_token }}"
      Content-Type: text/plain
    body: "true"
    force_basic_auth: true
    status_code: 200
    validate_certs: true
  loop: "{{ tcagent_name }}"
  delegate_to: localhost

Ansible Jinja global variable

Ansible uses Jinja for templates. It supports loops. Variables inside a loop are all local. Variable assignment in loop is cleared at the end of the iteration and cannot outlive the loop scope. When a variable is changed inside the loop, the scope of the variable stays local, and once the loop ends the original value of the variable is restored. That’s even true for variables which are created outside the loop.

But since version 2.10 you can use namespaces:

{% set ns = namespace(beds=0) %}
{% for room in house %}
    {% if room.has_bed %}
        {% set ns.beds = ns.beds + 1 %}
    {% endif %}
{% endfor %}
{{ house.address }} has {{ ns.beds }} beds.

In this case beds variable is in global scope and will keep changes after loop is done.

Ansible include variables specific for Linux distribution

First, add Debian.yml or RedHat.yml in vars directory of the Ansible role Then add task to import the variables:

- ansible.builtin.include_vars:
    file: "{{ ansible_os_family }}.yml"

Ansible include tasks specific for Linux distribution

First, add Debian.yml or RedHat.yml in tasks directory of the Ansible role Then add task to import the tasks:

- name: extra tasks OS based
  ansible.builtin.include_tasks:
    file: "{{ ansible_os_family }}.yml"

For example: - roles/example/tasks/main.yml

- name: Install the correct web server for RHEL
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'

- name: Install the correct web server for Debian
  import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'
- roles/example/tasks/redhat.yml
- name: Install web server
  ansible.builtin.yum:
    name: "httpd"
    state: present
- roles/example/tasks/debian.yml
- name: Install web server
  ansible.builtin.apt:
    name: "apache2"
    state: present

Ansible validate variable for invalid characterds

- name: Validate variable
  ansible.builtin.fail:
    msg: "Invalid entry found in variable: {{ variable }}"
  when:  
    - variable is defined
    - variable | length > 0
    - not variable | select('search', '[^.0-9:]') | list | length == 0

Ansible template variable from host_vars

  • host_vars/postgresql.yml
    postgresql_users:
      - name: "dbuser"
        password: "secretpassword"
        db: "dbname"
    
  • roles/template/template.j2
    keyname={{ postgresql_users | selectattr('name', 'equalto', 'dbuser') | map(attribute='password') | first }}
    

Components Breakdown: 1. {{ ... }}: This syntax is used in Jinja2 templates to denote an expression that should be evaluated and replaced with its result. 2. postgresql_users: This is expected to be a list of dictionaries (Ansible variables) where each dictionary contains details about a PostgreSQL user. For example:

```
postgresql_users:
 - name: dbuser
   password: secure_password_123
 - name: analytics
   password: another_password
```
  1. selectattr('name', 'equalto', 'dbuser'): This filter selects the elements from the postgresql_users list where the name attribute is equal to 'dbuser'. In our example, it will select the following dictionary:

    { 'name': 'dbuser', 'password': 'secure_password_123' }
    
  2. map(attribute='password'): This filter applies to the selected elements. It extracts the value of the password attribute from the filtered list. From our selected dictionary, this results in:

    [ 'secure_password_123' ]
    
  3. first: This filter returns the first item in the resulting sequence from the previous operation. Since our sequence only has one item, it simply returns:

    'secure_password_123'
    

Ansible role vars directory

Variables defined in the vars directory of a role have a higher precedence than those defined in host_vars. This means that host_vars cannot override variables that are set in the vars directory of a role.