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 & regex_replace Filters: Pattern Matching Guide

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

Complete guide to Ansible regex_search and regex_replace filters. Extract text with regex patterns, find and replace strings, validate formats, parse log.

Ansible's regex_search and regex_replace filters let you extract and transform text using regular expressions — parse version numbers, validate formats, clean strings, and extract values from command output.

regex_search: Find and Extract

Returns the first match (or None if no match):

- name: Extract version number
  ansible.builtin.set_fact:
    version: "{{ 'nginx/1.24.0' | regex_search('[0-9]+\\.[0-9]+\\.[0-9]+') }}"
# Result: "1.24.0"

- name: Check if string matches pattern ansible.builtin.debug: msg: "Valid IP address" when: my_var | regex_search('^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$')

See also: Ansible regex_replace Filter: Find & Replace with Regex (Complete Guide)

Capture Groups

# Extract specific parts using capture groups
- name: Extract major version
  ansible.builtin.set_fact:
    major: "{{ 'Python 3.11.5' | regex_search('([0-9]+)\\.([0-9]+)\\.([0-9]+)', '\\1') }}"
    minor: "{{ 'Python 3.11.5' | regex_search('([0-9]+)\\.([0-9]+)\\.([0-9]+)', '\\2') }}"
    patch: "{{ 'Python 3.11.5' | regex_search('([0-9]+)\\.([0-9]+)\\.([0-9]+)', '\\3') }}"
# major: ["3"], minor: ["11"], patch: ["5"]

# First element of the list - ansible.builtin.debug: msg: "Major version: {{ major | first }}"

regex_replace: Find and Replace

# Basic replacement
- name: Remove comments from config
  ansible.builtin.set_fact:
    clean_config: "{{ raw_config | regex_replace('#.*$', '', multiline=True) }}"

# Replace with capture groups - name: Swap first and last name ansible.builtin.set_fact: formatted: "{{ 'John Smith' | regex_replace('^(\\w+)\\s(\\w+)$', '\\2, \\1') }}" # Result: "Smith, John"

# Remove non-alphanumeric characters - name: Sanitize filename ansible.builtin.set_fact: safe_name: "{{ user_input | regex_replace('[^a-zA-Z0-9_-]', '_') }}"

See also: Ansible combine Filter: Merge Dictionaries & Override Defaults (Guide)

Common Patterns

Parse Command Output

- name: Get kernel version
  ansible.builtin.command: uname -r
  register: kernel_output
  changed_when: false

- name: Extract major.minor ansible.builtin.set_fact: kernel_version: "{{ kernel_output.stdout | regex_search('^([0-9]+\\.[0-9]+)') }}" # "6.8.0-41-generic" → "6.8"

Validate Email Format

- name: Validate email
  ansible.builtin.assert:
    that:
      - user_email | regex_search('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$')
    fail_msg: "Invalid email: {{ user_email }}"

Extract IP from Text

- name: Extract IP address
  ansible.builtin.set_fact:
    ip_address: "{{ log_line | regex_search('([0-9]{1,3}\\.){3}[0-9]{1,3}') }}"

Clean Whitespace

# Remove extra whitespace
- ansible.builtin.set_fact:
    clean: "{{ messy_string | regex_replace('\\s+', ' ') | trim }}"

Parse Key=Value Pairs

- name: Extract value from config line
  ansible.builtin.set_fact:
    db_port: "{{ config_line | regex_search('port=([0-9]+)', '\\1') | first }}"
# "host=localhost port=5432 dbname=myapp" → "5432"

Strip HTML Tags

- ansible.builtin.set_fact:
    plain_text: "{{ html_content | regex_replace('<[^>]+>', '') }}"

Case-Insensitive Matching

- ansible.builtin.set_fact:
    found: "{{ text | regex_search('error', ignorecase=True) }}"

See also: Ansible dict2items Filter: Convert Dictionaries to Lists (Guide)

Multiline Matching

- ansible.builtin.set_fact:
    block: "{{ multiline_text | regex_search('BEGIN(.+?)END', '\\1', multiline=True, dotall=True) }}"

Using regex in when Conditions

- name: Only run on RHEL 8.x or 9.x
  ansible.builtin.debug:
    msg: "Running on RHEL {{ ansible_distribution_version }}"
  when: ansible_distribution_version | regex_search('^[89]\\.')

- name: Skip if hostname contains 'test' ansible.builtin.include_tasks: production-tasks.yml when: not (inventory_hostname | regex_search('test|staging|dev'))

regex_findall: All Matches

# Find ALL occurrences (not just first)
- name: Extract all IPs from log
  ansible.builtin.set_fact:
    all_ips: "{{ log_content | regex_findall('([0-9]{1,3}\\.){3}[0-9]{1,3}') }}"

# Find all email addresses - ansible.builtin.set_fact: emails: "{{ text | regex_findall('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}') }}"

# regex_search — Jinja2 filter, returns match or None
{{ string | regex_search('pattern') }}

# match — Jinja2 test, matches from START of string {{ string is match('pattern') }}

# search — Jinja2 test, matches ANYWHERE in string {{ string is search('pattern') }}

# Examples
- ansible.builtin.debug:
    msg: "Starts with ansible"
  when: my_string is match('ansible.*')

- ansible.builtin.debug: msg: "Contains error anywhere" when: my_string is search('error')

- ansible.builtin.set_fact: version: "{{ my_string | regex_search('[0-9]+\\.[0-9]+') }}"

FAQ

Why does regex_search return None instead of matching?

Common causes: not escaping backslashes (use \\d not \d in YAML), or the pattern expects start/end anchors. Test your regex at regex101.com first, then double-escape for YAML.

How do I use capture groups with regex_search?

Pass the group reference as additional arguments: regex_search('pattern(group)', '\\1'). This returns a list, so use | first to get the string value.

What regex flavor does Ansible use?

Python re module — supports \d, \w, \s, lookahead (?=...), lookbehind (?<=...), and named groups (?P...).

How do I make regex_replace work on multiline strings?

Add multiline=True for ^ and $ to match line boundaries, or dotall=True for . to match newlines.

Conclusion

Use regex_search to extract data from strings, regex_replace to transform strings, and regex_findall to find all occurrences. Always double-escape backslashes in YAML (\\d not \d). For simple contains/startswith checks, prefer is search and is match tests — they're more readable than regex_search for boolean conditions.

Related Articles

Ansible map vs selectattr vs json_queryAnsible Jinja2 Templates GuideAnsible lineinfile Module CookbookAnsible Conditionals Guide

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home