15

I am trying to setup a playbook which will run the command to check status of the service installed in the target machine. The command will only work only if the .env file executed. The command to execute the .env file is .<space>./.env_file_name and the file contains list of environment variables like export JAVA_HOME=/optware/java/jdk/1.2.

I tried to execute the environment file before running the command with the below playbook, but it is not working.

- hosts: name
  tasks: 
    - name: `execute env file`
      command: . ./.env_file_name
      register: result

Is there any playbook to run the executable environment file to set the environments present on the target machine and then run our command??

Jimbo
  • 25,790
  • 15
  • 86
  • 131
Newbee2
  • 179
  • 1
  • 1
  • 6
  • Usually the .env or any other files are sourced using `source` keyword in shell. try that. `command: source ` – error404 Feb 13 '20 at 15:21
  • 1
    Do you expect to use the env vars in other tasks? Because it's not possible this way. If you want to use them in the same task, then you should use the [`shell` module](https://docs.ansible.com/ansible/latest/modules/shell_module.html). Otherwise, to use environment variables in tasks execution, you need to use [`environment` keyword](https://docs.ansible.com/ansible/latest/user_guide/playbooks_environment.html) – zigarn Feb 13 '20 at 16:35
  • @Zigarn I need to use the env variables in other task. do you have any example playbook to run .env file and to use the env variables in other tasks – Newbee2 Feb 14 '20 at 07:37
  • Note, the `.` and `source` command are virtually the same thing: https://stackoverflow.com/a/20097303/320399 – blong Feb 02 '21 at 13:01
  • Related: https://stackoverflow.com/a/42255589/320399 – blong Feb 02 '21 at 13:51

1 Answers1

16

First, the . ./.env_file_name syntax is a shell syntax and cannot work with the command module, you need to use the shell module.

Secondly, the shell environment context is reset at every task as each is an ssh command round-trip (so a new shell session), and loading the environment variables in one task will not not make them available for next tasks.

Depending on your context, you have some options:

1. Inventory environment variables

The best option is to have the environment at your inventory side in a variable with different value for each group/host through group_vars/host_vars, then to use it for the environment keyword

# host_vars/my_host.yml
---
env_vars:
  VAR1: key1
  VAR2: key2
- hosts: my_host
  tasks: 
    - name: Display environment variables
      command: env
      environment: "{{ env_vars }}"

Pros:

  • full ansible solution
  • will work for environment of every module

Cons:

  • need to know the environment variables at ansible side

2. Loading environment variables for every tasks

If your tasks are all shell/command (which I don't advise, as it's better to use appropriate ansible module whenever possible), you can simply load the env file every time with shell module

- hosts: my_host
  tasks: 
    - name: Display environment variables
      shell: |
        . ./.env_file_name && env

    - name: Do another action
      shell: |
        . ./.env_file_name && do_something_else

Pros:

  • no need to know the environment variables at ansible side

Cons:

  • limited to tasks with shell module

3. Load environment variables from env_file into ansible fact

This option is to parse the env file once and for all and load it in an ansible fact to use with the environment keyword.

- hosts: my_host
  tasks: 
    - name: Get env file content
      slurp:
        src: ./.env_file_name
      register: env_file_content

    - name: Parse environment
      set_fact:
        env_vars: "{{ ('{' + (env_file_content.content | b64decode).split('\n') | select | map('regex_replace', '([^=]*)=(.*)', '\"\\1\": \"\\2\"') | join(',') + '}') | from_json }}"

    - name: Display environment variables
      command: env
      environment: "{{ env_vars }}"

Or, if the env file need to be executed instead of directly parsed:

- hosts: my_host
  tasks: 
    - name: Get env file content
      shell: . ./.env_file_name && env
      register: env_file_result

    - name: Parse environment
      set_fact:
        env_vars: "{{ ('{' + env_file_result.stdout_lines | map('regex_replace', '([^=]*)=(.*)', '\"\\1\": \"\\2\"') | join(',') + '}') | from_json }}"

    - name: Display environment variables
      command: env
      environment: "{{ env_vars }}"

Pros:

  • will work for environment of every module
  • no need to know the environment variables at ansible side

Cons:

  • could fail on bad formatting of file
LavaHot
  • 382
  • 2
  • 20
zigarn
  • 10,892
  • 2
  • 31
  • 45
  • Thanks for this! Although the big problem I think is the regex, because it doesn't support quotes around values, and doesn't support comments either (`#`). – Jimbo Apr 24 '20 at 20:45
  • Right! It can be done by adding `| map('regex_replace', '#.*', '') | map('trim') | reject()` before the `| map('regex_replace'` to remove the comments and change the regex to `'([^=]*)=['"]?(.*)['"]?'` to handle simple cases of quoted values. – zigarn Apr 28 '20 at 17:12
  • So I followed your instructions and used: `{{ ('{' + (env_file_content.content | b64decode).split('\n') | select | map('regex_replace', '#.*', '') | map('trim') | reject() | map('regex_replace', '([^=]*)=['\"]?(.*)['\"]?', '\"\\1\": \"\\2\"') | join(',') + '}') | from_json }}` but unfortunately this didn't work. Although I feel this might be going more towards a separate question, do you know what I'm missing? – Jimbo Apr 29 '20 at 18:26
  • I think I was wrong (didn't test...): use `select` instead of `reject` – zigarn Apr 30 '20 at 11:02
  • I couldn't get this working and managed to find an answer that does, and it works with comments in the `.env` file and new lines too: https://stackoverflow.com/a/42255589/736809 – Jimbo Mar 01 '23 at 20:11
  • What works for me was `env_vars: "{{ (env_file_content.content | b64decode).split('\n') | map('regex_replace', '^#.*', '') | select | map('regex_replace', '([^=]*)=(.*)', '\\1: \\2') | join('\n') | from_yaml }}"` - This removes commented lines from env file, Now the set fact variable (`env_vars`) can be used to fetch the env keys like `env_vars["Key1"]` .. – Akshay Chandran Mar 08 '23 at 10:58