Условия в Ansible: директива when и логика

Рейтинг: 67.6% · 8 голосов
Подробный курс по Ansible с прицелом на экзамен RHCE EX294: установка и инвентарь, плейбуки, переменные и факты, Vault, циклы и условия, шаблоны Jinja2, роли и коллекции, Execution Environments и ansible-navigator, управление системами (диски, LVM, cron, SELinux, firewalld). Примеры на RHEL/Fedora. Актуально на 2026.
Ответить
Аватара пользователя
Maksim_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Условия в Ansible: директива when и логика

Сообщение Maksim_DevOps »

Оглавление курса (47)
  1. Что такое Ansible и зачем он нужен: автоматизация без агентов
  2. Сертификация RHCE и экзамен EX294: что внутри и как готовиться
  3. Архитектура Ansible: control node, узлы, модули и плагины
  4. Установка Ansible на control node: dnf, pip и версии ansible-core
  5. Подготовка управляемых узлов: SSH, пользователь и sudo
  6. Инвентарь Ansible: hosts, группы и переменные
  7. Настройка Ansible: файл ansible.cfg и приоритеты конфигурации
  8. Host patterns и инструменты командной строки Ansible
  9. Ad hoc команды Ansible: быстрые задачи без плейбука
  10. Ansible playbook: что это такое и как устроен
  11. Структура плейбука: play, task, модули и переменные
  12. Запуск плейбуков: проверка, теги, ограничения и отладка
  13. Параллелизм и порядок выполнения: forks, serial, strategy
  14. Факты Ansible: сбор информации об узлах
  15. Переменные Ansible: типы, объявление и приоритет
  16. Регистрация результатов и специальные переменные
  17. Ansible Vault: шифрование паролей и секретов
  18. Организация инвентаря: group_vars, host_vars и вложенные группы
  19. Динамический инвентарь и инвентарные плагины
  20. Циклы в Ansible: loop, списки и словари
  21. Повтор задачи до условия: until, retries и delay
  22. Условия в Ansible: директива when и логика (вы здесь)
  23. Jinja2 в Ansible: выражения, фильтры и подстановки
  24. Обработчики Ansible: handlers, notify и flush_handlers
  25. Обработка ошибок: failed_when, changed_when и ignore_errors
  26. Блоки в Ansible: block, rescue и always
  27. Управление файлами: модули file, copy, fetch и stat
  28. Архивы и сборка файлов: archive, unarchive, assemble
  29. Точечное редактирование файлов: lineinfile и blockinfile
  30. Шаблоны Jinja2: модуль template и динамические конфиги
  31. Jinja2 продвинуто: фильтры, циклы, макросы и lookup
  32. Модули Ansible: ansible-doc и поиск нужного модуля
  33. Разработка собственного модуля Ansible на Python
  34. Роли Ansible и Ansible Galaxy: переиспользование
  35. Разработка роли Ansible: создание с нуля
  36. Зависимости ролей и requirements.yml
  37. Коллекции Ansible: ansible.posix, community и своя коллекция
  38. Execution Environments: контейнеры для запуска Ansible
  39. Сборка Execution Environment с ansible-builder
  40. ansible-navigator: запуск плейбуков в Execution Environment
  41. Управление SSH-ключами через Ansible: authorized_key
  42. Управление дисками: filesystem, mount и parted
  43. LVM через Ansible: тома, группы и расширение
  44. Планировщик задач: cron и systemd timers через Ansible
  45. Безопасность: hardening SSH и репозитории dnf/yum
  46. SELinux через Ansible: булевы, контексты и порты
  47. Сквозной проект и подготовка к экзамену EX294
Представь плейбук, который ставит пакеты, открывает порты и правит конфиги одинаково на всех хостах подряд. На паре RHEL он отработает, а на Debian-ноде задача с dnf просто упадёт, потому что там apt. Или ты хочешь перезапустить сервис только если конфиг реально поменялся, а не каждый прогон. Городить под каждый случай отдельный плейбук - тупик. Решает это одна короткая директива - when. Это твой if внутри задачи: условие истинно - задача выполняется, ложно - Ansible её пропускает (skipped) и идёт дальше. Разберём условия в Ansible по-взрослому: от простых сравнений до логики and/or/not, проверок is defined и работы с register.

Как работает ansible when: механика и синтаксис

Ключевое, что надо понять сразу: значение when - это выражение Jinja2, но БЕЗ фигурных скобок. Внутри when ты пишешь голое выражение, как будто оно уже внутри {{ }}. Поэтому переменные тут указываются по имени, без скобок и в большинстве случаев без кавычек.

Код: Выделить всё

- name: Поставить httpd только на RHEL-семействе
  ansible.builtin.package:
    name: httpd
    state: present
  when: ansible_facts['os_family'] == "RedHat"
Тут ansible_facts['os_family'] - это факт, собранный модулем setup на этапе Gathering Facts. Сравниваем его со строкой "RedHat". Обрати внимание: переменную слева в кавычки НЕ берём, а вот строку справа - берём, потому что это литерал. Это и есть классический ansible when os_family, который ты будешь писать постоянно: один плейбук, разные ветки под RHEL/Fedora и под Debian/Ubuntu.

Доступные операторы - обычные питоновские: == != > < >= <=, логические and / or / not, проверка вхождения in, а также Jinja-тесты через is: is defined, is not defined, is none.

Изображение

Условие ansible на фактах, переменных и результатах register

Источников для условия три, и каждый ты будешь использовать.

1. Факты. Имя дистрибутива, версия, объём памяти, список интерфейсов - всё это факты. Современный канонический доступ - через словарь ansible_facts:

Код: Выделить всё

- name: Открыть порт 80 только если дистрибутив именно RHEL 9
  ansible.posix.firewalld:
    service: http
    permanent: true
    immediate: true
    state: enabled
  when:
    - ansible_facts['distribution'] == "RedHat"
    - ansible_facts['distribution_major_version'] == "9"
Заметь: версия сравнивается со строкой "9", а не с числом 9 - факт приходит строкой. Старый стиль ansible_os_family и ansible_distribution тоже работает (это те же факты, инжектированные как отдельные переменные), но в новых плейбуках под RHCE привыкай к форме ansible_facts['...'].

2. Обычные переменные. Их удобно проверять на сравнение или на само наличие:

Код: Выделить всё

- name: Включить debug-режим, если он явно задан в true
  ansible.builtin.debug:
    msg: "Режим отладки активен"
  when: enable_debug | default(false) | bool
3. Результат register. Самый частый сценарий на практике - "сделай Б, только если А дало такой-то результат". Сохраняем итог задачи в переменную через register и ветвимся по её полям:

Код: Выделить всё

- name: Проверить, есть ли файл-флаг
  ansible.builtin.stat:
    path: /etc/app/installed.flag
  register: flag

- name: Развернуть приложение, если флага ещё нет
  ansible.builtin.command: /opt/app/install.sh
  when: not flag.stat.exists
У register-переменной куча полезных полей: rc (код возврата), stdout, stderr, changed, failed, а после команд - ещё stdout_lines. Типичная проверка по тексту вывода:

Код: Выделить всё

- name: Узнать версию ядра
  ansible.builtin.command: uname -r
  register: kern
  changed_when: false

- name: Предупредить про старое ядро
  ansible.builtin.debug:
    msg: "Внимание: ядро серии 4.x"
  when: '"4." in kern.stdout'
Логика: and, or, not, in и список условий

Несколько условий комбинируются логическими операторами прямо в строке:

Код: Выделить всё

when: ansible_facts['os_family'] == "RedHat" and ansible_facts['distribution_major_version'] == "9"
Но есть способ читабельнее. Если передать when список, Ansible соединит элементы неявным AND - все условия должны быть истинны:

Код: Выделить всё

when:
  - ansible_facts['os_family'] == "RedHat"
  - ansible_facts['distribution_major_version'] | int >= 9
  - install_web | default(true) | bool
Это эквивалент трёх условий через and, но глазами читается куда легче. А вот OR в одну строку списком не выразишь - для "или" пиши явный or или группируй скобками: (a and b) or c.

Проверка наличия переменной - отдельная важная тема, на ней часто срезаются:

Код: Выделить всё

when: ssl_cert is defined
when: backup_path is not defined
when: my_list is defined and my_list | length > 0
Если переменная не определена, а ты просто сравниваешь её через ==, плейбук упадёт с undefined variable. Поэтому сначала is defined, потом сравнение - порядок в списке важен, Ansible проверяет слева направо (короткое замыкание).

Оператор in удобен для проверки членства - и в списке, и в строке:

Код: Выделить всё

when: ansible_facts['distribution'] in ["RedHat", "CentOS", "Rocky", "AlmaLinux"]
when с циклами и условные блоки

Когда when стоит рядом с loop, условие проверяется для каждого элемента отдельно. Внутри условия доступна переменная item:

Код: Выделить всё

- name: Поставить только тяжёлые пакеты
  ansible.builtin.package:
    name: "{{ item.name }}"
    state: present
  loop:
    - { name: nginx, heavy: true }
    - { name: vim, heavy: false }
    - { name: postgresql, heavy: true }
  when: item.heavy
Тут vim будет пропущен, а nginx и postgresql - установлены. Если же тебе надо одним условием прикрыть СРАЗУ ПАЧКУ задач, не дублируя when в каждой, используй block:

Код: Выделить всё

- name: Настройка веб-стека (только RHEL-семейство)
  block:
    - name: Установить веб-сервер
      ansible.builtin.package:
        name: httpd
        state: present

    - name: Запустить и включить сервис
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true
  when: ansible_facts['os_family'] == "RedHat"
when на блоке применяется ко всем задачам внутри. Для Debian/Ubuntu сделаешь параллельный block с when ... == "Debian" и пакетом apache2; в RED OS и Astra os_family будет тем же "RedHat" и "Debian" соответственно, так что ветки работают и для российских дистрибутивов.

Типичные грабли
  • Кавычки вокруг переменной в when. Самая частая ошибка новичков. Писать when: "ansible_facts['os_family'] == 'RedHat'" целиком в кавычках НЕ надо - переменную внутри when кавычить нельзя, иначе она станет просто строкой и условие всегда будет истинным. Кавычки нужны только для строковых ЛИТЕРАЛОВ ("RedHat") и для всего выражения целиком только тогда, когда YAML спотыкается о двоеточие или начальную кавычку.
  • Фигурные скобки. when: "{{ my_var }}" работать как ожидаешь не будет и кинет варнинг. Внутри when скобки не нужны - пиши when: my_var.
  • Сравнение версии с числом. ansible_facts['distribution_major_version'] - строка. Сравнивай со строкой "9" или прогоняй через | int перед числовым сравнением.
  • undefined без is defined. Сравнение неопределённой переменной валит плей. Сначала is defined.
  • Булевы строки. "true" из vars - это строка, а не булево. Прогоняй через | bool, чтобы when сработал корректно.
На экзамене EX294 when - один из самых частых элементов: почти каждая практическая задача требует "сделай это только если...". Срезаются обычно на кавычках, на сравнении версии и на забытом is defined.

Мини-лаба

Собери один плейбук на смешанном инвентаре (хотя бы одна RHEL/Fedora-нода) и повтори руками:
  • задача с when: ansible_facts['os_family'] == "RedHat" ставит httpd, а параллельный block под Debian ставит apache2;
  • через register сохрани вывод командой и сделай вторую задачу зависимой от not ...stat.exists или от текста в stdout;
  • проверь is defined: запусти плейбук с -e "ssl=on" и без, посмотри как меняется skipped;
  • поставь when на loop и убедись, что часть item пропускается;
  • заверни две задачи в block с одним when и глянь в выводе, что условие применилось к обеим.
Гоняй с key -v, чтобы видеть skipped/changed по каждой задаче.

Контрольные вопросы
  • Нужны ли фигурные скобки {{ }} и кавычки вокруг переменной внутри when? Почему?
  • Чем отличается when списком от when с and в одну строку, и можно ли так выразить OR?
  • Как безопасно проверить переменную, которая может быть не определена, и почему важен порядок условий?
  • Как ведёт себя when, если он стоит рядом с loop?
Итог

when - это if на уровне задачи: голое Jinja-выражение без скобок, с переменными без кавычек. Условия строятся на фактах (ansible_facts['os_family']), обычных переменных и результатах register. Операторы обычные питоновские плюс and/or/not, in и тесты is defined. Список условий = неявный AND и читается лучше. Один when на block прикрывает сразу группу задач, а рядом с loop условие проверяется для каждого item. Запомни три грабли - кавычки, фигурные скобки и забытый is defined - и на экзамене эта тема перестанет тебя цеплять.
👍3 ❤️3 🔥3 😄 🤔
Аватара пользователя
KernelHacker
Сообщения: 1
Зарегистрирован: 14 май 2026, 20:05

Re: Условия в Ansible: директива when и логика

Сообщение KernelHacker »

Спасибо, наконец дошло про кавычки. Полдня вчера ловил баг - условие всегда срабатывало, а я весь when в одинарные кавычки завернул, и переменная превратилась в строку. Снял кавычки с переменной, оставил только у RedHat - заработало.
👍2 ❤️2 🔥 😄 🤔
Аватара пользователя
zigwhale
Сообщения: 1
Зарегистрирован: 14 май 2026, 05:42

Re: Условия в Ansible: директива when и логика

Сообщение zigwhale »

Вопрос по версии: а если хостов и RHEL 8, и RHEL 9, и хочется >= 8 - правильно через distribution_major_version | int >= 8 или есть способ красивее? Просто сравнение строк '8' и '10' же сломается.
👍 ❤️1 🔥1 😄 🤔
Ответить
← Предыдущая глава
Повтор задачи до условия: until, retries и delay
Следующая глава →
Jinja2 в Ansible: выражения, фильтры и подстановки

Все главы курса «Ansible: автоматизация и подготовка к RHCE (EX294)»

Поделиться темой: ✈ Telegram VK
Похожие запросы: что такое ansible простыми словамиansible для начинающих с чего начатьустановка ansible на linuxansible inventory и hosts файлчто такое playbook в ansibleструктура ansible playbook из чего состоит

Вернуться в «Ansible: автоматизация и подготовка к RHCE (EX294)»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость