File Lookup — Reading A File
The file lookup is the simplest one to start with, and directly useful against our own lab — reading a local file’s contents straight into a value you can use in a task.
Reading A File With lookup('file', ...)
msg: "{{ lookup('file', 'notes.txt') }}"This reads notes.txt from the control node and returns its entire contents as a single string, with a single trailing newline stripped — the same convention command substitution follows.
A Realistic Example: Injecting An SSH Public Key
Recall the alice.pub and bob.pub key files generated back in the installation chapter, sitting on the control node right now. lookup('file', ...) can read one of those directly into a task, rather than manually copying its contents by hand:
- name: Write alice's public key to the remote host
ansible.builtin.copy:
content: "{{ lookup('file', 'alice.pub') }}"
dest: /tmp/alice_key_copy.pubcopy’s content: parameter, used here for the first time, writes literal text directly to the destination — an alternative to src: for exactly this situation, where the content comes from somewhere other than an existing file you’d copy wholesale.
Note
For genuinely managing a user’s authorized_keys file, ansible.posix.authorized_key is the properly purpose-built module — worth knowing it exists, even without covering it fully here. The copy version above demonstrates the lookup itself clearly, which is this chapter’s actual point.
Combining file With Other Filters
A lookup’s result is just a plain value — every string filter from the manipulating-data section applies to it directly:
msg: "{{ lookup('file', 'notes.txt') | trim }}"Useful the moment a file’s content needs cleanup — stray whitespace, for instance — before it’s actually used.
A Gotcha: Relative Paths Resolve Against The Playbook, Not Your Shell
lookup('file', 'notes.txt'), written without an absolute path, resolves relative to the playbook file’s own location — not relative to whatever directory you happen to be sitting in when you run ansible-playbook. Run the exact same playbook from a completely different working directory, and this path still resolves the same way, based on where the playbook itself lives, not your shell’s current directory.
This trips people up specifically because most command-line tools resolve relative paths against your current directory — Ansible’s lookups deliberately don’t, precisely so a playbook behaves consistently no matter where you happen to invoke it from.
Best Practices
- Use
lookup('file', ...)to inject local text content directly into a task, rather than manually pasting a file’s contents somewhere by hand. - Chain string filters onto a file lookup’s result when the raw content needs cleanup before use.
- Don’t assume a relative lookup path resolves against your shell’s current directory — it resolves against the playbook’s own location, which is a deliberate, consistent choice, not a bug.