AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible regex_search Filter: Search Strings with Regex Patterns (Guide)

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

How to search strings with regex in Ansible using the regex_search filter. Extract matches, capture groups, validate patterns in Jinja2.

The regex_search filter in Ansible extracts text from strings using regular expressions. It's invaluable for parsing command output, extracting values from configuration files, and implementing conditional logic based on pattern matching. This guide covers syntax, capture groups, multiline matching, and real-world examples for production automation.

regex_search searches a string for a regex pattern and returns the first match. If no match is found, it returns an empty string (which is falsy in Jinja2). This makes it perfect for both extraction and conditional logic.

Basic Syntax

# Returns the matched string or empty string
{{ my_string | regex_search('pattern') }}

# With flags (multiline, ignorecase, etc.) {{ my_string | regex_search('pattern', multiline=True, ignorecase=True) }}

# With capture groups - returns the group {{ my_string | regex_search('version: (\d+\.\d+)', '\\1') }}

See also: Ansible split Filter: Split Strings into Lists (Complete Guide)

Practical Examples

Example 1: Extract Version Numbers

---
- name: Parse version information
  hosts: all
  tasks:
    - name: Get application version
      ansible.builtin.command:
        cmd: nginx -v
      register: nginx_output
      changed_when: false
      failed_when: false

- name: Extract version number ansible.builtin.set_fact: nginx_version: "{{ nginx_output.stderr | regex_search('nginx/([\\d.]+)', '\\1') | first }}" nginx_major: "{{ nginx_output.stderr | regex_search('nginx/(\\d+)\\.', '\\1') | first }}"

- name: Display version ansible.builtin.debug: msg: "Nginx version: {{ nginx_version }}, Major: {{ nginx_major }}"

Example 2: Parse Configuration Values

---
- name: Extract config values
  hosts: all
  tasks:
    - name: Read config file
      ansible.builtin.slurp:
        src: /etc/myapp/config.conf
      register: config_file

- name: Parse config values ansible.builtin.set_fact: config_content: "{{ config_file.content | b64decode }}"

- name: Extract specific settings ansible.builtin.set_fact: db_port: "{{ config_content | regex_search('db_port\\s*=\\s*(\\d+)', '\\1') | first | default('5432') }}" db_host: "{{ config_content | regex_search('db_host\\s*=\\s*(\\S+)', '\\1') | first | default('localhost') }}" max_connections: "{{ config_content | regex_search('max_connections\\s*=\\s*(\\d+)', '\\1') | first | default('100') }}"

- name: Display parsed config ansible.builtin.debug: msg: "DB: {{ db_host }}:{{ db_port }}, Max Connections: {{ max_connections }}"

Example 3: Conditional Logic with Pattern Matching

---
- name: Conditional actions based on patterns
  hosts: all
  tasks:
    - name: Get kernel version
      ansible.builtin.command:
        cmd: uname -r
      register: kernel_info
      changed_when: false

- name: Check if running RHEL kernel ansible.builtin.set_fact: is_rhel_kernel: "{{ kernel_info.stdout | regex_search('\\.el[0-9]+') | length > 0 }}" kernel_major: "{{ kernel_info.stdout | regex_search('^(\\d+)\\.', '\\1') | first }}"

- name: Apply RHEL-specific tuning ansible.builtin.template: src: sysctl-rhel.conf.j2 dest: /etc/sysctl.d/99-custom.conf when: is_rhel_kernel | bool

Example 4: Parse Log Files for Errors

---
- name: Analyze log files
  hosts: all
  tasks:
    - name: Read recent log entries
      ansible.builtin.command:
        cmd: tail -100 /var/log/myapp/error.log
      register: log_content
      changed_when: false
      failed_when: false

- name: Check for critical errors ansible.builtin.set_fact: has_oom_error: "{{ log_content.stdout | regex_search('Out[Oo]f[Mm]emory|OOM|Cannot allocate memory') | default('', true) | length > 0 }}" has_disk_error: "{{ log_content.stdout | regex_search('No space left on device|ENOSPC') | default('', true) | length > 0 }}" last_error_time: "{{ log_content.stdout | regex_search('(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}).*ERROR', '\\1') | first | default('none') }}"

- name: Alert on critical errors ansible.builtin.debug: msg: "CRITICAL: OOM detected, last error at {{ last_error_time }}" when: has_oom_error | bool

Example 5: Extract IP Addresses and Network Info

---
- name: Parse network information
  hosts: all
  tasks:
    - name: Get IP configuration
      ansible.builtin.command:
        cmd: ip addr show eth0
      register: ip_output
      changed_when: false

- name: Extract network details ansible.builtin.set_fact: ipv4_address: "{{ ip_output.stdout | regex_search('inet (\\d+\\.\\d+\\.\\d+\\.\\d+)', '\\1') | first }}" ipv4_cidr: "{{ ip_output.stdout | regex_search('inet \\d+\\.\\d+\\.\\d+\\.\\d+/(\\d+)', '\\1') | first }}" mac_address: "{{ ip_output.stdout | regex_search('link/ether ([0-9a-f:]+)', '\\1') | first }}" mtu: "{{ ip_output.stdout | regex_search('mtu (\\d+)', '\\1') | first }}"

- name: Display network info ansible.builtin.debug: msg: "IP: {{ ipv4_address }}/{{ ipv4_cidr }}, MAC: {{ mac_address }}, MTU: {{ mtu }}"

Example 6: Multiline Pattern Matching

---
- name: Multiline regex parsing
  hosts: all
  tasks:
    - name: Read SSL certificate info
      ansible.builtin.command:
        cmd: openssl x509 -in /etc/ssl/server.crt -noout -text
      register: cert_info
      changed_when: false

- name: Extract certificate details ansible.builtin.set_fact: cert_cn: "{{ cert_info.stdout | regex_search('Subject:.*CN\\s*=\\s*([^\\n,]+)', '\\1', multiline=True) | first | trim }}" cert_expiry: "{{ cert_info.stdout | regex_search('Not After\\s*:\\s*(.+)', '\\1', multiline=True) | first | trim }}" cert_issuer: "{{ cert_info.stdout | regex_search('Issuer:.*CN\\s*=\\s*([^\\n,]+)', '\\1', multiline=True) | first | trim }}"

- name: Check certificate validity ansible.builtin.debug: msg: "Certificate for {{ cert_cn }} expires: {{ cert_expiry }}, issued by {{ cert_issuer }}"

regex_search vs regex_findall vs regex_replace

| Filter | Returns | Use Case | |--------|---------|----------| | regex_search | First match (string) | Extract a single value | | regex_findall | All matches (list) | Find all occurrences | | regex_replace | Modified string | Transform text |

# regex_search - get first match
version: "{{ output | regex_search('v(\\d+\\.\\d+)', '\\1') | first }}"

# regex_findall - get all matches all_ips: "{{ output | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+') }}"

# regex_replace - transform text clean: "{{ output | regex_replace('\\s+', ' ') }}"

See also: Ansible Split String: Filter Guide for CSV, Delimiters & Lists

Regex Flags

| Flag | Description | |------|-------------| | ignorecase=True | Case-insensitive matching | | multiline=True | ^ and $ match line boundaries |

# Case-insensitive search
{{ text | regex_search('error|warning|critical', ignorecase=True) }}

# Multiline (^ matches start of each line) {{ text | regex_search('^ERROR:.*$', multiline=True) }}

Common Patterns Reference

# IP address
{{ text | regex_search('\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}') }}

# Email address {{ text | regex_search('[\\w.+-]+@[\\w-]+\\.[\\w.]+') }}

# Semantic version {{ text | regex_search('(\\d+\\.\\d+\\.\\d+)', '\\1') | first }}

# UUID {{ text | regex_search('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') }}

# Key=value pair {{ text | regex_search('mykey=(\\S+)', '\\1') | first }}

# Between delimiters {{ text | regex_search('\\[(.+?)\\]', '\\1') | first }}

See also: Ansible set_fact Module: Set Variables Dynamically (Complete Guide 2026)

Troubleshooting

Empty String vs None

regex_search returns an empty string when no match is found. Always use default():

# Safe extraction with default
{{ output | regex_search('version: (\\S+)', '\\1') | first | default('unknown') }}

Escaping Backslashes

In YAML, backslashes need double-escaping in double-quoted strings:

# Double-quoted: escape twice
result: "{{ text | regex_search('\\d+\\.\\d+') }}"

# Single-quoted or block: no double escape needed in the regex itself # but Jinja2 still needs \\

Capture Group with first

When using capture groups, regex_search returns a list. Always pipe through first:

# Returns a list ['1.0'] - need | first
{{ 'version: 1.0' | regex_search('version: (\\S+)', '\\1') | first }}

Frequently Asked Questions

What does regex_search return when there is no match in Ansible?

It returns an empty string "", which is falsy in Jinja2. You can use this for conditional logic: when: my_var | regex_search('pattern') will be false when there's no match.

How do I use capture groups with regex_search?

Pass the group reference as an additional argument: regex_search('pattern (group1) (group2)', '\\1'). This returns a list of matches for that group. Pipe through | first to get the string value.

What is the difference between regex_search and regex_findall?

regex_search returns only the first match as a string (or list with capture groups). regex_findall returns all matches as a list. Use regex_search when you need one value, regex_findall when you need all occurrences.

Can I use regex_search in a when condition?

Yes. Since an empty string is falsy, you can write: when: variable | regex_search('pattern'). This runs the task only if the pattern matches.

How do I make regex_search case-insensitive?

Add ignorecase=True: {{ text | regex_search('error', ignorecase=True) }}.

Related Articles

Ansible Jinja2 FiltersAnsible set_fact ModuleAnsible builtin command ModuleAnsible Error Handling

Conclusion

The regex_search filter is a powerful tool for extracting and validating data in Ansible playbooks. Combined with set_fact and register, it enables your automation to parse command output, validate configurations, and make decisions based on text patterns. Master the patterns in this guide — version extraction, config parsing, log analysis, and network info parsing — and you'll handle any text processing challenge in your Ansible workflows.

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home