--- - name: Deploy Docker Service Configurations (Ownership-aware & verbose) hosts: - aranea - lunafreya - rikku - rinoa - ultima gather_facts: false become: true become_method: sudo strategy: mitogen_free vars: default_owner: "1000" default_group: "1000" default_mode: "0644" pre_tasks: - name: Get remote user's UID and GID ansible.builtin.command: "id -u && id -g" register: remote_user_info changed_when: false ignore_errors: true - name: Set remote user's UID and GID facts ansible.builtin.set_fact: remote_uid: "{{ remote_user_info.stdout_lines[0] | default(default_owner) | int }}" remote_gid: "{{ remote_user_info.stdout_lines[1] | default(default_group) | int }}" - name: Initialize file metadata with become info ansible.builtin.set_fact: file_metadata_with_become: {} - name: Annotate file metadata with become requirement ansible.builtin.set_fact: file_metadata_with_become: >- {{ file_metadata_with_become | combine({ item.key: item.value | combine({ 'owner': (item.value.owner | default(default_owner) | string), 'group': (item.value.group | default(default_group) | string), 'mode': (item.value.mode | default(default_mode) | string), 'become': ( ((item.value.owner | default(default_owner) | int) != remote_uid) or ((item.value.group | default(default_group) | int) != remote_gid) ) }) }) }} loop: "{{ (file_metadata | default({})) | dict2items }}" loop_control: label: "{{ item.key }}" - name: Find all files for this host (control node) ansible.builtin.find: paths: "{{ template_base_path }}/{{ inventory_hostname }}" recurse: true register: host_files delegate_to: localhost run_once: true changed_when: false - name: Build list of unique destination directories ansible.builtin.set_fact: dest_dirs: >- {{ host_files.files | map(attribute='path') | map('relpath', template_base_path ~ '/' ~ inventory_hostname) | map('dirname') | map('regex_replace', '^(.*)$', appdata_base_path ~ '/\1') | unique | list }} changed_when: false - name: Initialize deploy files list ansible.builtin.set_fact: deploy_files: [] - name: Build flat file deployment spec list ansible.builtin.set_fact: deploy_files: "{{ deploy_files + [ { 'src': item.path, 'dest': appdata_base_path ~ '/' ~ (item.path | relpath(template_base_path ~ '/' ~ inventory_hostname) | regex_replace('\\.j2$', '')), 'owner': (file_metadata_with_become.get(item.path | relpath(template_base_path ~ '/' ~ inventory_hostname), {}).get('owner', default_owner)) | string, 'group': (file_metadata_with_become.get(item.path | relpath(template_base_path ~ '/' ~ inventory_hostname), {}).get('group', default_group)) | string, 'mode': (file_metadata_with_become.get(item.path | relpath(template_base_path ~ '/' ~ inventory_hostname), {}).get('mode', default_mode)) | string, 'become': (file_metadata_with_become.get(item.path | relpath(template_base_path ~ '/' ~ inventory_hostname), {}).get('become', false)) | bool, 'is_template': item.path.endswith('.j2') } ] }}" loop: "{{ host_files.files }}" loop_control: label: "{{ item.path }}" - name: Print concise become list ansible.builtin.debug: msg: >- Files requiring become: {% set found = false %} {% for f in deploy_files if f.become %} {% set found = true %} - {{ f.dest }} -> owner:{{ f.owner }}:{{ f.group }} mode:{{ f.mode }} {% endfor %} {% if not found %} (none) {% endif %} run_once: true tasks: - name: Ensure destination directories exist ansible.builtin.file: path: "{{ item }}" state: directory mode: "0755" loop: "{{ dest_dirs }}" loop_control: label: "{{ item }}" - name: Deploy template files ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: "{{ item.owner }}" group: "{{ item.group }}" mode: "{{ item.mode }}" loop: "{{ deploy_files | selectattr('is_template') | list }}" loop_control: label: "{{ item.src }}" become: "{{ item.become }}" - name: Deploy static files ansible.builtin.copy: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: "{{ item.owner }}" group: "{{ item.group }}" mode: "{{ item.mode }}" remote_src: false loop: "{{ deploy_files | rejectattr('is_template') | list }}" loop_control: label: "{{ item.src }}" become: "{{ item.become }}"