from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = """ name: onepassword author: Menno version_added: "1.0" short_description: fetch secrets from 1Password description: - Uses the 1Password CLI to fetch secrets from 1Password using the op read command options: _terms: description: 1Password reference string (op://vault/item/field) required: true """ EXAMPLES = """ - name: fetch password using 1Password reference debug: msg: "{{ lookup('onepassword', 'op://vault/item/password') }}" - name: fetch username from item debug: msg: "{{ lookup('onepassword', 'op://vault/item/username') }}" - name: fetch custom field debug: msg: "{{ lookup('onepassword', 'op://vault/item/custom_field') }}" """ RETURN = """ _raw: description: field data requested """ from ansible.errors import AnsibleError from ansible.plugins.lookup import LookupBase from ansible.utils.display import Display import subprocess display = Display() class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): result = [] for term in terms: if not term.startswith('op://'): raise AnsibleError(f"1Password reference must start with 'op://', got: {term}") cmd = ['op', 'read', term] display.vvv(f"Executing command: {' '.join(cmd)}") try: process = subprocess.run( cmd, capture_output=True, text=True, check=True ) output = process.stdout.strip() display.vvv(f"1Password output for '{term}': '{output}'") if not output: display.warning(f"1Password returned empty output for '{term}'") result.append(output) except subprocess.CalledProcessError as e: error_msg = e.stderr.strip() display.warning(f"Error executing 1Password CLI: {error_msg}") display.warning(f"Command used: {' '.join(cmd)}") if "not found" in error_msg: raise AnsibleError(f"Secret referenced by '{term}' not found in 1Password") raise AnsibleError(f"Error fetching from 1Password: {error_msg}") return result