2025-03-11 20:16:36 +01:00

122 lines
3.9 KiB
Python

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
options:
item:
description: the item to fetch
required: false
field:
description: the field to fetch from the item
required: false
default: password
vault:
description: the vault to fetch from (name or ID)
required: false
reveal:
description: whether to reveal concealed fields
required: false
default: true
ref:
description: full 1Password reference (op://vault/item/field)
required: false
"""
EXAMPLES = """
- name: fetch password for an item
debug:
msg: "{{ lookup('onepassword', 'xxxx', field='password') }}"
- name: fetch password from specific vault
debug:
msg: "{{ lookup('onepassword', 'xxxx', field='password', vault='xxxx') }}"
- name: fetch using full reference
debug:
msg: "{{ lookup('onepassword', ref='op://vault/item/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):
ref = kwargs.get('ref')
if ref:
# If ref is provided, use op read command
cmd = ['op', 'read', ref]
display.vvv(f"Executing command with reference: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
return [result.stdout.strip()]
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:
return [f"Secret referenced by '{ref}' not found in 1Password"]
raise AnsibleError(f"Error fetching from 1Password: {error_msg}")
# If no ref is provided, fall back to the original behavior
if len(terms) != 1:
raise AnsibleError("onepassword lookup expects exactly one argument when not using ref parameter")
item = terms[0]
field = kwargs.get('field', 'password')
vault = kwargs.get('vault', '')
reveal = kwargs.get('reveal', True)
cmd = ['op', 'item', 'get', item, '--field', field]
# Add vault parameter if specified
if vault:
cmd.extend(['--vault', vault])
# Add reveal flag for concealed fields
if reveal and field.lower() in ['password', 'secret', 'token', 'key']:
cmd.append('--reveal')
display.vvv(f"Executing command: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
return [result.stdout.strip()]
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:
return [f"Secret '{item}' not found in 1Password"]
raise AnsibleError(f"Error fetching from 1Password: {error_msg}")