Repository: ansible/ansible

Issue:
Environment on task level doesn't override play level definition when used with loops
For example, the following yaml:
```- hosts: all
  environment:
    str: "play level"
  tasks:
    - name: test 1
      command: "/bin/echo $str"
      environment:
        str: "task level"
    - name: test 2
      command: "/bin/echo $str"
      with_items: [ 1, 2, 3]
      environment:
        str: "task level"
```
And execute: ```ansible-playbook bug.yml -v```

Task 'test 1' will have 'task level' in stdout
Task 'test 2' will loop 3 times with 'play level' in stdout

IF environment is not defined on play-level, 'test 2' will use the task-level setting.

```PLAY [all] ****************************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [localhost]

TASK [test 1] *************************************************************************************************************************************************************
changed: [localhost] => {"changed": true, "cmd": ["/bin/echo", "$str"], "delta": "0:00:00.001448", "end": "2017-11-08 15:36:32.769761", "failed": false, "rc": 0, "start": "2017-11-08 15:36:32.768313", "stderr": "", "stderr_lines": [], "stdout": "task level", "stdout_lines": ["task level"]}

TASK [test 2] *************************************************************************************************************************************************************
changed: [localhost] => (item=1) => {"changed": true, "cmd": ["/bin/echo", "$str"], "delta": "0:00:00.001317", "end": "2017-11-08 15:36:32.971494", "failed": false, "item": 1, "rc": 0, "start": "2017-11-08 15:36:32.970177", "stderr": "", "stderr_lines": [], "stdout": "play level", "stdout_lines": ["play level"]}
changed: [localhost] => (item=2) => {"changed": true, "cmd": ["/bin/echo", "$str"], "delta": "0:00:01.002670", "end": "2017-11-08 15:36:34.150920", "failed": false, "item": 2, "rc": 0, "start": "2017-11-08 15:36:33.148250", "stderr": "", "stderr_lines": [], "stdout": "play level", "stdout_lines": ["play level"]}
changed: [localhost] => (item=3) => {"changed": true, "cmd": ["/bin/echo", "$str"], "delta": "0:00:01.002600", "end": "2017-11-08 15:36:35.349230", "failed": false, "item": 3, "rc": 0, "start": "2017-11-08 15:36:34.346630", "stderr": "", "stderr_lines": [], "stdout": "play level", "stdout_lines": ["play level"]}

PLAY RECAP ****************************************************************************************************************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0   
```
##### ISSUE TYPE
 - Bug Report

##### ANSIBLE VERSION
```
ansible 2.4.1.0
```



Discussion:
@msoltyspl Greetings! Thanks for taking the time to open this issue. In order for the community to handle your issue effectively, we need a bit more information. 

Here are the items we could not find in your description:
- component name

Please set the description of this issue with this template:
https://raw.githubusercontent.com/ansible/ansible/devel/.github/ISSUE_TEMPLATE.md

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_missing_data --->

Resolving patch:
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -177,10 +177,9 @@ def _compute_environment_string(self, raw_environment_out=None):
             if not isinstance(environments, list):
                 environments = [environments]
 
-            # the environments as inherited need to be reversed, to make
-            # sure we merge in the parent's values first so those in the
-            # block then task 'win' in precedence
-            environments.reverse()
+            # The order of environments matters to make sure we merge
+            # in the parent's values first so those in the block then
+            # task 'win' in precedence
             for environment in environments:
                 if environment is None or len(environment) == 0:
                     continue


Test patch:
diff --git a/test/integration/targets/environment/test_environment.yml b/test/integration/targets/environment/test_environment.yml
--- a/test/integration/targets/environment/test_environment.yml
+++ b/test/integration/targets/environment/test_environment.yml
@@ -74,3 +74,93 @@
                 - '"not1" in test_env5.stdout_lines'
                 - '"val2" in test_env5.stdout_lines'
       environment: "{{test2}}"
+
+- name: test setting environment while using loops
+  hosts: testhost
+  environment:
+    foo: outer
+  tasks:
+    - name: verify foo==outer
+      command: /bin/echo $foo
+      loop:
+        - 1
+      register: test_foo
+
+    - name: assert foo==outer
+      assert:
+        that:
+          - "{{ test_foo.results[0].stdout == 'outer' }}"
+
+    - name: set environment on a task
+      environment:
+        foo: in_task
+      command: /bin/echo $foo
+      loop:
+        - 1
+      register: test_foo
+
+    - name: assert foo==in_task
+      assert:
+        that:
+          - "test_foo.results[0].stdout == 'in_task'"
+
+    - name: test that the outer env var is set appropriately still
+      command: /bin/echo $foo
+      loop:
+        - 1
+      register: test_foo
+
+    - name: assert foo==outer
+      assert:
+        that:
+          - "{{ test_foo.results[0].stdout == 'outer' }}"
+
+    - name: set environment on a block
+      environment:
+        foo: in_block
+      block:
+        - name: test the environment is set in the block
+          command: /bin/echo $foo
+          loop:
+            - 1
+          register: test_foo
+
+        - name: assert foo==in_block
+          assert:
+            that:
+              - "test_foo.results[0].stdout == 'in_block'"
+
+        - name: test setting environment in a task inside a block
+          environment:
+            foo: in_block_in_task
+          command: /bin/echo $foo
+          loop:
+            - 1
+          register: test_foo
+
+        - name: assert foo==in_block_in_task
+          assert:
+            that:
+              - "test_foo.results[0].stdout == 'in_block_in_task'"
+
+        - name: test the environment var is set to the parent value
+          command: /bin/echo $foo
+          loop:
+            - 1
+          register: test_foo
+
+        - name: assert foo==in_block
+          assert:
+            that:
+              - "test_foo.results[0].stdout == 'in_block'"
+
+    - name: test the env var foo has the initial value
+      command: /bin/echo $foo
+      loop:
+        - 1
+      register: test_foo
+
+    - name: assert foo==outer
+      assert:
+        that:
+          - "{{ test_foo.results[0].stdout == 'outer' }}"