Community.general.bitwarden on AWX

Hello!
I need to export an session key i get after logging into an application, but i always hace an missing “” or an additinally \ somehow added to the " "
The session keys are random after each login and i cannot set a fixed one:
I have to export this in the end:
export BW_SESSION="V5V7QpSo6LkdsCWhuK37Mm/r9pnFvnAexLNg7ZrQ=="
My playbook:

- name: Retrieve Bitwarden password test
  hosts: www43
  gather_facts: true
  become: true
  tasks:
    - name: Login to Bitwarden Vault
      ansible.builtin.command:
        cmd: "bw login {{ bw_usr }} '{{ bw_pw }}' --raw"
      ignore_errors: yes
      no_log: true

    - name: Unlock Bitwarden vault
      ansible.builtin.command:
        cmd: "bw unlock '{{ bw_pw }}'"
      register: bw_unlock
      ignore_errors: no  # Ensure the play stops if unlocking fails

    - name: Check if Bitwarden unlock was successful
      ansible.builtin.debug:
        msg: "Bitwarden unlocked successfully."
      when: bw_unlock.rc == 0  # Show message only if unlock succeeded

    - name: Extract BW_SESSION from unlock output
      ansible.builtin.set_fact:
        bw_session: "{{ (bw_unlock.stdout | regex_search('BW_SESSION=\"([^\"]+)', '\\1')) | first }}"

    # Updated task: Define bwsessionfull without extra escaping
    - name: Set bwsessionfull variable with BW_SESSION string
      ansible.builtin.set_fact:
        bwsessionfull: "BW_SESSION={{ bw_session | quote }}"

    - name: Export BW_SESSION environment variable
      ansible.builtin.shell: export BW_SESSION="{{ bw_session }}"
      environment:
        BW_SESSION: "{{ bw_session }}"

    - name: Debug output of BW_SESSION
      ansible.builtin.debug:
        msg: "The exported BW_SESSION variable is: {{ bw_session }}"

    - name: Debug output of bwsessionfull
      ansible.builtin.debug:
        msg: "The bwsessionfull variable is: {{ bwsessionfull }}"

    - name: "Get 'password' from all Bitwarden records named 'nagivis'"
      ansible.builtin.debug:
        msg: >-
          {{ lookup('community.general.bitwarden', 'nagivis', field='password') }}


Thank you!

I don’t think your problem is quotes. I think your problem is that you are running your lookup without giving it your bw_session. So the following will fail:

I would suggest you try both of these (not at the same time necessarily).
This first attempt passes the bw_session explicitly.

    - name: "Get 'password' from all Bitwarden records named 'nagivis'"
      ansible.builtin.debug:
        msg: >-
          {{ lookup('community.general.bitwarden',
                    'nagivis',
                    field='password',
                    bw_session=bw_session) }}

This one passes the bw_session as an environment variable.

    - name: "Get 'password' from all Bitwarden records named 'nagivis'"
      ansible.builtin.debug:
        msg: >-
          {{ lookup('community.general.bitwarden',
                    'nagivis',
                    field='password') }}
      environment:
        BW_SESSION: "{{ bw_session }}"

Those are my best guesses after a brief reading of the c.g.bitwarden lookup docs.

Thank you!

I thought i had to export the bw_session to the Host for this, but there is aleready an example using the bw_session var:

- name: "Get 'password' from all Bitwarden records named 'a_test', using given session key"
  ansible.builtin.debug:
    msg: >-
      {{ lookup('community.general.bitwarden', 'a_test', field='password', bw_session='bXZ9B5TXi6...') }}

This time its for me a little mpr complicated to adapt this bitwarden stuff from ansible to awx.
Normal with ansible you can execute the playbook and use all this login stuff in the console while the playbook runs, but here everything is a bit diffrent.
Its better for full auto stuff, but sometinmes its a little to a lot confusing.
But i really like awx and after not one year i can at least see n ow when chatgpt tries to sell me bullshit :rofl:

@utoddl
the new playbook but in the end the same error…
grafik
should i try my luck in the ansible community general plugins git?

---
- name: Retrieve Bitwarden password test
  hosts: www43
  gather_facts: false
  become: true
  vars:
    bitwarden_record_name: "nagivis"
  tasks:

    - name: Unlock Bitwarden vault
      ansible.builtin.command:
        cmd: "bw unlock '{{ bw_pw }}'"
      register: bw_unlock_result
      ignore_errors: no  

    - name: Extract BW_SESSION from Bitwarden output
      ansible.builtin.set_fact:
        bw_session: "{{ bw_unlock_result.stdout | regex_search('BW_SESSION=\"(.*?)\"', '\\1') }}"
      when: bw_unlock_result.rc == 0

    - name: Convert BW_SESSION to string
      ansible.builtin.set_fact:
        bw_session: "{{ bw_session | string }}"
      when: bw_session is defined

    - name: Display the BW_SESSION variable
      ansible.builtin.debug:
        msg: "Extracted BW_SESSION: {{ bw_session }}"

    - name: "Get 'password' from all Bitwarden records named '{{ bitwarden_record_name }}'"
      ansible.builtin.debug:
        msg: "{{ lookup('community.general.bitwarden', bitwarden_record_name, field='password', bw_session=bw_session) }}"

Your “Extract BW_SESSION from Bitwarden output” task sets bw_session to a list. You left off the “| first” which you had in your initial post.

The next task, “Convert BW_SESSION to string” is not necessary. Remove it after fixing the prior task.

The final task had no hope of succeeding with a string representation of a list as the BW_SESSION. Fix the above issues and try again.

but im confused the debug output of the bw_unlock is correct all the time, but the playbook fails

---
- name: Retrieve Bitwarden password test
  hosts: www43
  gather_facts: false
  become: true
  vars:
    bitwarden_record_name: "nagivis"
  tasks:

    - name: Unlock Bitwarden vault
      ansible.builtin.command:
        cmd: "bw unlock '{{ bw_pw }}'"
      register: bw_unlock
      ignore_errors: no  

    - name: Extract BW_SESSION from Bitwarden output
      ansible.builtin.set_fact:
        bw_session: "{{ (bw_unlock.stdout | regex_search('BW_SESSION=\"([^\"]+)', '\\1')) | first }}"

    - name: Display the BW_SESSION variable
      ansible.builtin.debug:
        msg: "Extracted BW_SESSION: {{ bw_session }}"

    - name: "Get 'password' from all Bitwarden records named '{{ bitwarden_record_name }}'"
      ansible.builtin.debug:
        msg: "{{ lookup('community.general.bitwarden', bitwarden_record_name, field='password', bw_session=bw_session) }}"

no luck again here

Would try to help, but without job log excerpts it’s hard to spot what you’re overlooking. :person_shrugging:

Hello!
Okay - i set the log to 5:
Here is the log the full one is too big and i replaced the still temp visible bitwarden userpassword and the session keys are random and new each playbook run.

Heres just the last one: the var extraction and password lookup:

TASK [Extract BW_SESSION from Bitwarden output] ********************************
task path: /runner/project/zzz_testplaybooks_and_archive/bwtest/bw_envtest copy.yaml:16
ok: [www43] => {
    "ansible_facts": {
        "bw_session": "VYF7bNR9aLc2fqxeJE6PAM7gA/EnOUOIyTsL7OQkwSD1FHKgTmkHoFYE4+zP/lAzMaVHDmFsxcHs9wz44Ns3og=="
    },
    "changed": false
}

TASK [Display the BW_SESSION variable] *****************************************
task path: /runner/project/zzz_testplaybooks_and_archive/bwtest/bw_envtest copy.yaml:20
ok: [www43] => {
    "msg": "Extracted BW_SESSION: VYF7bNR9aLc2fqxeJE6PAM7gA/EnOUOIyTsL7OQkwSD1FHKgTmkHoFYE4+zP/lAzMaVHDmFsxcHs9wz44Ns3og=="
}

TASK [Get 'password' from all Bitwarden records named 'nagivis'] ***************
task path: /runner/project/zzz_testplaybooks_and_archive/bwtest/bw_envtest copy.yaml:24
Loading collection community.general from /usr/share/ansible/collections/ansible_collections/community/general
exception during Jinja2 execution: Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ansible/template/__init__.py", line 879, in _lookup
    ran = instance.run(loop_terms, variables=self._available_variables, **kwargs)
  File "/usr/share/ansible/collections/ansible_collections/community/general/plugins/lookup/bitwarden.py", line 226, in run
    raise AnsibleError("Bitwarden Vault locked. Run 'bw unlock'.")
ansible.errors.AnsibleError: Bitwarden Vault locked. Run 'bw unlock'.
fatal: [www43]: FAILED! => {
    "msg": "An unhandled exception occurred while running the lookup plugin 'community.general.bitwarden'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Bitwarden Vault locked. Run 'bw unlock'.. Bitwarden Vault locked. Run 'bw unlock'."
}

Thank you for your patience!!!

Bitwarden - the bw binary - must set something locally when you login, in addition to giving you a sessions token. I’m guessing it puts something in ~/.local/bw or ~/.config/bw or something along those lines. But regardless, it wouldn’t be possible to take a BW_SESSION string to another host and do Bitwardeny things with it. And that makes sense.

All of your tasks run on the Ansible controller - set_fact, debug, and lookup(). The one exception is the command task. That “bw unlock {{ bw_pw }}” runs on the target machine.

Again, I don’t have and have never run Bitwarden, but I’m going to suggest you add a

delegate_to: localhost

to your first task so it, too, runs on the Ansible controller, and see if it doesn’t magically start working. If it does, great, and I’m sorry I didn’t think about this sooner. Do let us know either way. I’d really like to understand what’s going on here.

Its a little confusing too starting now:
This bitwarden stuff is a cli installed anywhere there you can configure the server where the cli is going to.
THen you have 2 steps of “logging in” bw login(Userlogin) bwunlock(now you can eccess the stored passwords)
when you login or unlock you get a message with this session token you can export to the local env so you dont have to enter the password for each search request for passwords etc.

The first time i started the one with the bw login task i got the same message i get when i login a second time “the user mytestusr@mydom.com is aready logged in” the user was logged in after i logged manually in locally in the server.
Now i have to go via lcalhost gets me a little confused :thinking:

I added the bw cli in the env but itseems to not be aviable at localhost:
PB

---
- name: Retrieve Bitwarden password test
  hosts: all
  gather_facts: false
  become: true
  vars:
    bitwarden_record_name: "nagivis"
  tasks:

    - name: Unlock Bitwarden vault
      ansible.builtin.command:
        cmd: "/usr/local/bin/bw unlock '{{ bw_pw }}'"
      register: bw_unlock
      ignore_errors: no  
      delegate_to: localhost

    - name: Extract BW_SESSION from Bitwarden output
      ansible.builtin.set_fact:
        bw_session: "{{ (bw_unlock.stdout | regex_search('BW_SESSION=\"([^\"]+)', '\\1')) | first }}"

    - name: Display the BW_SESSION variable
      ansible.builtin.debug:
        msg: "Extracted BW_SESSION: {{ bw_session }}"

    - name: "Get 'password' from all Bitwarden records named '{{ bitwarden_record_name }}'"
      ansible.builtin.debug:
        msg: "{{ lookup('community.general.bitwarden', bitwarden_record_name, field='password', bw_session=bw_session) }}"

Log:

PLAY [Retrieve Bitwarden password test] ****************************************

TASK [Unlock Bitwarden vault] **************************************************
task path: /runner/project/zzz_testplaybooks_and_archive/bwtest/bw_envtest copy 2.yaml:10
fatal: [www43 -> localhost]: FAILED! => {"changed": false, "module_stderr": "/bin/sh: line 1: sudo: command not found\\n", "module_stdout": "", "msg": "MODULE FAILURE\\nSee stdout/stderr for the exact error", "rc": 127}

And this is from my EE which gad no errors generating:


additional_build_steps:
  prepend_base:
    - RUN yum -y install epel-release libicu rsync  # Installs extra packages needed for the environment.
    - RUN yum -y remove vim-minimal  # Removes minimal Vim package to avoid conflicts or to save space.
    - RUN yum -y update && yum -y upgrade && yum clean all && rm -rf /var/cache/yum  # Updates system packages and cleans up cache.
    - RUN yum -y install python3-pip sshpass  # Installs pip for Python 3 and sshpass for SSH automation.
    - RUN /usr/bin/python3 -m pip install --upgrade pip --no-cache-dir  # Upgrades pip without caching to save space.

  append_base:
    - RUN yum -y install openssh-clients wget tar gzip git unzip # Installs tools for SSH, file downloads, and compression.
    - RUN yum -y remove python3-cryptography  # Removes an older version of cryptography to avoid version conflicts.
    - RUN wget -O /tmp/bw_cli.zip https://github.com/bitwarden/cli/releases/download/v1.22.1/bw-linux-1.22.1.zip
    - RUN unzip /tmp/bw_cli.zip -d /usr/local/bin/
    - RUN chmod +x /usr/local/bin/bw 

I dont get why it failed - i created an playbook which shows the content of /usr/local/bin/ and bw is there and i can use the command /usr/local/bin/bw -h and it doesnt fail and youre right with the config of bw its at .config/Bitwarden CLI/data.json
at my server its at /root/.config/Bitwarden CLI/data.json and playbook localhost: runner/.config/Bitwarden CLI/data.json
I got the message after the first run with just unlock task i got a message not logged in so i added the userlogin task.
Here i got username/pw wrong error.
Then i added the configure theserverfor the cli and changed the userpw to one without special characters, but still get username/pw error:
Playbook:

---
- name: Retrieve Bitwarden password test
  hosts: all
  gather_facts: false
  #become: true
  vars:
    bitwarden_record_name: "nagivis"
  tasks:

    - name: Configure BW CLI
      ansible.builtin.command:
        cmd: "bw config server https://bw.gruen.net/"
      #ignore_errors: yes
      #no_log: true
      delegate_to: localhost

    - name: Get contents of /runner/
      ansible.builtin.command:
        cmd: ls -a /root/.config/Bitwarden\ CLI/
      register: bin_contents

    - name: Display contents of /runner/
      debug:
        msg: "{{ bin_contents.stdout_lines }}"

    - name: Show contents of Bitwarden CLI data.json
      ansible.builtin.command:
        cmd: cat "/root/.config/Bitwarden CLI/data.json"
      register: bitwarden_data_json
      delegate_to: localhost
      ignore_errors: yes

    - name: Display data.json contents
      ansible.builtin.debug:
        msg: "{{ bitwarden_data_json.stdout }}"
      ignore_errors: yes

    - name: Login to Bitwarden Vault
      ansible.builtin.command:
        cmd: "bw login {{ bw_usr }} {{ bw_pw }}"
      #ignore_errors: yes
      #no_log: true
      delegate_to: localhost

    - name: Unlock Bitwarden vault
      ansible.builtin.command:
        cmd: /usr/local/bin/bw unlock {{ bw_pw }}
      register: bw_unlock
      ignore_errors: no  
      delegate_to: localhost

    - name: Extract BW_SESSION from Bitwarden output
      ansible.builtin.set_fact:
        bw_session: "{{ (bw_unlock.stdout | regex_search('BW_SESSION=\"([^\"]+)', '\\1')) | first }}"

    - name: Display the BW_SESSION variable
      ansible.builtin.debug:
        msg: "Extracted BW_SESSION: {{ bw_session }}"

    - name: "Get 'password' from all Bitwarden records named '{{ bitwarden_record_name }}'"
      ansible.builtin.debug:
        msg: "{{ lookup('community.general.bitwarden', bitwarden_record_name, field='password', bw_session=bw_session) }}"

Log

Without “become: true”, I wouldn’t expect to be able to see “/root/.config/Bitwarden CLI”. Nor would I have guessed that (non-)quoting and backslash escaped space to make it to the command. Yet there it is in your output. So I’m wrong twice and haven’t made it passed the second task.

The first task though clearly says it’s creating “/runner/.config/Bitwarden CLI/data.json” rather than “/root/.config/…”, and at least in the version of the playbook you posted, there is no active “become: true”, so I’d still think looking under “/root” isn’t helpful. In fact, if you change that “ls -a” to “ls -al” in your second task I expect you’ll find it’s old, left over from prior runs. (Unless your instance cycles in the mean time, in which case it won’t exist.)

But I’m with you, in that I don’t get what’s going on overall. I’d really like to, but probably not to the extent that I’m going to stand up a Bitwarden instance just to have something to bang my head against.

If anybody else out there has Ansible <=> Bitwarden experience or insight, please speak up!

The runner folder is confusing as hell to me too - the log states its saving in runner. but the folder is not eixistend …:

    - name: Get contents of /
      ansible.builtin.command:
        cmd: ls -a /
      register: bin_contents

    - name: Display contents of /
      debug:
        msg: "{{ bin_contents.stdout_lines }}"

log:

changed: [www43] => {
    "changed": true,
    "cmd": [
        "ls",
        "-a",
        "/"
    ],
    "delta": "0:00:00.006836",
    "end": "2024-11-12 14:14:31.621001",
    "invocation": {
        "module_args": {
            "_raw_params": "ls -a /",
            "_uses_shell": false,
            "argv": null,
            "chdir": null,
            "creates": null,
            "executable": null,
            "removes": null,
            "stdin": null,
            "stdin_add_newline": true,
            "strip_empty_ends": true
        }
    },
    "msg": "",
    "rc": 0,
    "start": "2024-11-12 14:14:31.614165",
    "stderr": "",
    "stderr_lines": [],
    "stdout": ".\\n..\\nbacula-fd.conf.ucftmp-0r4rZBEtYC\\nbin\\nboot\\ndev\\netc\\nhome\\ninitrd.img\\ninitrd.img.old\\nlib\\nlib32\\nlib64\\nlibx32\\nlost+found\\nmedia\\nmnt\\nopt\\nproc\\nroot\\nrun\\nsbin\\nsnap\\nsrv\\nswap.img\\nsys\\ntmp\\nusr\\nvar\\nvmlinuz\\nvmlinuz.old",
    "stdout_lines": [
        ".",
        "..",
        "bacula-fd.conf.ucftmp-0r4rZBEtYC",
        "bin",
        "boot",
        "dev",
        "etc",
        "home",
        "initrd.img",
        "initrd.img.old",
        "lib",
        "lib32",
        "lib64",
        "libx32",
        "lost+found",
        "media",
        "mnt",
        "opt",
        "proc",
        "root",
        "run",
        "sbin",
        "snap",
        "srv",
        "swap.img",
        "sys",
        "tmp",
        "usr",
        "var",
        "vmlinuz",
        "vmlinuz.old"
    ]
}

TASK [Display contents of /] ***************************************************
task path: /runner/project/zzz_testplaybooks_and_archive/bwtest/bw_envtest copy 2.yaml:31
ok: [www43] => {
    "msg": [
        ".",
        "..",
        "bacula-fd.conf.ucftmp-0r4rZBEtYC",
        "bin",
        "boot",
        "dev",
        "etc",
        "home",
        "initrd.img",
        "initrd.img.old",
        "lib",
        "lib32",
        "lib64",
        "libx32",
        "lost+found",
        "media",
        "mnt",
        "opt",
        "proc",
        "root",
        "run",
        "sbin",
        "snap",
        "srv",
        "swap.img",
        "sys",
        "tmp",
        "usr",
        "var",
        "vmlinuz",
        "vmlinuz.old"
    ]
}

And i ested with become active and the playbok failes in the localhost tasks - i think i need to add sudo to my EE(?) but i can do anything now too.

I think what you’re seeing is the difference between the Ansible controller control plane execution environment’s view of the world vs the playbook’s execution environment’s view of the world. I spent an enjoyable but mostly unenlightening evening researching how things look from these various containers’ perspectives, but have very few solid answers to show for it. I’m out of ideas and don’t want to mislead you farther.

The plugin assumes you’ve already logged in and unlocked your vault by the time the lookup is executed. It’s unfortunate it doesn’t include a top-to-bottom example showing how that looks when running under EEs like AWX.

If you ever get it figured out, please follow-up; I’m still curious, but I’m also spent.
Cheers

Todd

Thank you for your help!!
I think i ast in the git of the community module if someone has an exqaample plabook for awx
I would really keep ising awx because of the workflow possibilities,but its hard when there is nothing much documented, what you have to look out for when using awx instead. Ot coud i het an ticket from red hat where they could solve this issue?

You’re asking in the right place already, right here on the Ansible Forum, but nobody’s going to guess that we’re discussing Bitwarden with a title like “How to generate/use vars including doublequotes”. I’m going to change the title to “community.general.bitwarden on AWX” in hopes that someone who knows the answer might see it.

[Edit: I also added a “bitwarden” tag.]

1 Like