3

How can I add new line "\n" characters to Ansible variable mailbody

I set the below variable to ansible's mail module body attribute.

   - set_fact:
       mailbody: "{{ mailbody | default('') + 'PROFILE_NAME:' +  PROFILE_NAME ~ '\n\nSERVER_NAME:' + SERVER_NAME ~ '\n\nNODE_NAME:' +  NODE_NAME ~ '\n\n\n\n' }}"

But the body of the email has '\n\n' printed instead of new lines.

Can you please suggest what changes are needed?

Ashar
  • 2,942
  • 10
  • 58
  • 122
  • Have you try {% raw %}{% endraw %} ? check documentation https://docs.ansible.com/ansible/latest/user_guide/playbooks_advanced_syntax.html – MikZuit Jun 03 '20 at 13:41
  • I'm not using `Jinja` template ... but this `set_fact` variable is assigned to `email` module's `body` attribute. – Ashar Jun 03 '20 at 13:46
  • ansible already comes with Jinja2 temp support. Assuming you tried mailbody: "!unsafe '\n'" and won't work, there is also the alternative to use yaml folded style like they explain here https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-basics – MikZuit Jun 03 '20 at 14:19
  • @MikZuit can you give me the exact set_fact code syntax for me to try? – Ashar Jun 03 '20 at 14:34
  • How about `mailbody.split('\n')` and then write email body using loop? – Moon Jun 03 '20 at 15:19
  • Related: [Trying to do a newline/return character](https://www.reddit.com/r/ansible/comments/q6nbsr/trying_to_do_a_newlinereturn_character/) (Reddit). – kenorb Nov 20 '21 at 00:39
  • I'd just like to say that every single answer given here is wrong. – TheDESTROS Aug 22 '23 at 15:51

3 Answers3

2

Please try something like this :

as stated on the documentation I share in comment "The difference between single quotes and double quotes is that in double quotes you can use escapes : foo: "a \t TAB and a \n NEWLINE"

so

1.first thing to try:

mailbody: "{{ mailbody | default('') + 'PROFILE_NAME:' +  PROFILE_NAME ~}} {{ 'SERVER_NAME:' + SERVER_NAME ~ }}\n\n{{'NODE_NAME:' +  NODE_NAME ~ }}\n\n\n\n"

2.if that doesn help then:

mailbody: >
   "{{ mailbody | default('') + 'PROFILE_NAME:' +  PROFILE_NAME ~}}"
   "{{ 'SERVER_NAME:' + SERVER_NAME ~ }}"
   "{{'NODE_NAME:' +  NODE_NAME ~ }}"

3.if still won't work:

 mailbody: | 
     "{{ mailbody | default('') + 'PROFILE_NAME:' +  PROFILE_NAME ~}}"
     "{{ 'SERVER_NAME:' + SERVER_NAME ~ }}"
     "{{'NODE_NAME:' +  NODE_NAME ~ }}"

4.if this still isn't working try removing double quotes to this last 2 and 3.

MikZuit
  • 684
  • 5
  • 17
  • None of the 4 suggestions helped and I get `unexpected end of print statement` error for each. Complete output here: `TASK [set_fact] **************************************************************** task path: /app/test.yml:74 fatal: [10.9.121.117_1]: FAILED! => {"msg": "template error while templating string: unexpected 'end of print statement'. String: {{ mailbody | default('') + 'PROFILE_NAME:' + PROFILE_NAME ~ }} {{ 'SERVER_NAME:' + SERVER_NAME ~ }} {{ 'NODE_NAME:' + NODE_NAME ~ }} "}` – Ashar Jun 03 '20 at 15:13
0

I created an example playbook using the 4th suggestion from MikZuit's answer with the literal block syntax and it works like a charm. It sets the variable with set_fact which I tried to both debug and copy (print to a file). This is it:

- name: variable test
  hosts: 127.0.0.1
  connection: local
  vars:
    mailbody: "I am a mail body."
    PROFILE_NAME: "my profile"
    SERVER_NAME: "myserver.example.org"
    NODE_NAME: "I feel on edge today."
  tasks:
    - name: variable test | set variable
      ansible.builtin.set_fact:
        mailbody: |
          {{ mailbody | default('') }}

          PROFILE_NAME: {{ PROFILE_NAME }}

          SERVER_NAME: {{ SERVER_NAME }}

          NODE_NAME: {{ NODE_NAME }}

    - name: variable test | print variable to stdout
      ansible.builtin.debug:
        var: mailbody
    - name: variable test | print variable to file
      ansible.builtin.copy:
        content: "{{ mailbody }}"
        dest: "/tmp/mailbody.txt"

When I run the playbook the debug output looks like this:

ok: [localhost] => {
    "mailbody": "I am a mail body.\n\nPROFILE_NAME: my profile\n\nSERVER_NAME: myserver.example.org\n\nNODE_NAME: I feel on edge today.\n"
}

Note that the debug module prints the output as JSON and escapes any special characters, unlike the copy module which, in this playbook, prints the following output to /tmp/mailbody.txt:

I am a mail body.

PROFILE_NAME: my profile

SERVER_NAME: myserver.example.org

NODE_NAME: I feel on edge today.

Some general notes:

Since v2.10 or so, Ansible has been strongly modularized. This means that task names should now be fully qualified collection names (e.g. ansible.builtin.copy instead of copy) in order to avoid possible name collisions with other collections. Also it does not accept any plays or tasks without a name anymore.

When using the literal block syntax, it will truncate trailing newlines (like the empty line after NODE_NAME: {{ NODE_NAME }}) down to at most one. Btw, you don't need any quotes inside these blocks except for literals in evaluated expressions, e.g. {{ mailbody | default('') }}.

Quoting in Ansible works very similar to quoting in Linux shells. Double quotes can evaluate expressions and escaped characters while single quotes pass everything "as is".

Concerning the unexpected end of print statement error in Ashar's comment: the error occurs because the concatenation operator (~) expects a second operand but instead finds closed curly braces which mark the end of the expression. Just delete all the tildes shown in your error message and you should be good to go.

Marcel B.
  • 26
  • 1
  • 6
0

This code works for me.

- name: E-email notification SECTION
  hosts: localhost

  tasks:
    - name: Get output dir detail
      shell: 'ls -alh /opt/git/configbackups/'
      register: ls_command_output

    - name: Send a success email
      mail:
        host: example.com
        port: 25
        to: someone@example.com
        subject: Network device backup Complete
        body: |
               "Access http: //x.x.x.x/gitweb/ for Viewing Configuration with your AD username and password"
               {% for line in ls_command_output.stdout_lines %}
               {{ line }}
               {% endfor %}'
marsteel
  • 1
  • 1