### Summary

This appears to be related to this change around action_groups: https://github.com/ansible/ansible/pull/74039

Before that, a module in a role could be set in `module_defaults`, but it doesn't work anymore. The action fails to resolve, as it's always trying to associate it with `ansible.builtin` rather than `ansible.legacy`, even when fully qualified.

Consider a role with `library/my_module.py` and a simple `tasks/main.yml`:

```yaml
- name: Try defaults
  module_defaults:
    my_module:
      data: hello
  block:

    - my_module:
```

```yaml
- name: Try defaults
  module_defaults:
    my_module:
      data: hello
  block:

    - ansible.legacy.my_module:
```

```yaml
- name: Try defaults
  module_defaults:
    ansible.legacy.my_module:
      data: hello
  block:

    - ansible.legacy.my_module:
```

```yaml
- name: Try defaults
  module_defaults:
    ansible.legacy.my_module:
      data: hello
  block:

    - my_module:
```

All permutations fail with the same error:

> ERROR! Could not resolve action ansible.builtin.my_module in module_defaults

EDIT to add:

I checked out commit 9af0d916768986f50647b11f896a7cdac1550230 (which as far as I can tell is the last commit before the linked PR was merged), and all of the above forms work except the last one, which fails with:

> fatal: [localhost]: FAILED! => {"changed": false, "msg": "missing required arguments: data"}


---

The changelog entry in that PR mentions:

> Fully qualified 'ansible.legacy' plugin names are not included implicitly in action_groups.

But I'm not sure if that's relevant, as no action groups are in play here. I couldn't find any way to make module defaults work for the module in the role.

### Issue Type

Bug Report

### Component Name

lib/ansible/playbook/base.py

### Ansible Version

```console
$ ansible --version
ansible [core 2.12.0.dev0] (devel 1fc1ab89ae) last updated 2021/07/17 12:29:56 (GMT -400)
  config file = None
  configured module search path = ['/home/briantist/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/briantist/code/ansible/ansible.core/lib/ansible
  ansible collection location = /home/briantist/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/briantist/code/ansible/ansible.core/bin/ansible
  python version = 3.6.9 (default, Jan 26 2021, 15:33:00) [GCC 8.4.0]
  jinja version = 3.0.1
  libyaml = True
```


### Configuration

```console
$ ansible-config dump --only-changed
[DEPRECATION WARNING]: Ansible will require Python 3.8 or newer on the controller starting with Ansible 2.12. Current version: 3.6.9 (default, Jan 26 2021, 15:33:00) [GCC 8.4.0]. This feature will be removed from
 ansible-core in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: You are running the development version of Ansible. You should only run Ansible from "devel" if you are modifying the Ansible engine, or trying out features under development. This is a rapidly
changing source of code and can become unstable at any point.
[DEPRECATION WARNING]: ANSIBLE_CALLBACK_WHITELIST option, normalizing names to new standard, use ANSIBLE_CALLBACKS_ENABLED instead. This feature will be removed from ansible-core in version 2.15. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
CALLBACKS_ENABLED(env: ANSIBLE_CALLBACK_WHITELIST) = ['profile_tasks']
DEFAULT_FORKS(env: ANSIBLE_FORKS) = 30
INVENTORY_ENABLED(env: ANSIBLE_INVENTORY_ENABLED) = ['host_list', 'auto', 'ini', 'yaml', 'toml']
```


### OS / Environment

Ubuntu 18.04 in WSL2

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
Described in issue summary

### Expected Results

`module_defaults` work for modules in a role

### Actual Results

```console
ERROR! Could not resolve action ansible.builtin.my_module in module_defaults
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
if omit_value is not None and value == omit_value:
                    if callable(attribute.default):
                        setattr(self, name, attribute.default())
                    else:
                        setattr(self, name, attribute.default)
                    continue
                
                if value is not None:
                    value = self.get_validated_value(name, attribute, value, templar)
                
                setattr(self, name, value)
            except (TypeError, ValueError) as e:
                value = getattr(self, name)
                raise AnsibleParserError("the field '%s' has an invalid value (%s), and could not be converted to an %s."
                                         "The error was: %s" % (name, value, attribute.isa, e), obj=self.get_ds(), orig_exc=e)
            except (AnsibleUndefinedVariable, UndefinedError) as e:
                if templar._fail_on_undefined_errors and name != 'name':
                    if name == 'args':
                        msg = "The task includes an option with an undefined variable. The error was: %s" % (to_native(e))
                    else:
                        msg = "The field '%s' has an invalid value, which includes an undefined variable. The error was: %s" % (name, to_native(e))
                    raise AnsibleParserError(msg, obj=self.get_ds(), orig_exc=e)
        self._finalized = True
    def _load_vars(self, attr, ds):
        '''
        Vars in a play can be specified either as a dictionary directly, or
        as a list of dictionaries. If the later, this method will turn the
        list into a single dictionary.
        '''
        def _validate_variable_keys(ds):