Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ansible. Показать все сообщения
Показаны сообщения с ярлыком ansible. Показать все сообщения

четверг, 2 апреля 2020 г.

Как автоматически перезапускать плейбук?

#bash #ansible #devops

                    
Конфигурация проходит через VPN и возникают периодические отваливания в соединении,
что приводит к остановке плейбука для данного хоста.

Если в самом Ansible инструмент, перезапускающий плейбук до тех пор, пока .retry
не останется пустым?

Пока вижу выход в небольшом баш скрипте с whle true циклом.
    


Ответы

Ответ 1



Подобных средств на уровне плейбука не знаю, а бесконечный while это откровенный костыль. Вам могут помочь следующие вещи: включите pipelining в скриптах ansible чтобы увеличить скорость выполнения скрипта увеличьте таймаут ssh на своём сервере включите в конфиге retries если вышеперечисленное не помогло, то подумайте о режиме ansible-pull (выполнять скрипты локально на самом сервере) Также можно подумать о том, чтобы каждый шаг плейбука переписать в стиле: --- - hosts: all connection: local tasks: - shell: exit 1 register: task_result until: task_result.rc == 0 retries: 10 delay: 1 ignore_errors: yes

Ошибка Ansible: “ERROR! 'mysql_user' is not a valid attribute for a Play”

#mysql #python #linux #ansible #yaml

                    
Хост машина с Ansible 2.3.1.0 - Ubuntu 17.04, клиентская нода - Centos 7(LXC). 
Побродив в гугле на гитхабе в поисках проблемы, опробовал несколько вариантов решения
проблемы: установки пакетов MySQL-python,python-mysqldb и прочие, в т.ч и для версии
3.4. Что странно, так это то, что через плейбук я смог выполнить mysql_secure_installation
пошагово, а простого пользователя добавить не могу, когда казалось бы через ту же либо
mysql-python будет работать.
При выполнении плейбука вида:

---
- hosts: lxc01
 become: yes
 tasks:
 name: add mysql user
 mysql_user:
 name: bob
 password: 12345
 priv: '*.*:ALL, GRANT'


Получаю:

ERROR! 'mysql_user' is not a valid attribute for a Play

The error appears to have been in '/home/sat/jedi/mysq.yml': line 2, 
column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

---
- hosts: trapeznikov-lxc01
  ^ here


библиотеки требуемые для выполнения этой операции, указанные в офф документации Ansible
установлены, дальше даже пробовал модули через pip устанавливать и на хосте и на клиенте,
все равно 0 толку.
Может проще кормить в импорт .sql файлы с запросами?
Кто подскажет, что не так делаю?
    


Ответы

Ответ 1



Возможно, что вы не понимаете синтаксис yaml? Предполагаю, что вы хотели написать что-то типа: --- - name: add mysql users hosts: lxc01 become: yes tasks: - name: add mysql user1 mysql_user: name: bob1 password: 12345 priv: '*.*:ALL, GRANT' - name: add mysql user2 mysql_user: name: bob2 password: 12345 priv: '*.*:ALL, GRANT' Простой пример в документации можно посмотреть здесь: Playbook Language Example

Динамическое добавление вложенных переменных в существующую структуру переменных

#ansible

                    
Есть структура вида в котором хранятся объекты со своими какими-то свойствами, которые
прописаны в файле определения переменных:

vars:
  objects:
    - name: objectA:
      sectionB:
        property1: "value1"
        property2: "value2"
        property3: "value3"


Хотел бы динамически во время исполнения моего playbook исполнять task, который добавлял
бы в данный(ые) объект(ы) дополнительное динамически вычисляемое свойство.

Нашел много примеров, которые динамически добавляют элементы (items) в словарь (dictionary),
но вот подобного найти не получается.

Требуется сделать нечто подобное (один из вариантов, которые я пробовал использовать):

- set_fact:
    item.sectionB: "{{item.sectionB | combine(property_string) }}"
  with_items:
    - "{{ objects }}"
  vars:
    property_string: '{ property4: "value4" }'


Чтобы получить в итоге:

vars:
  objects:
    - name: objectA:
      sectionB:
        property1: value1
        property2: value2
        property3: value3
        property4: value4


Пробовал использовать функцию combine(), она в таком виде отрабатывает без ошибок,
но структуру не изменяет, а функция union() завершается ошибкой.
    


Ответы

Ответ 1



Вот здесь описан принцип решения: https://ansibledaily.com/process-complex-variables-with-set_fact-and-with_items/ Изменить изначальную переменную нельзя, но можно при помощи set_fact сделать факт с таким же именем, который в большинстве случаев будет меть приоритет к изначальной переменной. Первым шагом формируете новый список объектов с добавлением к элементам нужных атрибутов через combine. Примерно: - set_fact: tmp_object: "{{ item | combine(<объет_с_дополнениям>) }}" with_items: "{{ objects }}" register: tmp_objects И потом из tmp_objects достаем то, что нам на самом деле нужно: - set_fact: objects: {{ tmp_objects.results | map(attribute='ansible_facts.tmp_object') | list }}"

воскресенье, 29 марта 2020 г.

как в ansible заменить строку по регулярному выражению

#регулярные_выражения #ansible


Для работы со строками есть replace и lineinfile, но они позволяют полностью удалить
строку при обнаржении шаблона, а replace дает заменить вхождения шаблона(но не всю
строку). Т.е. получается сначала удалить все обнаружонные строки, а потом вставить
нужную в нужном месте через linеinfile.
А как вот чтобы одним действием?
    


Ответы

Ответ 1



- name: Замена с обратными ссылками lineinfile: path: some.conf regexp: '^(.*)match(.*)$' line: '\1replacement\2' backrefs: yes Документация

среда, 4 марта 2020 г.

Как правильно добавить репозиторий в ansible?

#linux #ansible


У меня есть centOS. И для его раскатки написан такой ansible скрипт:

- hosts: all
  sudo: yes

  tasks:
          - name: set vm.max_map_count
            sysctl: name=vm.max_map_count value=262144 state=present

          - name: ensure all interfaces are up
            service: name=network enabled=yes state=restarted

          - name: install docker
            yum: name=docker-1.10.3 state=latest


Проблема в том, что изначально в yum репозиториях нет докера. Такая задача не помогает:

  - name: add docker repository to yum 
    yum_repository:
            name: docker-ce
            description: docer-ce repository
            baseurl: https://download.docker.com/linux/centos/docker-ce.repo


Возникает проблема с ключами GPG:

Alternatively you can specify the url to the key you would like to use
for a repository in the 'gpgkey' option in a repository section and yum
will install it for you.

For more information contact your distribution or package provider.


Как организовать установку докера правильно?
    


Ответы

Ответ 1



Модуль yum_repository поддерживает работу с ключами: - name: Add the YUM Docker repository. yum_repository: name: docker description: Docker Repository baseurl: https://download.docker.com/linux/centos/docker-ce.repo gpgkey: https://download.docker.com/linux/centos/gpg gpgcheck: yes

Ответ 2



Я так добавляю, PGP не спрашивает - name: Add docker repository get_url: url: https://download.docker.com/linux/centos/docker-ce.repo dest: /etc/yum.repos.d/docer-ce.repo become: yes

понедельник, 24 февраля 2020 г.

Ansible и Windows

#windows #ansible


Существует ли поддержка Ansible в Windows powershell? Или его возможно поднять только
по Cygwin? Как его установить?
    


Ответы

Ответ 1



Windows - клиент. Это стандартный случай использования ansible, описанный в документации. Требования: На управляющей машине Ansible версии 1.7+ Python модуль pywinrm: pip install "pywinrm>=0.1.1" В соответствующем group_vars установить несколько переменных для настройки соединения с windows-хостами. Этот group_vars должен быть зашифрован с помощью ansible-vault. Если необходима авторизация через kerberos: pip install kerberos и дополнительная конфигурация kerberos. На клиенте Для работы большей части модулей ansible понадобится Powershell 3.0, доступный начиная с Windows 7 SP1, Windows Server 2008 SP1. Есть также скрипт для быстрой установки Powershell 3.0. Windows - управляющая машина Официальная документация говорит о том, что эта возможность не поддерживается и не планируется. Но варианты решения всё-таки есть. Очевидный способ решения: вылить воду из чайника, чем свести задачу к предыдущей поставить Linux в виртуальной машине и работать с него. Jeff Geerling описывает способ установки через cygwin. Если кратко, требования такие: Cygwin c набором необходимых модулей Отдельно установить и сконфигурировать Ansible, PyYAML, Jinja2 При необходимости работы через прокси, дополнительно сконфигурировать .bash_profile вашего cygwin.

Ответ 2



Тут говорится, что да. В качестве бэкенда юзает winrm. Ставить ничего не надо. Ansible - безклиентская система управления. В отличии от puppet. Там же говорится, что в качестве управляющего сервера нужен всё-таки linux.

Ответ 3



Начиная с Windows 10 1809, можно запустить Ansible внутри WSL (по сути в той-же виртуальной машине, как предложил Nick Volynkin) Набросал скрипт для этого, если кому надо - пользуйтесь https://github.com/rikipm/ansible-on-windows-wsl

Параллельная и последовательная обработка в ansible

#ansible


Вопрос вообще возник из того, что при клонировании крупных репозиториев вижу, что
порядок вывода на экран меняется.

Ну, чтобы не быть голословным, - конкретный пример для ansible 2.1

Набор данных:

  repositories:
    mediawiki_core1:
      repo: https://gerrit.wikimedia.org/r/p/mediawiki/core.git
      path: w1
    mediawiki_core2:
      repo: https://gerrit.wikimedia.org/r/p/mediawiki/core.git
      path: w2
      version: REL1_25
    mediawiki_core3:
      repo: https://gerrit.wikimedia.org/r/p/mediawiki/core.git
      path: w3


Обработчик:

- name: clone repositories
  git:
   repo: "{{ item.value.repo }}"
   dest: "/root/tests/{{ item.value.path }}/"
   version: "{{ item.value.version | default('HEAD') }}"
  become: true
  become_user: apache
  with_dict: "{{ repositories }}"


Вывод окажется таким:


  TASK [abcdef : clone repositories]
  
  
  
  changed: [server.domain.ru] => (item={'value': {u'repo':
  u'https://gerrit.wikimedia.org/r/p/mediawiki/core.git', u'path':
  u'w1'}, 'key': u'mediawiki_core1'})
  
  changed: [server.domain.ru] => (item={'value': {u'repo':
  u'https://gerrit.wikimedia.org/r/p/mediawiki/core.git', u'path':
  u'w3'}, 'key': u'mediawiki_core3'})
  
  changed: [server.domain.ru] => (item={'value': {u'repo':
  u'https://gerrit.wikimedia.org/r/p/mediawiki/core.git', u'path':
  u'w2', u'version': u'REL1_25'}, 'key': u'mediawiki_core2'})


Нужно сделать так, чтобы гарантированно выполнить последовательно задачи одну за
другой -- вне зависимости от того, сколько длится её выполнение.

Типичный пример: после того, как клонируется движок вики (очень длительная задача),
можно приступать к клонированию множества мелких репозиториев со скринами и экстеншнами.

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

P.S. Что самое странное: именно в приведённом примере проблема с порядком возникает,
а вот когда запускаешь разные репозитории (первым -- тяжёлый медиавики, вторым -- небольшой
собственный репозиторий), проблема перестаёт воспроизводиться: сначала долго отрабатывает
первый таск, потом быстро пролетает второй. 
В мистику не верю: это просто говорит о непонимании, как оно всё работает под капотом.
    


Ответы

Ответ 1



Насколько я понял гоняя допонительные тесты, дело вообще не в том, сколько по времени выполняется задача внутри dict -- а в том, что сортировка для dict не гарантирована. Об этом было в этом вопросе на большом СО. Точнее, обход with_dict проходит по dict не в том порядке, в котором я указываю элементы: идёт внутренняя сортировка по значению хеша. Для того, чтобы обходить элементы в том порядке, в котором я записал их - нужно заменять dict на items и обход делать не with_dict, а with_items. vars: with_dict_test: - { key: 'one', value: 1 } - { key: 'two', value: 2 } - { key: 'three', value: 3 } - { key: 'four', value: 4 } - { key: 'five', value: 5 } tasks: - name: with_dict test debug: msg="{{item.key}} --> {{item.value}}" with_items: "{{ with_dict_test }}" Или, точный пример, как у меня в вопросе: vars: with_dict_test: - { repo: 'url1', path: 'A', version: 'REL1_25' } - { repo: 'url2', path: 'B' } tasks: - name: with_dict test debug: msg="{{item.repo}} --> {{item.path}} -- {{ item.version | default ('HEAD') }}" with_items: "{{ with_dict_test }}" И что-то мне такая форма сильно напоминает ;) Открываем документацию, раздел Standard Loops и видим: Note that the types of items you iterate over with ‘with_items’ do not have to be simple lists of strings. If you have a list of hashes, you can reference subkeys using things like: - name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' } Вот так совсем хорошо: with_dict_test: - { repo: 'url1' , version: 'REL1_25' , path: 'A' } - { repo: 'url2' , path: 'B' }

четверг, 6 февраля 2020 г.

Запуск задачи в Ansible от имени другого пользователя

#ansible #sudo


Есть работающий от рута плейбук для ansible. (знаю, что от рута плохо, но на то есть
причины), который, от рута же, делает свои дела на неком скопе серверов.

Есть PHP-шная админка, работающая от имени wwwrun

Хочется запускать плейбук по кнопочке в админке. То есть юзер wwwrun запускает плейбук,
который исполняется от рута.

Так вот вопрос. Можно ли сделать такое настройками ролей в ansible(или по другому
как?), не выдавая пользователю wwwrun возможность повышения своих привилегий?

PS Документацию и интернеты читал, ответа на вопрос не нашел. Может плохо читал конечно,
но тогда киньте ссылкой, где про это написано.
    


Ответы

Ответ 1



Создаёте скриптик #!/bin/sh ansible-playbook ... В sudoers добавляете разрешение пользователю wwwrun запускать именно этот созданный скрипт: wwwrun ALL=(ALL) NOPASSWD: /full/path/to/script Технически, это повышение привилегий пользователем, но только для одной определённой команды, что при условии отсутствия прав на редактирование этого скрипта (и плейбука!) вполне допустимо. Другой вариант - очередь полноценная с сервером очередей (избыточно, если только для этой задачи и нужно) или её подобие. Самое простое на файлике: Веб-морда создаёт где-нибудь файлик с определённым именем, отмечающий, что нужно запустить плейбук. по крону запускается шелл-скритп, проверяющий, существует ли такой файлик. Если есть - запускает плейбук и после удаляет файлик соответственно на веб-морде проверка - если файл ещё есть, значит плейбук ещё не выполнился. В бонусе - одновременно выполняется только один плейбук. Минус - крон запускается самое частое раз в минуту. Вопросов, почему плейбук выполняется от рута и зачем для этого веб-морда, пожалуй, касаться не буду.

Ответ 2



Если вас действительно интересует именно ansible way. Скрипт ansible который запускается на некотором сервере server-ansible-01 из-под пользователя user01 и выполняет работу на другом сервере server-web-01 действительно может работать из-под пользователя ansible сервера server-web-01. (При чём server-ansible-01 и server-web-01 могут оказаться одним и тем же сервером -- и это как раз ваш случай) Это совершенно штатная фича ansible. Задаётся она на уровне плейбука, а не роли -- хотя если будет желание, то для отдельных шагов роли, подключаемой в плейбуке можно будет указать и другие credentials. Просто укажите в своём плейбуке remote_user: - name: asdf hosts: '{{ target }}' remote_user: root roles: - myrole01 Также если вы подключились к удалённому server-ansible-01 как ansible, то вы можете и некоторые шаги выполнить как пользователь apache или root. Читаем про become - Become (Privilege Escalation) Это работает в современных версиях ансибл (кажется, с 1.9, у меня в 2.1 точно уже давно есть), раньше были другие команды, sudo_user и sudo, можете поискать в документации, я уже давно свои плейбуки отрефакторил. Вопросы, которые касаются возможности подключения к другому серверу -- они всегда сводятся к корректной настройке ssh-ключей (рекомендую почитать в этом моём вопросе: Ansible: форвард ssh-агента и sudo ) и частично будет затронут вопрос passwordless sudo: - name: nopasswd sudo for ansible user lineinfile: "dest=/etc/sudoers state=present regexp='^{{ ansible_user.login }}' line='{{ ansible_user.login }} ALL=(ALL) NOPASSWD: ALL'" Я очень сильно не приветствую то, что вы хотите выполять скрипты ansible из-под аккаунта root (у меня все скрипты спокойно работают и без этого, более того -- со включенным SELinux), этот костыль если вы не хотите убирать -- это уже ваше собственное решение. В целом же ничего не мешает запустить плейбук от имени пользователя wwwrun версебрвера и начать выполнение скриптов на "удалённом" веб-сервере (с тем же самым IP) но уже как root этого веб-сервера. Всё вышенаписанное касается именно выполнения плейбука от другого пользователя. Но. Вы в свой вопрос уместили совершенно другой вопрос: "как мне запустить sh-скрипт из админки сайта, но не от пользователя веб-сервера". Этот вопрос уже никоим образом не относится к ансибл, ответ на него вам дали выше -- если вам важнее именно эта часть вопроса, то рекомендую принять именно ответ @Мелкий -- там ничего нет про ансибл, это просто разрешение wwwrun на sudoers одной определённой команды. Более того, если хотите конкретизировать вопрос и поставить задачу "запускать не просто абы какой скрипт, а именно плейбук ansible", то у вас могут возникнуть сложности именно в связи с тем, что пользователь wwwrun из соображений безопасности имеет урезанные права - вам нужно будет ему в /usr/apache создавать собственный ssh ключ и много чего ещё исправлять. Проходил, знаю. Не то, чтобы эти проблемы нерешаемы, просто я в последнее время как-то больше внимания стал уделять тому, чтобы при создании решений не проделывать дыры в безопасности, а дыр этих тут у вас хватает.

пятница, 24 января 2020 г.

Аналог grep -v для ansible

#python #регулярные_выражения #администрирование #ansible #jinja2


Нужно получить всех когда-либо входивших пользователей на винде. В списке не должно
быть папки Public.

Такая конструкция не работает:

- name: TEST find 4 win
  block:
    - win_find:
        paths: C:\Users\
        file_type: directory
        excludes: Public
      register: win_find_out
    - debug:
        msg: "{{ win_find_out.files }}"


Есть ещё параметр patterns, но !Public, ?!Public или not (Public) не работают - выхлоп
вообще пропадает.

ansible 2.7.5
    


Ответы

Ответ 1



Вы можете использовать такое регулярное выражение: ^(?!.*Public) См. демо регулярного выражения онлайн. Подробности ^ - начало строки (?!.*Public) - исключающий блок предварительного просмотра вперёд, который не вернёт совпадения, если сразу после текущей позиции имеется .* - 0 и более символов, отличных от символа перевода на новую строку Public - строка Public Если в строке могут быть символы переноса строки, добавьте (?s) в начало: (?s)^(?!.*Public)

Как работает sudo в ansible?

#linux #ansible


подскажите, пожалуйста, как работает sudo в ansible и в чем его разница, если я применяю
sudo непосредственно в консоли,
Авторизованным на сервере, в консоли выполняю

[user@server01 opt]$ sudo mkdir /opt/test


Выполняется успешно, но при использовании команды непосредственно с помощью ansible:

[user@server01 opt]$ ansible servers -m file -a "dest=/opt/test  mode=755" -k -u user -b


Возникает ошибка

"module_stdout": "Sorry, user user is not allowed to execute '/bin/sh -c echo BECOME-SUCCESS-qvlcsrrsgixvikeamiollrbbnyxljoxl


Используется стандартные become настройки, включая метод, sudo.
У меня и в самом деле ограниченный sudo на команды /bin/sh и /bin/bash, вопрос заключается
в следующем, в чем разница моего локального выполнения sudo и через ansible, почему
ansible под пользователем на конечном сервере пытается выполнить не sudo mkdir /opt/test,
а как понимаю sudo /bin/sh mkdir /opt/test, в следствии чего возникает ошибка is not
allowed to execute '/bin/sh mkdir /opt/test' as root?

права sudo -l

(ALL) NOPASSWD: ALL, !/bin/sh, !/bin/tcsh, !/bin/csh, !/bin/zsh, !/bin/ksh, !/bin/bash,
!/usr/bin/sudo, !/bin/su, !/usr/bin/mc
(root) NOPASSWD: ALL
(root) NOPASSWD: ALL, !/bin/sh, !/bin/tcsh, !/bin/csh, !/bin/zsh, !/bin/ksh, !/bin/bash,
!/usr/bin/sudo, !/bin/su, !/usr/bin/mc, !/usr/bin/chattr, !/usr/bin/screen, !/usr/bin/tmux


P.S. коллеги, если убрать из запретов !/bin/sh и !/bin/bash, то конечно будет работать
и аналогичный вопрос конечно уже был, но может с 17 года появились ответы, Как запустить
плейбук ansible непривелигированному пользователю?
    


Ответы

Ответ 1



О-о-о, помню эту фигню. Суть в том, что Ansible перед тем, как выполнить собственно команду, зачем-то проверяет, можно ли вообще использовать sudo. И делает он это через /bin/sh -c echo …. И если этот echo не удаётся, он считает, что sudo не sudo. И это почти нигде не документировано! У меня была ситуация, где sudo разрешалось использовать одну-две команды, и никакого /bin/sh. Я с этим часа два боролся, а потом плюнул и решил просто прописать sudo внутри команды текстом и не использовать этот их сломанный, слишком «умный» become вовсе. Возможно, придёт другой коллега и покажет класс, но для меня ответ был простой: «Это сломано».

четверг, 23 января 2020 г.

Ansible долго выполняет task setup

#ansible


Периодически возникает проблема с ansible-playbook: очень долго "висит" на задаче
setup. Подробный режим ничего внятного не сообщил:

TASK [setup] *******************************************************************
ESTABLISH LOCAL CONNECTION FOR USER: dmitry
127.0.0.1 EXEC /bin/sh -c '( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1464268193.51-68811809432139
`" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1464268193.51-68811809432139 `" )'
127.0.0.1 PUT /tmp/tmpD6mRtI TO /home/dmitry/.ansible/tmp/ansible-tmp-1464268193.51-68811809432139/setup
127.0.0.1 EXEC /bin/sh -c 'LANG=ru_RU.UTF-8 LC_ALL=ru_RU.UTF-8 LC_MESSAGES=ru_RU.UTF-8
/usr/bin/python /home/dmitry/.ansible/tmp/ansible-tmp-1464268193.51-68811809432139/setup;
rm -rf "/home/dmitry/.ansible/tmp/ansible-tmp-1464268193.51-68811809432139/" > /dev/null
2>&1'


Сколько это занимает по времени точно не засекал, но где-то порядка 10-15 минут,
дальше выполнение происходит в нормальном темпе.

Сталкивался ли кто с таким, как лечить?
    


Ответы

Ответ 1



Я думаю, что это весьма похоже на очень частую проблему "очень долго тупит midnight commander при запуске" (проблема разрешения имён - в hosts не прописан ip для hostname) - и у вас что-то не то с разрешением имён хоста. Плюс за эту версию -- что имя хоста разрешилось в 127.0.0.1 судя по вашему выводу. Больше никакой конкретики не подскажу: если направление решения задачи угадано верно - то нужно знать, как у вас устроена сеть на предприятии - какие DNS-сервера, DHCP-сервера, какое окружение и т.п.

Инициализация нового хоста ansible: как проверить возможность подключения к хосту

#ansible


У меня используется ansible 2.0

Есть скрипты, которые выполняют различные настройки серверов, общие части вынесены
в роли и т.п. Настройка выполняется на каждом сервере из-под учётной записи automate_ansible,
который может passwordless sudo: в ansible_config прописан remote_user, все скрипты
имеют строку become -- в общем, всё давно работает.

Всё хорошо, кроме того, что нужно первоначально этого пользователя создать. Есть
как раз один скрипт, который коннектится к серверу как root, создаёт пользователя ansbile,
делает некоторые настройки по корпоративным стандартам.

Боль-печаль заключается в том, что этот скрипт -- единственный неидемпонентный: среди
его шагов есть один шаг, в котором устанавливается запрет подключения по ssh пользователем
root и поэтому как только выполнишь скрипт -- уже работают все остальные, но этот уже
вылетит с ошибкой подключения к серверу.

А очень хочется иметь возможность запускать этот скрипт ещё раз, чтобы ансибл перезаписал
заново пароли рута, те же самые конфиги ssh, пользователей и т.п.

Есть ли возможность как-то проверять предварительно соединение с сервером (ткнуться
поду учёткой спец. пользователя, если есть -- пройти весь остаток скрипта из-под него,
только с become, если нет -- зайти root'ом и остаток скрипта пройти как чистый рут)?

Все варианты решения, которые я знаю - это лишь костыли.

Либо можно написать крошечный скрипт инициализации, который выполнит всего одно действие
(создаст пользователя для анисибл и отвалится) -- а все остальные действия вынести
в скрипт инициализация2.

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

Хочется возможности тестирования соединения ssh. Это реально сделать или не предусмотрено?
Кто и как решает подобную задачу? Или может и нет большого смысла в том, чтобы пытаться
её решить?
    


Ответы

Ответ 1



На днях на большом stackoverflow было обсуждение этой же темы (там только порт ssh перевешивается на нестандартный), видимо нужно признать, что наиболее употребимый подход -- это вынесение в отдельный плейбук инициализации шагов по настройке. Получается, что каждый скрипт остаётся идемпонентным. Кто-то старается побольше всего вместить в такие плейбуки, кто-то меньше -- но направление в целом одинаковое.

воскресенье, 12 января 2020 г.

Перебирать порты для соединения по ssh в Ansible

#ansible


Есть, к примеру, хост:

[api]
10.0.0.1 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_port=22


Здесь мы указываем 22 порт для ssh. Но на некоторых серверных стоит другой порт,
скажем 220.

Возможно как-то задать дополнительный порт для соединения по ssh на случай, если
не смог присоединиться по 22? Каким-нибудь списком, чтоб Ansible по очереди перебирал.
    


Ответы

Ответ 1



Частично воспользовался примером от Nick Volynkin. Вначале плейбука проверяем порты, выбираем тот, что доступен и устанавливаем в ansible_ssh_port, после чего Ansible будет уже подсоединяться к машине по этому порту. Единственный минус - использованием wait_for. То есть, в данном случае мы ждём 15 секунд (т.к. проверяем 3 порта), пока порты проверятся. - name: just test hosts: server gather_facts: false vars: list_of_ssh_ports: [22, 220, 234] tasks: - name: test ssh on port sudo: no local_action: wait_for port={{item}} timeout=5 host={{inventory_hostname}} register: ssh_checks with_items: "{{list_of_ssh_ports}}" ignore_errors: true - debug: msg = "{{item}}" with_items: "{{ssh_checks.results}}" - name: set available ansible_ssh_port sudo: no set_fact: ansible_ssh_port={{item.item}} when: ssh_checks is defined and {{item.elapsed}} < 5 with_items: "{{ssh_checks.results}}" - hosts: server roles: - остальные таски

суббота, 11 января 2020 г.

Как заставить ansible перелогиниться при включенном pipelining?

#ssh #ansible


Когда в конфиге ansible включен pipelinining, он переиспользует ssh-соединение для
всех задач.
Иногда одна из задач меняет привилегии текущего пользователя, например, добавляет
пользователя в группу, и для применения этих привилегий приходится перезапускать весь
playbook.  

Конкретно, я столкнулся с этой проблемой при установке docker, для избежания become:
true задач контейнеризации, я добавляю текущего пользователя в группу docker.

Существует ли способ форсировать реаутентификацию без перезапуска playbook?  

Я знаю, что можно переопределить переменную окружения ANSIBLE_SSH_PIPELINING=0 для
всего playbook, но он довольно большой - повторный запуск гораздо быстрее.
    


Ответы

Ответ 1



Если говорить о конкретной проблеме добавления пользователя в группу, то перезагрузить группы можно без выхода: sudo gpasswd -a username somegroup newgrp somegroup Или конкретно для вашего случая: newgrp docker В случае Ansible этот трюк может не сработать, в таком случае, начиная с версии 2.3, можно указать Ansible повторно установить соединение: - user: name={{ansible_user}} groups=docker append=yes - name: update effective groups to include docker meta: reset_connection

пятница, 10 января 2020 г.

Как использовать ключи из /etc/ssh_known_hosts в скриптах git

#git #ssh #ansible


Сценарий. Пользователь alice заходит на сервер msk-web-01 (centos7.2, selinux включен)
по своему ssh ключу и хочет в папке /www/mysite.ru/htdocs/ (владелец папки -- пользователь
apache) запустить команду git pull.

Для этого написан небольшой батник:

#!/bin/sh

#see http://ru.stackoverflow.com/questions/548545/ for details
sudo setfacl -m apache:x   $(dirname "$SSH_AUTH_SOCK")
sudo setfacl -m apache:rwx "$SSH_AUTH_SOCK"

cd /www/mysite.ru/htdocs/
pwd
sudo su -s /bin/sh apache -c "/usr/bin/git pull"
....


И он работает... выдавая многочисленные предупреждения:


/www/site1.ru/htdocs
Could not create directory '/usr/share/httpd/.ssh'.
Failed to add the ECDSA host key for IP address '1.2.3.4' to the list of known hosts
(/usr/share/httpd/.ssh/known_ho).
Already up-to-date.
/www/site2.ru/htdocs
Could not create directory '/usr/share/httpd/.ssh'.
Failed to add the ECDSA host key for IP address '1.2.3.4' to the list of known hosts
(/usr/share/httpd/.ssh/known_ho).
Already up-to-date.



Задача -- избавиться от этих лишних записей, добившись чистого вывода при помощи
добавления записей в глобальный known_hosts.

Требуемый эффект можно получить если создать /usr/share/httpd/.ssh/known_hosts со
строкой CheckHostIP no:


/www/site1.ru/htdocs
Already up-to-date.
/www/site2.ru/htdocs
Already up-to-date.
/www/site3.ru/htdocs
Already up-to-date.



Разумеется, такой способ не рассматривается как решение задачи, так же как и другие
обходные пути типа "совсем отключить проверку" (скажем, раз или два)

PS Ключи сохранял одним из двух способов, первый:

ssh-keyscan -t rsa,dsa git.mycomany.ru >> /etc/ssh/ssh_known_hosts


второй:

ssh-keyscan git.mycomany.ru >> /etc/ssh/ssh_known_hosts


Разница не особо велика: в первом случае Failed to add the RSA host, во втором -
Failed to add the ECDSA host key.

И даже так с горя:

ssh-keyscan git.mycomany.ru,1.2.3.4 >> /etc/ssh/ssh_known_hosts

    


Ответы

Ответ 1



как выяснилось в комментариях, /usr/share/httpd — это домашний каталог пользователя apache. именно этому пользователю и должен принадлежать каталог (и всё его содержимое) /usr/share/httpd/.ssh: $ sudo chown -R apache /usr/share/httpd/.ssh также он должен быть доступен только самому пользователю: $ sudo chmod -R go= /usr/share/httpd/.ssh добавить публичные ключи (по умолчанию — типов rsa, ecdsa и ed25519 — см. $ man ssh-keyscan) машины git.mycompany.ru в файл «известных хостов» пользователя apache (/usr/share/httpd/.ssh/known_hosts) можно так: $ ssh-keyscan git.mycompany.ru | sudo tee -a /usr/share/httpd/.ssh/known_hosts если этот файл до этого не существовал, команды из первого пункта надо повторить после этой операции. вместо манипуляций с acl-ами (setfacl ...) лучше добавить строку Defaults env_keep+=SSH_AUTH_SOCK в /etc/sudoers (или даже ещё лучше — в файл с произвольным именем, не содержащем точек и тильд в имени, в каталоге /etc/sudoers.d). подробнее см., например, здесь. редактировать эти файлы лучше через «обёртку» visudo — см., например, здесь. уточнение в связи с требованием не использовать каталог ~/.ssh пользователя apache. чтобы не использовать ~/.ssh/config, но иметь возможность указать особую конфигурацию для конкретного пользователя, можно воспользоваться директивой match user в /etc/ssh/ssh_config, добавив в конец (это важно — см. $ man ssh_config на предмет опции match) этого файла примерно следующее: match user apache # какие-либо персональные настройки для пользователя apache чтобы процесс ssh не использовал ~/.ssh/knonw_hosts, можно (способом, описанным в предыдущем пункте) переопределить для данного пользователя значение конфигурационной переменной userknownhostsfile, указав в ней, например, файл /etc/ssh/ssh_known_hosts (или любой другой доступный пользователю для чтения): userknownhostsfile /etc/ssh/ssh_known_hosts нужные ключи в этот файл, разумеется, уже должны быть добавлены заранее. например, тем способом, что описан во втором пункте первой части ответа. возможно, для пользователей по умолчанию включена необходимость хешировать записи в known_hosts, тогда процесс ssh попытается перезаписать файл с ключами. чтобы он этого не делал, укажите (хотя бы для этого пользователя) не хешировать записи, добавив (как описано в первом пункте) строку: hashknownhosts no

Ответ 2



В итоге, всё оказалось достаточно просто. Сначала ещё раз о постановке задачи. Есть ряд доверенных серверов организации, между которыми сотрудникам нужно перемещаться с сохранением авторизации (ForwardAgent), поэтому нужно уметь заполнять файл /etc/ssh/ssh_known_hosts доверенными данными. Во-первых, гит в своём общении с удалёнными репозиториями по протоколу ssh полагается на системные утилиты, особых настроек ssh-подключения нет -- можно почитать про хаки с созданием файла ssh и экспортированием переменной GIT_SSH. Поэтому изначально в вопросе было больше про ssh, чем про конкретно git. Во-вторых, когда я начал разбираться с ключами (rsa, dsa, esdca) и версиями протоколов -- я решил не только абстрагироваться от гит, но и от ансибл - чтобы не влияло, на всякий случай. После тестов у меня получилось, что ключи были сгенерированы правильно, настройки ssh (как дефолтные центоса, так и мои кастомные) тоже не влияют на работу. Всё, что нужно -- это сдампить отпечатки и записать в общий known_hosts: ssh-keyscan -t rsa,dsa git.mycomany.ru >> /etc/ssh/ssh_known_hosts Включение хеширования не влияет на итог - тоже. А что влияет? Как ни странно -- выяснилось, что влияет ansible. Если файла /etc/ssh/ssh_known_hosts на диске нет -- он создаёт файл с правами rw-r--r-- но стоит лишь ещё раз запустить повторно -- права чудесным образом становятся rw------- Разумеется, что процессы, которые хотят прочитать хранилище и проверить, нет ли там такого отпечатка обламываются и ничего не находят. Стоит лишь восстановить права на файл -- и работа снова восстанавливается. Пример заполнения ключей практически такой же, как и в документации: - name: global pubkes for servers known_hosts: path='/etc/ssh/ssh_known_hosts' name='{{ item }}' key="{{ lookup('file', 'files/pubkeys/{{ item }}.pub') }}" with_items: - git.mycompany.ru Я хотел было создать баг на гитхабе -- но перед заведением поискал, нет ли готового. И нашёл, вот: https://github.com/ansible/ansible-modules-extras/issues/2513

четверг, 26 декабря 2019 г.

Минимизировать ущерб в случае проблем во время настройки sshd и фаерволла

#ssh #ansible


Типичный плейбук у меня включает обычно конфигурирование фаервола и ssh:

- hosts: api
  roles:
    - sshd
    - apf


Для sshd просто копируется темплейт с нужным портом в /etc/ssh/sshd_config, где:

# What ports, IPs and protocols we listen for
Port {{ ssh_port }}


После чего установка apf происходит, настройка /etc/apf-firewall/allow_hosts.rules
и основного /etc/apf-firewall/conf.apf.
Как понимаете в conf.apf:

IG_TCP_CPORTS="{{ ssh_port }}"


Предпосылка: скажем у нас уже установлен sshd и apf и порт для ssh прописан 22.

При реконфигурации (например хотим сменить порт на 222) может статься так, что между
настройкой sshd и настройкой apf что-то пойдёт не так. 

В итоге ssh будет на 222 порту, но в apf будет открыт старый 22 порт. В итоге по
ssh уже не присоединиться, придётся ребутить машину в recovery mod и ручками исправлять всё.

Данный пример я привёл так как сам столкнулся с подобным. Но и во многих других задачах
при возникновении проблемы посередине выполнения role система в итоге останется в неконсистентом
состоянии.

Как можно подобные вещи предотвратить или свести к минимуму ущерб?
    


Ответы

Ответ 1



А что, если переходить постепенно? apf – открыть порт 222. Если сломается после этого шага, у вас просто будет лишний открытый порт 222. sshd – переключиться на порт 222 Если сломается здесь, то тоже останется лишний открытый 22, но по ssh вы уже сможете зайти на 222й. apf – закрыть порт 222

среда, 25 декабря 2019 г.

Как временно “приглушить” monit на период развертывания сервиса через ansible?

#linux #развертывание #ansible #monit


Есть сервер на linux, на нем работает некий сервис myservice, за сервисом наблюдает
monit. Сообщает на почту обо всех изменениях и при необходимости перезагружает сервис.

Есть ansible playbook, который переустанавливает сервис, конфигурирует и перезапускает.
И monit тоже конфигурирует и перезапускает при необходимости. Работает это отлично,
но плохо то, что при этом monit шлет пачку писем:


service instance пропал
service instance появился
service instance сменил PID
monit перезагрузился


Можно в роли давать monit задачу приостанавливать мониторинг, но об этой задаче он
тоже сообщает. Дважды.

- name: monit не наблюдает за myservice
  shell: monit unmonitor myservice

- name: monit наблюдает за myservice
  shell: monit monitor myservice


Можно перезапускать сервис из самого monit'a. Тогда письма приходят с менее угрожающими
заголовками, вроде Action done myservice. Но всё равно одно или два письма + одно про
релоад монита.

- name: Перезапуск myservice через monit
  shell: monit restart myservice


Я беспокоюсь о том, что это всё сработает как у мальчика, который кричал «Волки!».
Все привыкнут, что с почты постоянно валится куча писем и что это нормальный процесс,
волноваться не о чем. Поэтому хочу, чтобы штатные работы не приводили ни к каким тревогам
вообще.

Можно ли это как-то реализовать на этом стеке технологий? Или я категорически неправ
в своих опасениях?
    


Ответы

Ответ 1



Решение monit config: /etc/monit/monitrc set alert {{mail_recipient}} but not on { action, instance } ansible: Во всех командах параметр -I нужен, почему — смотри ниже. В начале установки через monit останавливаем сервис, это считается action - name: Остановка myservice через monit shell: monit -I stop myservice Потом производим всю необходимую установку и настройку. Убеждаемся, что сам monit запущен. Перезагружаем конфиги через сам monit, это событие типа instance - name: Убедиться, что monit запущен service: name=monit state=started - name: Перезагрузка конфигов monit shell: monit -I reload Потом перезагружаем сервис, это считается action - name: Перезапуск myservice через monit shell: monit -I restart myservice Конфигурация monit Получать только определенные события set alert foo@bar only on { timeout, nonexist } Получать все события кроме некоторых: set alert foo@bar but not on { instance } Подробнее про фильтрацию событий в monit: https://mmonit.com/monit/documentation/monit.html#Setting-an-event-filter Оставшиеся проблемы monit "забывает" команды, отданные после перезагрузки — решено. Разобрано в отдельном вопросе, здесь я просто добавляю -I к командам. C такой конфигурацией monit перестает сообщать об изменениях собственного instance не только на период развертывания, а вообще. То есть, если он был перезагружен вручную, либо если упал а потом был поднят при развертывании — то сообщения не будет. Можно оставить только такой конфиг: set alert {{mail_recipient}} but not on { action } Но тогда при развертывании все равно будет одно сообщение на почту про sudo monit reload. Безуспешно пробовал такую схему, она не работает. name: monit - приостановить мониторинг shell: monit unmonitor all name: monit - перезагрузка конфигов shell: monit reload name: monit - возобновить мониторинг shell: monit monitor all

воскресенье, 15 декабря 2019 г.

Копирование файлов посредством ansible

#ansible


Что-то не смог понять из примеров в документации - как же скопировать файл с удалённого
сервера на локальный и наоборот? Вот есть модули copy и fetch, например. Вот я пишу
в плейбуке:

- name: Copy file from remote host to local machine
  fetch: src=/tmp/somefile dest=/tmp/fetched(взято из доки)


Как указать ansible, с какого хоста я хочу скопировать файл? Ну и соответственно,
при копировании на удалённый хост, где указывать этот удалённый хост?

P.S. Немного разобрался:
Чтобы скопировать файл с удалённого хоста на локальный:

- hosts: localhost

  vars_files:
    - config.yml

  tasks:
    - include: ../share/dev.yml

    - name: Get file from remote
      fetch: src="{{ remote_sources_path }}/test.txt" dest="backup"
      delegate_to: '{{ remote_host }}'
      tags: fetch


Файл test.txt будет лежать в каталоге ./backup/localhost/{{ remote_sources_path }}

Скопировать с локального на удалённый:

- name: Send file to remote
  copy: src="{{ local_sources_path }}/to_remote_test.txt" dest={{ remote_sources_path }}
  delegate_to: '{{ remote_host }}'
  tags: fetch


Подозреваю, что, если указать в начале - hosts: {{ remote_host }} вместо localhost,
то delegate_to использовать не нужно.
    


Ответы

Ответ 1



Ну зачем же так сложно? )) Все эти задачи решаются и без мороки с Delegation и Local Actions, вам же самим потом сложнее будет разбираться в собственных плейбуках или читать чужие, написанные по-простому. Смотрите. В каждом плейбуке вы указываете на каких хостах запускать задачи - пишете hosts: '{{ target }}' (если хотите из командной строки управлять списком через --extra-vars "target=10.0.100.123") или hosts: dbservers (если фиксировать плейбук на группу хостов): --- # This playbook for quick tests. - name: quick tests hosts: '{{ target }}' become: true become_user: root roles: - role1 - role2 tasks: - name: install mc yum: name=mc state=latest - name: install wget yum: name=wget state=latest Эти хосты - "удалённый" сервер, ну а "локальный" - это само собой хост, на котором находится ваш плейбук. Теперь: Для копирования файла с локального сервера на удалённый -- используете модуль copy Для копирования файла с удалённого сервера на локальный -- используете модуль fetch И в copy и в fetch src - это откуда брать файл, а dest - это куда положить файл. Для copy src=local а dest=remote, для fetch -- наоборот src=remote, а dest=local. Вот и всё. Вам кстати именно об этом говорили в комментарии к вашему последнему вопросу: "В целом Ansible подразумевает то, что ты уже на удаленном хосте", не надо нарезать лишние петли вокруг локалхоста. PS Если нужно копировать с произвольного хоста на произвольный хост - то смотрите в сторону модуля syncronyze на основе rsync. Но вообще в 90% случаев вам понадобится копирование с локального хоста на удалённый и будете использовать copy или template.

четверг, 11 июля 2019 г.

Условия в vars у Ansible task

- roles - consul - vars/main.yml
В main.yml использую:
consul_is_server: {{ true if consul_server is defined else false }}
playbook:
- hosts: consul-server roles: - consul vars: consul_server: true
Ошибка:
consul_is_server: {{ "true" if consul_server is defined else "false" }} ^ We could be wrong, but this one looks like it might be an issue with missing quotes. Always quote template expression brackets when they start a value. For instance:
with_items: - {{ foo }}
Should be written as:
with_items: - "{{ foo }}"
Сделал так:
consul_is_server: > {{ true if consul_server is defined and consul_server==true else false }}
Но тогда consul_is_server будет строкой: "False" или "True". И тогда в шаблонах приходится кастить в bool
"server": {{ "true" if consul_is_server |bool else "false" }}
Можно как-то в vars написать так проверку условий, чтоб потом в шаблоне кастить не пришлось?


Ответ

Используйте ternary
Документация: http://docs.ansible.com/playbooks_filters.html
Пример:
--- # https://ru.stackoverflow.com/questions/470202/
- name: https://ru.stackoverflow.com/questions/470202/ hosts: test vars: str1: "asdf1" str2: "asdf2" cond1: True cond2: False result1: "{{ cond1 | ternary(str1, str2) }}" result2: "{{ cond2 | ternary(str1, str2) }}" tasks: - name: debug result1 debug: msg="{{ result1 }}" connection: local - name: debug result2 debug: msg="{{ result2 }}" connection: local
Вывод:
$ ansible-playbook -i hosts_debug sample_ru_470202.yml
PLAY [collect info] ************************************************************
TASK [setup] ******************************************************************* ok: [myserver1]
TASK [debug result1] *********************************************************** ok: [myserver1] => { "msg": "asdf1" }
TASK [debug result2] *********************************************************** ok: [myserver1] => { "msg": "asdf2" }
PLAY RECAP ********************************************************************* myserver1 : ok=3 changed=0 unreachable=0 failed=0
PS Более сложный пример вот тут сам спросил сегодня: Составная переменная в ternary j2-шаблона ansible

вторник, 9 июля 2019 г.

Как создать папку на другом сервере при помощи ansible?

Внимание! Этот вопрос является переводом вопроса: How to create a directory using Ansible?
Как создать папку /src/www на другом сервере при помощи ansible? Операционная система на удалённом сервере - debian или centos.


Ответ

Используйте модуль file с параметром state=directory.
Минимальный вариант:
- name: сreate directory file: path=/src/www state=directory
или:
- name: сreate directory file: path=/src/www state=directory
Дополнительно можно указать опциональные параметры, например, владельца папки или права на папку:
- name: сreate directory file: path=/src/www mode=0755 owner=root group=root state=directory
Также обратите внимание, что создавать последовательно вложенные папки нет никакой необходимости. Вместо:
- name: сreate directory file: path=/src/www state=directory - name: сreate subdirectory file: path=/src/www/logs state=directory
Можно сразу указывать сразу путь целиком: все необходимые папки и подпапки будут созданы автоматически.
- name: сreate directories file: path=/src/www/logs state=directory
Исключение -- случай, когда вам необходимо указывать различные уровни доступа, владельцев и т.п.

Об идемпотентности
Как и все модули ansible, file поддерживает идемпотентность выполнения. В случае, если на диске уже есть папка, которую вы пытаетесь создать, папка создаваться заново не будет.
Здесь есть один нюанс. Если вы указываете только имя папки и не удаляете каталог, а лишь меняете права доступа и/или владельца, то при повторном запуске плейбука права доступа или владельца не будут восстановлены к значениям по умолчанию, с которым была создана папка.
А вот в случае, когда вы не только указываете путь к папке, но и права доступа и владельца - то (в случае если они были изменены) ansible при повторных запусках плейбука будет не только проверять наличие папки, но и права доступа и владельца.
Например:
папка не удалена, владелец и права доступа не изменены: задача не будет выполнена, значение changed не будет увеличено папка не удалена, владелец не изменился, но изменены права доступа на папку: задача будет выполнена (папка не будет пересоздаваться, лишь восстановятся права доступа), значение changes увеличится на единицу
Продемонстрирую на следующем примере. Есть скрипт создания папки:
- name: http://ru.stackoverflow.com/questions/609260/ hosts: webservers become: true become_user: root tasks: - name: сreate directory file: path=/home/ansible/www mode=0775 state=directory
Запускаем в первый раз - создаётся папка:
$ ansible-playbook -i hosts_development so609260.yml
PLAY [http://ru.stackoverflow.com/questions/609260/] ***************************
TASK [setup] ******************************************************************* ok: [myserver.tld]
TASK [сreate directory] ******************************************************** changed: [myserver.tld]
PLAY RECAP ********************************************************************* myserver.tld : ok=2 changed=1 unreachable=0 failed=0
Заходим на удалённый сервер, смотрим - всё правильно:
# ls -l /home/ansible drwxrwxr-x. 2 root root 6 дек 29 17:09 www
При повторном запуске скрипта - ничего не меняется:
$ ansible-playbook -i hosts_development so609260.yml
PLAY [http://ru.stackoverflow.com/questions/609260/] ***************************
TASK [setup] ******************************************************************* ok: [myserver.tld]
TASK [сreate directory] ******************************************************** ok: [myserver.tld]
PLAY RECAP ********************************************************************* myserver.tld : ok=2 changed=0 unreachable=0 failed=0
Но вот если сымитировать некоторые вандальные действия (папка не удалена, но кто-то поменял права доступа):
# chmod 0777 /home/ansible/www # ls -l /home/ansible drwxrwxrwx. 2 root root 6 дек 29 17:09 www
То повторный запуск восстановит права на папку:
$ ansible-playbook -i hosts_development so609260.yml
PLAY [http://ru.stackoverflow.com/questions/609260/] ***************************
TASK [setup] ******************************************************************* ok: [myserver.tld]
TASK [сreate directory] ******************************************************** changed: [myserver.tld]
PLAY RECAP ********************************************************************* myserver.tld : ok=2 changed=1 unreachable=0 failed=0
Как и было:
# ls -l /home/ansible drwxrwxr-x. 2 root root 6 дек 29 17:09 www