Introduction
Reliable file automation distinguishes the controller's source, the managed copies and the artifacts collected back for inspection. Correct filenames alone do not establish content integrity, permissions or host identity.
You will deploy a banner, inspect its actual SHA-256 metadata with stat and register, generate per-host operational reports, and fetch those reports into separate controller paths. The prepared two-host SSH environment and privilege model are the same ones practiced earlier.
Distribute a File with Explicit Managed Permissions
In this step, you will copy a private controller source into a protected managed directory and choose remote ownership and permissions explicitly.
Open the Terminal and enter the project:
cd /home/labex/project
Create the reusable controller source, including its final newline:
printf 'Managed service banner\n' > site-banner.txt
chmod changes a local file's permissions. Mode 0600 gives its owner read/write access and no access to its group or others. Keep this controller source private while deliberately choosing different managed permissions:
chmod 0600 site-banner.txt
Write a distribution playbook:
cat > deploy-banner.yml <<'EOF'
- name: Distribute the managed banner
hosts: all
gather_facts: false
become: true
tasks:
- name: Prepare the managed site directory
ansible.builtin.file:
path: /srv/ansible-site
state: directory
owner: root
group: automation
mode: "0750"
- name: Copy the controller banner with explicit managed permissions
ansible.builtin.copy:
src: /home/labex/project/site-banner.txt
dest: /srv/ansible-site/banner.txt
owner: root
group: automation
mode: "0640"
EOF
The play-level become requests elevation on the managed nodes, where writing under /srv and assigning root ownership require it. It does not turn the controller's source-reading process into a root login.
The directory is owned by root, grouped to automation and mode 0750. The banner is root-owned, grouped to automation and mode 0640. That lets the ordinary automation account traverse the directory and read the banner without granting it write access. Explicit managed mode is independent of the controller source's 0600 mode.
Check and apply the playbook:
ansible-playbook deploy-banner.yml --syntax-check
ansible-playbook deploy-banner.yml
Both hosts should complete without failures. Test ordinary read access without become:
ansible all -m ansible.builtin.command -a 'cat /srv/ansible-site/banner.txt'
Each host should print Managed service banner. This demonstrates the intended remote group-read access while keeping the source and managed copies as separate files.
Register and Consume Structured File Metadata
In this step, you will inspect the real managed banner and write a per-host operational report from its metadata.
The stat module returns file information rather than copying it. checksum_algorithm: sha256 selects SHA-256 for content verification. register stores that task's result for later tasks on the same host; its stat field contains the file metadata.
Create an inspection playbook:
cat > inspect-and-collect.yml <<'EOF'
- name: Inspect the banner and prepare host reports
hosts: all
gather_facts: true
tasks:
- name: Inspect managed content and permissions
ansible.builtin.stat:
path: /srv/ansible-site/banner.txt
checksum_algorithm: sha256
register: banner_info
- name: Display the structured file metadata
ansible.builtin.debug:
var: banner_info.stat
- name: Write the per-host report from real metadata
become: true
ansible.builtin.copy:
dest: /srv/ansible-site/node-report.txt
content: |
host={{ inventory_hostname }}
distribution={{ ansible_facts.distribution }}
banner_sha256={{ banner_info.stat.checksum }}
owner: root
group: automation
mode: "0640"
EOF
The debug module displays an existing variable with var, so banner_info.stat reveals fields such as exists, mode, owner/group names, size and checksum. Displaying metadata does not create a file or prove a later operation succeeded.
The copy task consumes banner_info.stat.checksum together with inventory_hostname and the distribution fact. Its task-level become is needed to write the root-owned report. The inspection tasks themselves use ordinary read access.
Run the inspection and report generation:
ansible-playbook inspect-and-collect.yml
The metadata should identify a regular file with mode 0640, owner root and group automation. Each report has its own host name and a SHA-256 value derived from the actual remote banner.
sha256sum computes a local file's SHA-256 digest and prints the digest followed by its name. Compare the controller source digest with the metadata and report values:
sha256sum site-banner.txt
Read both remote reports:
ansible all -m ansible.builtin.command -a 'cat /srv/ansible-site/node-report.txt'
Each result should contain host=web1 or host=web2, distribution=Ubuntu and banner_sha256 followed by the matching 64-character digest. Keep observations tied to each real host, rather than replacing them with a constant debug message.
Fetch Separate Reports Back to the Controller
In this step, you will collect both managed reports without overwriting one host's result with the other.
copy normally sends a controller source to a managed destination. fetch works in the other direction: its src is on the managed node and its dest is a controller directory. With flat false, fetch includes the inventory host name and remote path beneath dest.
Append this task to the existing inspection playbook. Keep its indentation aligned with the other task entries; >> appends without replacing the earlier stat, register and report tasks:
cat >> inspect-and-collect.yml <<'EOF'
- name: Collect each managed report without flattening host paths
ansible.builtin.fetch:
src: /srv/ansible-site/node-report.txt
dest: /home/labex/project/reports/
flat: false
EOF
Run the complete inspection and collection workflow:
ansible-playbook inspect-and-collect.yml
The first fetch should create the two controller copies. It runs as the ordinary automation account on each target because the report's group permissions already allow reads.
Inspect each collected artifact locally:
cat reports/web1/srv/ansible-site/node-report.txt
cat reports/web2/srv/ansible-site/node-report.txt
The paths preserve independent host identity. Each report's host field should match its path, and both checksum fields should match the deployed source. Flattening both results to one shared filename would lose one host's artifact.
Repeat the complete workflow:
ansible-playbook inspect-and-collect.yml
With unchanged source, facts and file state, both hosts should report changed=0 and no failures/unreachable results. You now have independently collected reports backed by actual managed metadata.
Summary
You kept controller and managed file state distinct, declared remote ownership and permissions, and consumed registered stat metadata instead of merely printing a success message. Per-host reports recorded real SHA-256 integrity information, and fetch preserved both hosts' artifacts in separate controller paths with stable repeat execution.


