--- - name: Универсальная динамическая синхронизация всех SSL-сертификатов Let's Encrypt hosts: mail_servers become: true vars: local_tmp_dir: "/tmp/ansible_certs" # Имя вашего старого сервера, как оно записано в файле inventory Ansible source_host_inventory_name: "old_server" tasks: - name: БЛОК АВТОМАТИЧЕСКОГО СБОРА (Выполняется на сервере-источнике) block: - name: Динамический поиск только папок доменов (игнорируем README и файлы) ansible.builtin.find: paths: "/etc/letsencrypt/live" file_type: directory recurse: false excludes: "all,README" register: found_live_dirs - name: Формирование чистого списка доменов ansible.builtin.set_fact: dynamic_domains: "{{ found_live_dirs.files | map(attribute='path') | map('basename') | list }}" - name: Создание локальной структуры папок на управляющей машине (Ansible Host) ansible.builtin.file: path: "{{ local_tmp_dir }}/{{ item }}" state: directory mode: '0755' delegate_to: localhost run_once: true loop: [ 'archive', 'live', 'renewal' ] - name: Скачивание папок archive (сохраняем оригиналы файлов) ansible.builtin.fetch: src: "/etc/letsencrypt/archive/{{ item }}/" dest: "{{ local_tmp_dir }}/archive/{{ item }}/" flat: false loop: "{{ dynamic_domains }}" - name: Скачивание конфигураций обновления renewal ansible.builtin.fetch: src: "/etc/letsencrypt/renewal/{{ item }}.conf" dest: "{{ local_tmp_dir }}/renewal/{{ item }}.conf" flat: true loop: "{{ dynamic_domains }}" - name: Скачивание папок live (копируем исходное содержимое) ansible.builtin.fetch: src: "/etc/letsencrypt/live/{{ item.0 }}/{{ item.1 }}.pem" dest: "{{ local_tmp_dir }}/live/{{ item.0 }}/{{ item.1 }}.pem" flat: true loop: "{{ dynamic_domains | product(['cert', 'chain', 'fullchain', 'privkey']) | list }}" delegate_to: "{{ source_host_inventory_name }}" run_once: true - name: БЛОК НАКАТЫВАНИЯ И АДАПТИВНОГО ПЕРЕЗАПУСКА (Выполняется на удаленных хостах) block: - name: Сбор информации о запущенных сервисах на целевом хосте ansible.builtin.service_facts: - name: Автоматическое создание всей иерархии папок Let's Encrypt на целевом хосте ansible.builtin.file: path: "/etc/letsencrypt/{{ item }}" state: directory owner: root group: root mode: '0755' loop: [ 'archive', 'live', 'renewal' ] - name: Копирование содержимого папки archive на целевой сервер ansible.builtin.copy: src: "{{ local_tmp_dir }}/archive/{{ hostvars[source_host_inventory_name]['inventory_hostname'] }}/etc/letsencrypt/archive/{{ item }}/" dest: "/etc/letsencrypt/archive/{{ item }}/" owner: root group: root mode: '0600' loop: "{{ hostvars[source_host_inventory_name]['dynamic_domains'] }}" - name: Копирование конфигураций renewal на целевой сервер ansible.builtin.copy: src: "{{ local_tmp_dir }}/renewal/{{ item }}.conf" dest: "/etc/letsencrypt/renewal/{{ item }}.conf" owner: root group: root mode: '0644' loop: "{{ hostvars[source_host_inventory_name]['dynamic_domains'] }}" - name: Создание папок доменов внутри директории live ansible.builtin.file: path: "/etc/letsencrypt/live/{{ item }}" state: directory owner: root group: root mode: '0700' loop: "{{ hostvars[source_host_inventory_name]['dynamic_domains'] }}" - name: Нативная сборка относительных символических ссылок в папке live ansible.builtin.file: src: "../../archive/{{ item.0 }}/{{ item.1 }}1.pem" dest: "/etc/letsencrypt/live/{{ item.0 }}/{{ item.1 }}.pem" state: link force: true loop: "{{ hostvars[source_host_inventory_name]['dynamic_domains'] | product(['cert', 'chain', 'fullchain', 'privkey']) | list }}" - name: Автоматический перезапуск активных веб- и почтовых служб (только если они запущены) ansible.builtin.systemd: name: "{{ item }}" state: restarted loop: - dovecot - nginx - apache2 - httpd when: - ansible_facts.services[item + '.service'] is defined - ansible_facts.services[item + '.service'].state == 'running'