Search code examples
loopsansibleslack

How to NOT to loop in ansible if one of the variable is undefined


Here is my script , i am trying to send a message to multiple slack channels, using loops. I have defined variable inside variable.

    - name: send msg to slack
      community.general.slack:
         token: XXXXXXXXXXXX
         blocks:
           - type: section
             text:
              type: mrkdwn
              text: |-
               ****text message********
         channel: '{{ item.slack_channel }}'
         username: 'slackbot'
         parse: 'full'
      loop: "{{ slack | default([]) | selectattr('slack_channel') | list }}"
      when: inventory_hostname ==  'abc.xyz'

vars

slack:
      - slack_channel: '#mychannel'
      - slack_channel: '{{ slackchannel1|default(None) }}'
      - slack_channel: '{{ slackchannel2|default(None) }}'

slackchanne1 and slackchannel2 variable are optional, sometimes i will define value but sometimes i won't define value

What i want is -

When a script is triggered, message has to send to all slack channels, but if the variable is not defined then i don't want loop to run and get fail, incase if it run and no variable found then it should't say play failed, it has to skip that loop cause no value is defined for variable slackchannel1 and slackchannel2 , at the end play should be successful with single defined variable vlaue - slack_channel: #mychannel

error i am getting

fatal: [abc.xyz]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'msg'\n\nThe error appears to be in 'tasks/slack.yml': line 303, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: send msg to slack\n      ^ here\n"}

Solution

  • Use default(None) to avoid error "'slackchannel1' is undefined"

        slack:
          - slack_channel: '#mychannel'
          - slack_channel: '{{ slackchannel1|default(None) }}'
          - slack_channel: '{{ slackchannel2|default(None) }}'
    

    Then, select defined items

    loop: "{{ slack | default([]) | selectattr('slack_channel') | list }}"
    

    Quoting from selectattr

    If no test is specified, the attribute’s value will be evaluated as a boolean.


    For example, the playbook

    shell> cat playbook.yml
    - hosts: localhost
      vars:
        slack:
          - slack_channel: '#mychannel'
          - slack_channel: '{{ slackchannel1|default(None) }}'
          - slack_channel: '{{ slackchannel2|default(None) }}'
    
      tasks:
        - debug:
            msg: "{{ item.slack_channel }}"
          loop: "{{ slack | default([]) | selectattr('slack_channel') | list }}"
    
    shell> ansible-playbook playbook.yml
    

    gives

    ok: [localhost] => (item={'slack_channel': '#mychannel'}) => 
      msg: '#mychannel'
    

    When the variable is defined

    shell> ansible-playbook playbook.yml -e slackchannel2=test
    

    the playbook gives

    ok: [localhost] => (item={'slack_channel': '#mychannel'}) => 
      msg: '#mychannel'
    ok: [localhost] => (item={'slack_channel': 'test'}) => 
      msg: test