Repository: ansible/ansible

Issue:
included include_role fails to escalate since ebf971f
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
include_role


##### ANSIBLE VERSION
```
2.4
devel
```

##### OS / ENVIRONMENT
- ubuntu LTS

##### SUMMARY
- 2nd reproducable test case here: https://github.com/kiorky/ansible_test_cases/tree/35065b -> 
   - The third form (with block) should already work
- reproducable test case here: https://github.com/kiorky/ansible_test_cases/tree/35065
- With the commit ebf971f931290a113f738f658d7a6e095994e120 the test will fail, the third role wont escalate to root, prior this commit, everything is fine.



Related to #33922 & #32565 


Discussion:
@kiorky 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 --->
cc @jimi-c 
I wanted to take a moment, and update this issue with our plans for this issue.

We are going to work on a change for the 2.4.3 release, that will maintain the recursion fix, but revert the previous behavior to allow some of the attributes to `include_*` to merge down onto the included tasks.

Our plan for 2.5 currently, is to leave the functionality as is.

The reason for this decision and disparity between releases is due to the fact that it was never intended for `include_*` to merge down any attributes to the included tasks in 2.4.0, restricting that behavior only to `import_*`. The commit you referenced above actually fixed a bug in the code that allowed `include_*` to merge some attributes.  However, since we try not to change behavior in a bug fix release like this change introduced, we'll revert the behavior change only for 2.4.
@sivel @abadger im not sure that will be the proper way to go as you then can't escalate a role that doesnt explictly "becomes" itself, like the test case i gave and is pretty inintuitive.

You can't then run a task somewhere that does:
```yaml
- include_role: {name: foo}
  become: true
```
and expect ``foo`` to run as **root**.

Idem for the when statements, this defeats and breaks completly the initial use of include_role.
I'm also begging for a long time for your input on my  #32565 PR, if someone can make things go a little bit further ;-)
This looks related to #34974 "inconsistent documentation for tags on dynamic-include tasks"

@sivel where can we see which attributes will be merged down and which will not? Thanks!
in 2.5, None.
Really, the more i think to this, the more i think that for the special case of the ``become_`` knobs, they should merge down to the executed tasks for ``include_tasks`` & ``include_roles``.

This will be in other case disastrous in term of usability of ``include_*`` and DRY patterns.
As the only solution i see for the moment is to patch all include statements to add a vars: {somevar: true}

```yaml
- include_role: {name: foo}
  vars: {somevar: true}
```

and also patch all the underlying, naive roles, that dont escalate themselves up to support escalating from the upper role by reacting on that ``somevar``

```yaml
# foo/tasks/main.yml
- become: "{{somevar}}"
  shell: foobar
```

This also dismiss the ability to reuse roles that is not under the OP control, as he wont be able to patch the ``naive`` role.

So, we seem there to be in an impasse.

Resolving patch:
diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py
--- a/lib/ansible/playbook/block.py
+++ b/lib/ansible/playbook/block.py
@@ -291,7 +291,7 @@ def _get_parent_attribute(self, attr, extend=False, prepend=False):
             value = self._attributes[attr]
             if self._parent and (value is None or extend):
                 try:
-                    if getattr(self._parent, 'statically_loaded', True):
+                    if attr != 'when' or getattr(self._parent, 'statically_loaded', True):
                         if hasattr(self._parent, '_get_parent_attribute'):
                             parent_value = self._parent._get_parent_attribute(attr)
                         else:
diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py
--- a/lib/ansible/playbook/task.py
+++ b/lib/ansible/playbook/task.py
@@ -413,7 +413,7 @@ def _get_parent_attribute(self, attr, extend=False, prepend=False):
         try:
             value = self._attributes[attr]
             if self._parent and (value is None or extend):
-                if getattr(self._parent, 'statically_loaded', True):
+                if attr != 'when' or getattr(self._parent, 'statically_loaded', True):
                     # vars are always inheritable, other attributes might not be for the partent but still should be for other ancestors
                     if attr != 'vars' and getattr(self._parent, '_inheritable', True) and hasattr(self._parent, '_get_parent_attribute'):
                         parent_value = self._parent._get_parent_attribute(attr)


Test patch: