Skip to content

linuxfabrik.lfops.bitwarden_item (lookup)

Fetch (or create) a Bitwarden login item

Synopsis

  • Looks up a password item in Bitwarden by name (and optional username, folder, collection and organization), or directly by Bitwarden item ID.
  • If the lookup is by name and no matching item is found, a new login item is created on the fly. This makes the plugin idempotent for automation. The first run creates the secret, every subsequent run returns the same item.
  • If the lookup is by id and the item is not in the local cache, the plugin falls back to a single API call. If the ID still does not resolve, the plugin fails - IDs are never auto-created.
  • If a name-based search returns more than one match, the plugin fails because it cannot decide which item to use.
  • On success, the plugin returns the full Bitwarden item object. username and password are additionally lifted to the top level so they can be addressed without going through the login sub-dictionary.
  • When name is omitted, a title is generated automatically as hostname - purpose (e.g. dbserver - MariaDB) or just hostname when no purpose is given.
  • Generated passwords use Python's secrets module (cryptographically strong RNG), not the Bitwarden generator. This lifts the 128-character limit and allows arbitrary character sets, including hex.
  • Items are read from a local on-disk cache backed by bw serve. The cache is synced once per Ansible run, since listing all items takes bw serve up to half a minute; where the run cannot be identified (no /proc on the controller), at most every 60 seconds. An item missing from the cache is looked up again after a fresh sync before it is created, so an item created elsewhere during a long run is not created twice.
  • A failed sync is tried again after 10, 30 and 60 seconds before the lookup fails.
  • Lookups on the same controller run one at a time, so hosts that are processed in parallel and need the same missing item create it only once.
  • Right after a sync, bw serve can report an empty vault for a few seconds (https://github.com/bitwarden/clients/issues/23283). The plugin then asks again for about ten seconds, counts a vault that stays empty as a failed sync, and fails after the last sync attempt rather than treat every item as missing. A vault that really is empty needs one item created by hand first.

Available since LFOps 1.0.0.

Requirements

  • Bitwarden CLI bw version v2022.9.0 or newer. See https://bitwarden.com/help/article/cli/ for installation instructions.
  • You must be logged in and unlocked (bw login followed by bw unlock), and have the local API running, e.g. bw serve --hostname 127.0.0.1 --port 8087. The plugin connects to 127.0.0.1:8087 by default.
  • bw serve takes its session from the environment it is started in and keeps it for its lifetime. Export BW_SESSION before starting it. Unlocking the vault afterwards only affects the bw CLI in your shell, so bw status can report unlocked while the API the plugin talks to is still locked.

Optional Parameters

collection_id

  • Bitwarden collection ID the item belongs to. Used both as a search filter and, if the item has to be created, as the target collection.
  • Type: String.

create

  • Whether an item that does not exist yet may be created. Set this to false for runs in which no new secrets must be minted, for example when re-running a playbook against a host whose credentials are known to exist already. A name-based lookup that finds no item then fails instead of creating one. Unlike the per-term settings above, this is a plugin-wide option and is therefore set via its environment variable or in ansible.cfg, not inside the lookup term.
  • Type: Bool.
  • Default: true

folder_id

  • Bitwarden folder ID the item belongs to. Used both as a search filter and, if the item has to be created, as the target folder.
  • Type: String.

hostname

  • Hostname the password belongs to. Used to generate the item name when name is not given.
  • Type: String.

id

  • Look up an existing item by its Bitwarden item ID. When set, all other search filters are ignored. The plugin fails if the ID does not exist (no auto-creation).
  • Type: String.

name

  • Explicit name/title of the item. When set, disables automatic name generation from hostname and purpose.
  • Type: String.

notes

  • Notes to set when the item is created. Bitwarden limits notes to 10000 characters. Existing items are not modified by this lookup.
  • Type: String.
  • Default: Generated by Ansible.

organization_id

  • Bitwarden organization ID the item belongs to. Used both as a search filter and, if the item has to be created, as the target organization.
  • Type: String.

password_choice

  • Character set used for generating new passwords. To produce a hex password, set this to '0123456789abcdef' and ensure password_length is even. Existing passwords are never regenerated.
  • Type: String.
  • Default: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

password_length

  • Length of the password generated when the item does not yet exist. Must be positive. When generating hex passwords (see password_choice), it must also be even, otherwise the lookup fails.
  • Type: Number.
  • Default: 60

purpose

  • What the password is for (e.g. MariaDB, Rocky). Combined with hostname to build the item name when name is not given.
  • Type: String.

uris

  • List of URIs to attach when the item is created. Has no effect on already existing items.
  • Type: List.

username

  • Username for the login item. Used both as a search filter and, if the item has to be created, as the login.username value.
  • Type: String.

Examples

- name: 'The normal way using this lookup plugin. Search for the Bitwarden item using hostname, purpose and username. If not found, creates a new item called `appsrv01 - MariaDB`. Returns the password item, including a `username` and a `password` subkey.'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'hostname': 'appsrv01',
          'purpose': 'MariaDB',
          'username': 'mariadb-monitoring',
        },
      ) }}"

- name: 'If only the password is required, use the following lookup.'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'hostname': 'appsrv01',
          'purpose': 'MariaDB',
          'username': 'mariadb-monitoring',
        },
      )['password'] }}"

- name: 'Lookup by name. If not found, creates the item `appsrv01 - MariaDB`.'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'hostname': 'appsrv01',
          'purpose': 'MariaDB',
        },
      ) }}"

- name: 'Lookup by ID'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'id': '9b527543-8335-41fb-b83f-8c35f4bd86e7',
        },
      ) }}"

- name: 'Lookup (all parameters), and - if needed - create a hex-based password (256 chars length = 128 bit hex)'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'hostname': 'appsrv74',
          'purpose': 'MariaDB',
          'username': 'mariadb-admin',
          'organization_id': '3334651d-8dd8-4221-bdb6-e4f8d695e7f1',
          'collection_id': '5d1d231a-4264-4b16-a0d3-324d168f210d',
          'password_length': 256,
          'password_choice': '01234567890abcdef',
          'notes': 'Please be careful.',
          'uris': [
            'https://www.example.com',
            'https://packages.example.com',
          ],
        },
      ) }}"

- name: 'Lookup multiple items at once'
  ansible.builtin.debug:
    msg: "{{ lookup('linuxfabrik.lfops.bitwarden_item',
        {
          'hostname': 'appsrv03',
          'purpose': 'MariaDB',
          'username': 'mariadb-admin',
          'organization_id': 'e4f526c4-b39d-4fa8-8645-d10373ee1f2d',
          'collection_id': '706793bc-4123-481d-a550-669f0b079ad6',
        },
        {
          'hostname': 'appsrv04',
          'purpose': 'MariaDB',
          'username': 'mariadb-admin',
          'organization_id': '832f727b-e666-4ae4-8c34-308dbbba7c99',
          'collection_id': '16ea112a-dd5f-4f68-9dfb-95a9f302a8a5',
        },
      ) }}"

Return Values

collectionIds

  • List of collection IDs in which the item is.
  • Type: List.
  • Returned: always.
  • Sample:

    yaml - 9f665810-549f-4b27-829c-93dab1e806e3

favorite

  • Whether the item is favorited or not.
  • Type: Bool.
  • Returned: always.
  • Sample: False

folderId

  • Bitwarden folder ID in which the item is.
  • Type: String.
  • Returned: always.
  • Sample: d27186d7-665a-4dde-bc4c-c0c824ca1207

id

  • Unique Bitwarden password item ID.
  • Type: String.
  • Returned: always.
  • Sample: 220c250a-302c-40d4-a5e1-fbb63a78b8d1

login

  • A Bitwarden login object (dictionary).
  • Type: Dictionary.
  • Returned: always.

name

  • Name/Title of the password item.
  • Type: String.
  • Returned: always.
  • Sample: appsrv01 - MariaDB

notes

  • Some notes about the cipher.
  • Type: String.
  • Returned: always.
  • Sample: Automatically generated by Ansible.

object

  • Type of the Bitwarden object. Always returns item.
  • Type: String.
  • Returned: always.
  • Sample: item

organizationId

  • The ID of the organization.
  • Type: String.
  • Returned: always.
  • Sample: e7d88250-5d1f-411c-b2f0-692c38da7b6f

password

  • The password. Same as under the login dict, but at a higher level for easier access.
  • Type: String.
  • Returned: always.
  • Sample: 6VuIkoKQmCl9Yyv3xpgjU6SF2ecs6k

reprompt

  • If Bitwarden should re-prompt for the master password when accessing this item.
  • Type: Number.
  • Returned: always.
  • Sample: 0

revisionDate

  • Date/Time the item was created or last modified.
  • Type: String.
  • Returned: always.
  • Sample: 2019-01-28T15:31:34.300Z

type

  • Bitwarden item type. Always 1 (login) for items handled by this plugin. Other Bitwarden types (2 secure note, 3 card, 4 identity) are filtered out and never returned.
  • Type: Number.
  • Returned: always.
  • Sample: 1

username

  • Username to which the password belongs. Same as under the login dict, but at a higher level for easier access.
  • Type: String.
  • Returned: always.
  • Sample: root

Notes

  • Lookups are evaluated by the templating engine on the controller and have no notion of check mode, so a run with --check creates a missing item for real. Set create to false to turn that into a failure.
  • Only login items (Bitwarden type 1) are supported. Cards, secure notes and identities are ignored.
  • TOTP secrets are not managed; the totp field is set to an empty string on creation.
  • URIs on existing items are not edited by this lookup; they are only set at item creation time.
  • Organization, collection and folder IDs can be copied from the URL in the Bitwarden web vault.
  • The cache file lives in $XDG_RUNTIME_DIR (falling back to /tmp) and is shared across this plugin and the bitwarden_item module within the same controller session.

Authors

  • Linuxfabrik GmbH, Zurich, Switzerland, https://www.linuxfabrik.ch