Repository: ansible/ansible
Pull request: Document how to migrate a standalone role to a collection
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Document how to migrate a standalone role to a collection.
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
Fixes https://github.com/ansible/ansible/issues/68473

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Docs Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
docs.ansible.com
##### ADDITIONAL INFORMATION
<!--- Include additional information to help people understand the change here -->
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->

<!--- Paste verbatim command output below, e.g. before and after your change -->
```paste below

```


Problem statement:
Document how to convert legacy roles to collections
<!--- Verify first that your improvement is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below, add suggestions to wording or structure -->
Add the documentation in the next comment to the Ansible docs.
<!--- HINT: Did you know the documentation has an "Edit on GitHub" link on every page ? -->

##### ISSUE TYPE
- Documentation Report

##### COMPONENT NAME
<!--- Write the short name of the rst file, module, plugin, task or feature below, use your best guess if unsure -->
docs.ansible.com
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below

```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. OS version, browser, etc. -->

##### ADDITIONAL INFORMATION
<!--- Describe how this improves the documentation, e.g. before/after situation or screenshots -->

<!--- HINT: You can paste gist.github.com links for larger files -->

Hints from issue discussion:
# Migrating Roles to Roles in Collections

This document stands as a guide for Content Authors to migrate “legacy style” Ansible Roles, which we will refer to as Legacy Roles throughout this document, into an Ansible Role that’s a part the more featureful and flexible Ansible Collection format used to distribute Ansible Content now and into the future.

## Legacy Role Structure
The first thing we need to do is keep in mind the directory structure of a Legacy Role as we will reference it periodically while moving through this guide to migrate out automation content.

```
legacy-role/
├── defaults
├── files
├── handlers
├── library
├── meta
├── [*_plugins]
├── tasks
├── templates
├── tests
└── vars
```

As notated in the [Ansible Roles documentation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html), each of these directories exists for a specific purpose.

- defaults - default variables for the Legacy Role (see [Using Variables](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#playbooks-variables) for more information).
- files - contains files which can be deployed via this Legacy Role.
- handlers - contains handlers, which may be used by this role or even anywhere outside this Legacy Role.
- library - contains custom Ansible Modules encapsulated within this Legacy Role.
- tasks - contains the main list of tasks to be executed by the Legacy Role.
- vars - other variables for the Legacy Role (see [Using Variables](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#playbooks-variables) for more information).
- templates - contains templates which can be deployed via this Legacy Role.
- meta - defines some metadata for this Legacy Role such as dependencies and Ansible Galaxy information

- \*\_plugins - optionally any plugin type can be encapsulated inside a Legacy Role by placing a directory named PLUGINTYPE_plugins (such as ``filters_plugin``)

## Ansible Collections

Ansible Collections, introduced in Ansible 2.9, are a means of creating and distributing Ansible Content that overcomes the limitations of Legacy Roles. Ansible Collections may contain one or many Roles, Modules, Plugins (of any supported type), and much more. Ansible Collections introduce a concept of Collection Namespace and Collection Name, this way an Organization can define a Namespace to easily identify all Ansible Content produced by the organization and then the Name can represent the specific technology or solution that the content targets. Contents within an Ansible Collection are meant to be referenced by Fully Qualified Collection Name (FQCN), but can be referenced as shorthand as described in the [Ansible Collection User Guide](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html).

The directory structure of an Ansible Collection is listed below, some of its subdirectories will be the target location of the content we will migrate from the Legacy Role and we will need to be familiar with them.
```
namespace/
└── name/
    ├── docs/
    ├── galaxy.yml
    ├── plugins/
    │   ├── modules/
    │   │   └── module1.py
    │   ├── inventory/
    │   └── .../
    ├── README.md
    ├── roles/
    │   ├── role1/
    │   ├── role2/
    │   └── .../
    ├── playbooks/
    │   ├── files/
    │   ├── vars/
    │   ├── templates/
    │   └── tasks/
    └── tests/
```
The main directories that we will focus on here are the ``roles/`` and the ``plugins/`` directory.

A Collection can contain one or many Ansible Roles inside the roles/ directory and these are almost identical to Legacy Roles with the following exceptions:

- Roles inside Collections do not, and can not, have a ``library/`` directory containing custom modules, instead modules are kept in the plugins/modules/ directory of the Collection and can be referenced by any of the Roles contained within a Collection, or independently as their own entity for use in Roles outside of this Collection, Playbooks, Task files, etc.
  - Modules inside a Collection need FQCN paths for their Python imports, this will be covered in detail in the Porting Plugins and Modules section of this document.
  - NOTE: The reason for the ``plugins/modules/`` location is because modules are technically just another type of plugin in Ansible. They have historically been treated in a special way but are not referenced in such a way any longer.
- Legacy Roles can optionally encapsulate any Ansible Plugin in a ``PLUGINTYPE_plugins/`` directory (such as ``filters_plugin/``), however these plugins will go into their respective ``plugins/PLUGINTYPE/`` (such as ``plugins/filter/``) directory in an Ansible Collection.
  - This allows them to be independently referenced by FQCN
  - NOTE: In Legacy Roles, some of the plugin directories referenced their plugin types in the plural sense; this is not the case in Collections. The specific plugin directories as expected as covered in the Creating a Collection section of this document.

### Creating a Collection

In order to migrate from a Legacy Role to a Collection, we need to create a Collection. Fortunately, just as with Legacy Roles the ability to initialize a Collection is provided by the ``ansible-galaxy`` command-line utility.

We will create a Collection using both the namespace and the name in dot notation:
```
    ansible-galaxy collection init namespace.name
```
This will create the directory structure that’s listed at the top of this section of the document and the ``namespace/name/plugins/`` directory contains a ``README.md`` file, which is a markdown format document that explains the various types of Ansible Plugins that can be contained within the optionally created subdirectories.

## Migrating a Legacy Role without Plugins or Modules

In the instance of a Legacy Role that does not contain any custom modules or plugins, the only thing that is necessary is to copy the entire Legacy Role directory into the ``roles/`` subdirectory of the Collection. At this point the Role may be used as referenced by FQCN.

Example:
```
$ mkdir namespace/name/roles/my_legacy_role/
$ cp -r /path/to/legacy/role/namespace/\* name/roles/my_legacy_role/
```
At this point we can utilize it in a playbook like the following:

```
---
- name: example role by FQCN
  hosts: some_host_pattern
  tasks:
    - name: import FQCN Role from a Collection
      import_role:
        name: namespace.name.my_legacy_role
```

Something to note from this example is that the type of Content from inside a Collection is inferred contextually. More information can be found in the [Ansible Collection User Guide](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html).

## Porting Plugins and Modules

Plugins, and Modules which are also plugins, written in Python need some consideration when using Collections. We will cover those situations here.

### Custom module_utils

In the event you have a module that is written in Python and is using a custom module_utils, it was previously addressable in the top level ``ansible.module_utils`` Python namespace. However, that is no longer the case as the top level Python namespace will no longer merge the Ansible internal Python namespace with external content in the future and merging external content from Collections into the Ansible internal Python namespace is not supported. There is an example of this in the [Ansible Developing Collections Guide](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#module-utils), but we will also cover an example in this document.

When coding with ``module_utils`` in a collection, the Python import statement needs to take into account the _FQCN_ along with the ``ansible_collections`` convention. The resulting Python import will look like the following example:
```
from ansible_collections.{namespace}.{name}.plugins.module_utils.{util} import {something}
```

The following example code snippets show a Python and a PowerShell module using both default Ansible module_utils and those provided by a collection. In this example the namespace is ansible_example, the collection is community. In the Python example the module_util in question is called helper such that the FQCN is ``ansible_example.community.plugins.module_utils.helper``:

```python
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible_collections.ansible_example.community.plugins.module_utils.helper import HelperRequest

argspec = dict(
	name=dict(required=True, type='str'),
	state=dict(choices=['present', 'absent'], required=True),
)

module = AnsibleModule(
	argument_spec=argspec,
	supports_check_mode=True
)


_request = HelperRequest(
	module,
	headers={"Content-Type": "application/json"},
      data=data
)
```

In the PowerShell example the module_utils in question is called hyperv such that the FQCN is ``ansible_example.community.plugins.module_utils.hyperv``:

```
#!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -PowerShell ansible_collections.ansible_example.community.plugins.module_utils.hyperv

$spec = @{
	name = @{ required = $true; type = "str" }
	state = @{ required = $true; choices = @("present", "absent") }
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)

Invoke-HyperVFunction -Name $module.Params.name

$module.ExitJson()
```

### Importing from \_\_init\_\_.py

Because of the way that the CPython interpreter does imports, combined with the way the Ansible plugin loader works, if your custom embedded Module or Plugin requires importing something from an ``\_\_init\_\_.py`` file that will also become part of your Collection, either by originating as content inside a Legacy Role or otherwise, it requires using the file name in the python import statement. The following example is for an ``\_\_init\_\_.py`` file that is part of a Callback Plugin found inside a Collection named ``namespace.name``.

```python
from ansible_collections.namespace.name.plugins.callback.__init__ import CustomBaseClass
```

# Deliver Downstream Compatibility (RPMs)

In the event the content of a Legacy Role is part of a Support Lifecycle of a product, or there is some other requirement for a Legacy Role to continue co-existing with its Collection Role counterpart, hopefully as part of a transition period, there are ways to allow Collection Role content be delivered downstream in methods such as RPM packaging that will function like they did as Legacy Roles. There is a real-world example of “porting” RHEL System Roles to a Collection and providing existing backwards compatibility via downstream RPM [here](https://github.com/maxamillion/collection-rhel-system-roles), but we will walk through a fictional example in this section of the document.
- **NOTE:** This requires Ansible 2.9.0 or newer.


In this example we will have a Legacy Role called ``my-legacy-role.webapp`` to emulate a Legacy Role that contains dashes in the name (which is not valid in Collections), this Legacy Role will contain a custom module in the ``library/`` directory called manage_webserver.

```
mylegacy-role.webapp
├── defaults
├── files
├── handlers
├── library
├── meta
├── tasks
├── templates
├── tests
└── vars
```

The first thing we need to do is create a new Collection, for the sake of example we will use the namespace acme and the name webserver which produces the combination of ``acme.webserve``:r

```
$ ansible-galaxy collection init acme.webserver
- Collection acme.webserver was created successfully
$ tree acme -d 1
acme
└── webserver
	├── docs
	├── plugins
	└── roles
```
Next, we need to create our webapp Role inside the Collection by simply copying over all contents from the Legacy Role.

```
$ mkdir acme/webserver/roles/webapp
$ cp mylegacy-role.webapp/* acme/webserver/roles/webapp/
```

At this point we need to move the manage_webserver module to its new home in ``acme/webserver/plugins/modules/``:

```
$ cp mylegacy-role.webapp/library/manage_webserver.py acme/webserver/plugins/modules/manage.py
```

You will note that the original source file of ``manage_webserver.py`` and the destination file of ``manage.py`` differ in name. This is optional but I’ve chosen here to change the name of the module since I no longer have to contextualize the module with webserver being in the name, instead because the FQCN provides proper namespacing it can be used as ``acme.webserver.manage``. Next we will need to go through all Tasks files in the Role (in our example there is only one: ``mylegacy-role.webapp/tasks/main.yml``) and change any use of the ``manage_webserver`` module to ``acme.webserver.manage`` in the Tasks list.
- **NOTE:** The renaming that takes place here is not a requirement but simply an illustration of what Content referenced by FQCN can offer in terms of context and in turn can make module and plugin names shorter. Something else to keep in mind is that if there is a need to offer these modules independently of the Role to users, the old naming conventions can be maintained but users will have to add the [collections keyword to their Plays](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#using-collections-in-a-playbook). Typically Roles are meant to be used as the abstraction layer and users aren’t digging in and using components of the Role independently so this is likely not a concern.

Now that we have a functional Collection what we will need to do is provide it and its roles as parallel Content in a downstream distribution mechanism. I will illustrate how to do this as an RPM.

## Downstream RPM
In order to deliver a Role as both a Legacy Role and a Collection Role, only a few things need to be done and are outlined here:

- The Collection be placed in ``/usr/share/ansible/collections/ansible_collections/``
- The contents of the Role inside the Collection be copied into a directory named after the Legacy Role and be placed in ``/usr/share/ansible/roles/``
- All previously bundled modules and plugins that are used in the Legacy Role are now referenced by FQCN so that even though they are no longer embedded, they can be found from the Collection Contents
  - This is an example of how the content inside an Ansible Collection is a unique entity and doesn’t have to be bound to a Role or otherwise. We could have made two separate Collections: one for the modules and plugins and another for the Legacy Role to migrate to; and as long as the Role used the modules and plugins as FQCN entities it would work just as it always has.

Here is an example RPM spec file to accomplish this using the example content from above:
```
Name: acme-ansible-content
Summary: Ansible Collection for deploying and configuring ACME webapp
Version: 1.0.0
Release: 1%{?dist}
License: GPLv3+
Source0: amce-webserver-1.0.0.tar.gz

Url: https://github.com/acme/webserver-ansible-collection
BuildArch: noarch

%global roleprefix mylegacy-role.
%global collection_namespace acme
%global collection_name webserver

%global collection_dir %{_datadir}/ansible/collections/ansible_collections/%{collection_namespace}/%{collection_name}

%description
Ansible Collection and Legacy Role (for legacy compatibility and migration) to deploy, configure, and manage the ACME webapp software.

%prep
%setup -qc

%build

%install

mkdir -p %{buildroot}/%{collection_dir}
cp -r ./* %{buildroot}/%{collection_dir}/

mkdir -p %{buildroot}/%{_datadir}/ansible/roles
for role in %{buildroot}/%{collection_dir}/roles/*
do
	cp -pR ${role} %{buildroot}/%{_datadir}/ansible/roles/%{roleprefix}$(basename ${role})

	mkdir -p %{buildroot}/%{_pkgdocdir}/$(basename ${role})
	for docfile in README.md COPYING LICENSE
	do
    	if [ -f ${role}/${docfile} ]
    	then
        	cp -p ${role}/${docfile} %{buildroot}/%{_pkgdocdir}/$(basename ${role})/${docfile}
    	fi
	done
done


%files
%dir %{_datadir}/ansible
%dir %{_datadir}/ansible/roles
%dir %{_datadir}/ansible/collections
%dir %{_datadir}/ansible/collections/ansible_collections
%{_datadir}/ansible/roles/
%doc %{_pkgdocdir}/*/README.md
%doc %{_datadir}/ansible/roles/%{roleprefix}*/README.md
%{collection_dir}
%doc %{collection_dir}/roles/*/README.md
%license %{_pkgdocdir}/*/COPYING
%license %{_pkgdocdir}/*/LICENSE
```

@samccann - This looks helpful (and it might also prod someone to build a tool to at least convert simple roles automatically), but I am still interested in figuring out how to convert roles on Ansible Galaxy to collections, using the same name—right now it seems there's no way to do that unless I delete the role first, which would affect all my role's existing users.

This might not be the right place to _document_ this particular functionality (not sure exactly where, maybe somewhere in 'Contributing Content' under Galaxy? (https://galaxy.ansible.com/docs/contributing/index.html) Even if it's documented there, wherever this documentation ends up might also consider adding a link to that documentation as well, especially if roles are going to be _officially_ deprecated at some point.

Related issue: https://github.com/ansible/galaxy/issues/2253
(Also, just wondering, by the fact that this documentation calls out roles as being "Legacy Roles", does that mean the idea of roles being able to be used separate from collections (e.g. standalone where your playbook has a roles directory alongside it) will also be deprecated at some point?)
Good questions. @abenokraitis - any words o wisdom here?
I think the goal was to say that "legacy roles" == roles that stand by themselves and not packaged as part of a collection. The idea of roles shouldn't be labeled as "legacy" but the packaging and delivery can be. @maxamillion @thaumos any other commentary here for @geerlingguy ?
Maybe we then try to be clear on "legacy standalone roles" or "legacy Galaxy roles" (latter preferred by me), since there tends to be a large amount of confusion about the future of _roles_ as an entity in the Ansible universe (vs. roles as standalone distributable bits of content in Galaxy).

Or am I also reading this wrong in that having a "role" in a local playbook outside a collection will someday not be supported (e.g. along with the deprecation of things like loading roles from `ANSIBLE_ROLES_PATH`)? (cc @sivel and @bcoca with whom I've discussed this as well)
In the rewrite to .rst I've started calling them standalone roles (vs roles within a collection). Does that work?
Great, I think we've agreed to proceed and place given what's there and the small changes above. Thank you!
@geerlingguy: FWIW I refer to what I think you mean by "legacy standalone roles" as "organizational roles" in all the best practices talks I've given over the years. I defined these as roles that "live" solely within a single automation project repo and were created to better organize things because the automation has become sufficiently complex enough that a single playbook becomes too unwieldy. There is little to no utility for these roles outside of that project or playbook and thereby would never go into Galaxy or Automation Hub.

Commit message: address latest feedback

Patch to review:
diff --git a/docs/docsite/rst/dev_guide/developing_collections.rst b/docs/docsite/rst/dev_guide/developing_collections.rst
index b1cc89e54e1a89..f24ea38f4cbeb1 100644
--- a/docs/docsite/rst/dev_guide/developing_collections.rst
+++ b/docs/docsite/rst/dev_guide/developing_collections.rst
@@ -83,6 +83,8 @@ Add a 'per plugin type' specific subdirectory here, including ``module_utils`` w
 
 Vars plugins are unsupported in collections. Cache plugins may be used in collections for fact caching, but are not supported for inventory plugins.
 
+.. _collection_module_utils:
+
 module_utils
 ^^^^^^^^^^^^
 
diff --git a/docs/docsite/rst/dev_guide/index.rst b/docs/docsite/rst/dev_guide/index.rst
index b74d80b3bd34b0..35d613a1ed850a 100644
--- a/docs/docsite/rst/dev_guide/index.rst
+++ b/docs/docsite/rst/dev_guide/index.rst
@@ -17,6 +17,7 @@ Find the task that best describes what you want to do:
    * I want to :ref:`add a custom plugin or module locally <developing_locally>`.
    * I want to figure out if :ref:`developing a module is the right approach <module_dev_should_you>` for my use case.
    * I want to :ref:`develop a collection <developing_collections>`.
+   * I want to :ref:`migrate a role to a collection <migrating_roles>`.
 
 * I've read the info above, and I'm sure I want to develop a module:
 
@@ -84,4 +85,5 @@ If you prefer to read the entire guide, here's a list of the pages in order.
    developing_module_utilities
    developing_collections
    collections_galaxy_meta
+   migrating_roles
    overview_architecture
diff --git a/docs/docsite/rst/dev_guide/migrating_roles.rst b/docs/docsite/rst/dev_guide/migrating_roles.rst
new file mode 100644
index 00000000000000..028f9f6a0c5d52
--- /dev/null
+++ b/docs/docsite/rst/dev_guide/migrating_roles.rst
@@ -0,0 +1,411 @@
+
+.. _migrating_roles:
+
+*************************************************
+Migrating Roles to Roles in Collections on Galaxy
+*************************************************
+
+You can migrate an existing standalone role into a collection-based role that you can host on Galaxy. This offers you more flexibility and resources to have your role within a collection. With Ansible collections on Galaxy, you can use a single collection as a delivery mechanism for many roles, releasing them as a single cohesive unit of re-usable automation. Inside a collection, custom plugins that used to be shipped in the role's :file:`library/`` directory can now be shared across all roles within a
+collection without duplicating them in each role's :file:`library/`` directory.
+
+You also need to migrate roles to collections if you want them as part of certified Ansible content.
+
+.. note::
+
+	If you want to import your collection to Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+See :ref:`developing_collections` for details on collections.
+
+
+.. contents::
+   :local:
+   :depth: 1
+
+Comparing standalone roles to collection roles
+===============================================
+
+:ref:`Standalone roles <playbooks_reuse_roles>` have the following directory structure:
+
+.. code-block:: bash
+  :emphasize-lines: 5,7,8
+
+    role/
+    ├── defaults
+    ├── files
+    ├── handlers
+    ├── library
+    ├── meta
+    ├── module_utils
+    ├── [*_plugins]
+    ├── tasks
+    ├── templates
+    ├── tests
+    └── vars
+
+
+The highlighted directories above will change when you migrate to a collection-based role. The collection directory structure includes a :file:`roles/` directory:
+
+.. code-block:: bash
+
+    mynamespace/
+    └── mycollection/
+      ├── docs/
+      ├── galaxy.yml
+      ├── plugins/
+      │   ├── modules/
+      │   │   └── module1.py
+      │   ├── inventory/
+      │   └── .../
+      ├── README.md
+      ├── roles/
+      │   ├── role1/
+      │   ├── role2/
+      │   └── .../
+      ├── playbooks/
+      │   ├── files/
+      │   ├── vars/
+      │   ├── templates/
+      │   └── tasks/
+      └── tests/
+
+You will need to use the Fully Qualified Collection Name (FQCN) to use the roles and plugins when you migrate your role into a collection. The FQCN is the combination of the collection ``namespace``, collection ``name``, and the content item you are referring to.
+
+So for example, in the above collection, the FQCN to access  ``role1`` would be:
+
+.. code-block:: Python
+
+	mynamespace.mycollection.role1
+
+
+A collection can contain one or more roles in the :file:`roles/` directory and these are almost identical to standalone roles, except you need to move plugins out of the individual roles, and use the :abbr:`FQCN (Fully Qualified Collection Name)` in some places, as detailed in the next section.
+
+.. note::
+
+	 In standalone roles, some of the plugin directories referenced their plugin types in the plural sense; this is not the case in collections.
+
+.. _simple_roles_in_collections:
+
+Migrating a role to a collection
+=================================
+
+To migrate from a standalone role (with no plugins) to a collection role:
+
+1. Create a local :file:`ansible_collections` directory and ``cd`` to this new directory.
+
+2. Create a collection. If you want to import this collection to Ansible Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+.. code-block:: bash
+
+  $ ansible-galaxy collection init mynamespace.mycollection
+
+This creates the collection directory structure.
+
+3. Copy the standalone role directory into the :file:`roles/` subdirectory of the collection. Roles in collections cannot have hyphens in the role name. Rename any such roles to use underscores instead.
+
+.. code-block:: bash
+
+  $ mkdir mynamespace/mycollection/roles/my_role/
+  $ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
+
+4. Update ``galaxy.yml`` to include any role dependencies.
+
+5. Update the collection README.md file to add links to any role README.md files.
+
+
+.. _complex_roles_in_collections:
+
+Migrating a role with plugins to a collection
+==============================================
+
+To migrate from a standalone role that has plugins to a collection role:
+
+1. Create a local :file:`ansible_collections directory` and ``cd`` to this new directory.
+
+2. Create a collection. If you want to import this collection to Ansible Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+.. code-block:: bash
+
+  $ ansible-galaxy collection init mynamespace.mycollection
+
+This creates the collection directory structure.
+
+3. Copy the standalone role directory into the :file:`roles/` subdirectory of the collection. Roles in collections cannot have hyphens in the role name. Rename any such roles to use underscores instead.
+
+.. code-block:: bash
+
+  $ mkdir mynamespace/mycollection/roles/my_role/
+  $ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
+
+
+4. Move any modules to the :file:`plugins/modules/` directory.
+
+.. code-block:: bash
+
+  $ mv -r mynamespace/mycollection/roles/my_role/library/\* mynamespace/mycollection/plugins/modules/
+
+5. Move any other plugins to the appropriate :file:`plugins/PLUGINTYPE/` directory.  See :ref:`migrating_plugins_collection` for additional steps that may be required.
+
+6. Update ``galaxy.yml`` to include any role dependencies.
+
+7. Update the collection README.md file to add links to any role README.md files.
+
+8. Change any references to the role to use the :abbr:`FQCN (Fully Qualified Collection Name)`.
+
+.. code-block:: yaml
+
+  ---
+  - name: example role by FQCN
+    hosts: some_host_pattern
+    tasks:
+      - name: import FQCN role from a collection
+        import_role:
+          name: mynamespace.mycollection.my_role
+
+
+You can alternately use the ``collections`` keyword to simplify this:
+
+.. code-block:: yaml
+
+  ---
+  - name: example role by FQCN
+    hosts: some_host_pattern
+    collections:
+      - mynamespace.mycollection
+    tasks:
+      - name: import role from a collection
+        import_role:
+          name: my_role
+
+
+.. _migrating_plugins_collection:
+
+Migrating other role plugins to a collection
+---------------------------------------------
+
+To migrate other role plugins to a collection:
+
+
+1. Move any nonmodule plugins to the appropriate :file:`plugins/PLUGINTYPE/` directory. The :file:`mynamespace/mycollection/plugins/README.md` file explains the types of plugins that the collection can contain within optionally created subdirectories.
+
+.. code-block:: bash
+
+  $ mv -r mynamespace/mycollection/roles/my_role/filter_plugins/\* mynamespace/mycollection/plugins/filter/
+
+2. Update documentation to use the FQCN. Plugins that use ``doc_fragments`` need to use FQCN (for example, ``mydocfrag`` becomes ``mynamespace.mycollection.mydocfrag``).
+
+3. Update relative imports work in collections to start with a period.  For example, :file:`./filename` and :file:`../asdfu/filestuff` works but :file:`filename` in same directory must be updated to :file:`./filename`.
+
+
+If you have a custom ``module_utils`` or import from ``__init__.py``, you must also:
+
+#. Change the Python namespace for custom ``module_utils`` to use the :abbr:`FQCN (Fully Qualified Collection Name)` along with the ``ansible_collections`` convention. See :ref:`update_module_utils_role`.
+
+#. Change how you import from ``__init__.py``. See :ref:`update_init_role`.
+
+
+.. _update_module_utils_role:
+
+Updating ``module_utils``
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If any of your custom modules use a custom module utility, once you migrate to a collection you cannot address the module utility in the top level ``ansible.module_utils`` Python namespace. Ansible does not merge content from collections into the the Ansible internal Python namespace. Update any Python import statements that refer to custom module utilities when you migrate your custom content to collections. See :ref:`module_utils in collections <collection_module_utils>` for more details.
+
+When coding with ``module_utils`` in a collection, the Python import statement needs to take into account the :abbr:`FQCN (Fully Qualified Collection Name)` along with the ``ansible_collections`` convention. The resulting Python import looks similar to the following example:
+
+.. code-block:: python
+
+  from ansible_collections.{namespace}.{collectionname}.plugins.module_utils.{util} import {something}
+
+.. note::
+
+	You need to follow the same rules in changing paths and using namespaced names for subclassed plugins.
+
+The following example code snippets show a Python and a PowerShell module using both default Ansible ``module_utils`` and those provided by a collection. In this example the namespace is ``ansible_example`` and the collection is ``community``.
+
+In the Python example the ``module_utils`` is ``helper`` and the :abbr:`FQCN (Fully Qualified Collection Name)` is ``ansible_example.community.plugins.module_utils.helper``:
+
+.. code-block:: python
+
+  from ansible.module_utils.basic import AnsibleModule
+  from ansible.module_utils._text import to_text
+  from ansible.module_utils.six.moves.urllib.parse import urlencode
+  from ansible.module_utils.six.moves.urllib.error import HTTPError
+  from ansible_collections.ansible_example.community.plugins.module_utils.helper import HelperRequest
+
+  argspec = dict(
+	  name=dict(required=True, type='str'),
+	  state=dict(choices=['present', 'absent'], required=True),
+  )
+
+  module = AnsibleModule(
+	  argument_spec=argspec,
+	  supports_check_mode=True
+  )
+
+  _request = HelperRequest(
+  	module,
+	  headers={"Content-Type": "application/json"},
+       data=data
+ )
+
+In the PowerShell example the ``module_utils`` is ``hyperv`` and the :abbr:`FQCN (Fully Qualified Collection Name)` is ``ansible_example.community.plugins.module_utils.hyperv``:
+
+.. code-block:: powershell
+
+  #!powershell
+  #AnsibleRequires -CSharpUtil Ansible.Basic
+  #AnsibleRequires -PowerShell ansible_collections.ansible_example.community.plugins.module_utils.hyperv
+
+  $spec = @{
+	  name = @{ required = $true; type = "str" }
+  	state = @{ required = $true; choices = @("present", "absent") }
+  }
+  $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
+
+  Invoke-HyperVFunction -Name $module.Params.name
+
+  $module.ExitJson()
+
+
+.. _update_init_role:
+
+Importing from __init__.py
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Because of the way that the CPython interpreter does imports, combined with the way the Ansible plugin loader works, if your custom embedded module or plugin requires importing something from an :file:`__init__.py` file, that also becomes part of your collection. You can either originate the content inside a standalone role or use the file name in the Python import statement. The following example is an :file:`__init__.py` file that is part of a callback plugin found inside a collection named ``ansible_example.community``.
+
+.. code-block:: python
+
+  from ansible_collections.ansible_example.community.plugins.callback.__init__ import CustomBaseClass
+
+
+Example: Migrating a standalone role with plugins to a collection
+-----------------------------------------------------------------
+
+In this example we have a standalone role called ``my-standalone-role.webapp`` to emulate a standalone role that contains dashes in the name (which is not valid in collections). This standalone role contains a custom module in the ``library/`` directory called ``manage_webserver``.
+
+.. code-block:: bash
+
+  my-standalone-role.webapp
+  ├── defaults
+  ├── files
+  ├── handlers
+  ├── library
+  ├── meta
+  ├── tasks
+  ├── templates
+  ├── tests
+  └── vars
+
+1. Create a new collection, for example, ``acme.webserver``:
+
+.. code-block:: bash
+
+  $ ansible-galaxy collection init acme.webserver
+  - Collection acme.webserver was created successfully
+  $ tree acme -d 1
+  acme
+  └── webserver
+	 ├── docs
+	 ├── plugins
+	 └── roles
+
+2. Create the ``webapp`` role inside the collection and copy all contents from the standalone role:
+
+.. code-block:: bash
+
+  $ mkdir acme/webserver/roles/webapp
+  $ cp my-standalone-role.webapp/* acme/webserver/roles/webapp/
+
+3. Move the ``manage_webserver`` module to its new home in ``acme/webserver/plugins/modules/``:
+
+.. code-block:: bash
+
+  $ cp my-standalone-role.webapp/library/manage_webserver.py acme/webserver/plugins/modules/manage.py
+
+.. note::
+
+  This example changed the original source file ``manage_webserver.py`` to the destination file ``manage.py``. This is optional but the :abbr:`FQCN (Fully Qualified Collection Name)` provides the ``webserver`` context as ``acme.webserver.manage``.
+
+4. Change ``manage_webserver`` to ``acme.webserver.manage`` in :file:`tasks/` files in the role ( for example, ``my-standalone-role.webapp/tasks/main.yml``) and any use of the original module name.
+
+.. note::
+
+  This name change is only required if you changed the original module name, but illustrates content referenced by :abbr:`FQCN (Fully Qualified Collection Name)` can offer context and in turn can make module and plugin names shorter. If you anticipate using these modules independent of the role, keep the original naming conventions. Users can add the  :ref:`collections keyword <collections_using_playbook>` in their playbooks. Typically roles are an abstraction layer and users won't use components of the role independently.
+
+
+Example: Supporting standalone roles and migrated collection roles in a downstream RPM
+---------------------------------------------------------------------------------------
+
+A standalone role can co-exist with its collection role counterpart (for example, as part of a support lifecycle of a product). This should only be done for a transition period, but these two can exist in downstream in packages such as RPMs. For example, the RHEL system roles could coexist with an `example of a RHEL system roles collection <https://github.com/maxamillion/collection-rhel-system-roles>`_ and provide existing backwards compatibility with the downstream RPM.
+
+This section walks through an example creating this coexistence in a downstream RPM and requires Ansible 2.9.0 or later.
+
+To deliver a role as both a standalone role and a collection role:
+
+#. Place the collection in  :file:`/usr/share/ansible/collections/ansible_collections/`.
+#. Copy the contents of the role inside the collection into a directory named after the standalone role and place the standalone role in  :file:`/usr/share/ansible/roles/`.
+
+All previously bundled modules and plugins used in the standalone role are now referenced by :abbr:`FQCN (Fully Qualified Collection Name)` so even though they are no longer embedded, they can be found from the collection contents.This is an example of how the content inside the collection is a unique entity and does not have to be bound to a role or otherwise. You could alternately create two separate collections: one for the modules and plugins and another for the standalone role to migrate to. The role must use the modules and plugins as :abbr:`FQCN (Fully Qualified Collection Name)`.
+
+The following is an example RPM spec file that accomplishes this using this example content:
+
+.. code-block:: text
+
+  Name: acme-ansible-content
+  Summary: Ansible Collection for deploying and configuring ACME webapp
+  Version: 1.0.0
+  Release: 1%{?dist}
+  License: GPLv3+
+  Source0: amce-webserver-1.0.0.tar.gz
+
+  Url: https://github.com/acme/webserver-ansible-collection
+  BuildArch: noarch
+
+  %global roleprefix my-standalone-role.
+  %global collection_namespace acme
+  %global collection_name webserver
+
+  %global collection_dir %{_datadir}/ansible/collections/ansible_collections/%{collection_namespace}/%{collection_name}
+
+  %description
+  Ansible Collection and standalone role (for backward compatibility and migration) to deploy, configure, and manage the ACME webapp software.
+
+  %prep
+  %setup -qc
+
+  %build
+
+  %install
+
+  mkdir -p %{buildroot}/%{collection_dir}
+  cp -r ./* %{buildroot}/%{collection_dir}/
+
+  mkdir -p %{buildroot}/%{_datadir}/ansible/roles
+  for role in %{buildroot}/%{collection_dir}/roles/*
+    do
+	   cp -pR ${role} %{buildroot}/%{_datadir}/ansible/roles/%{roleprefix}$(basename ${role})
+
+	   mkdir -p %{buildroot}/%{_pkgdocdir}/$(basename ${role})
+	   for docfile in README.md COPYING LICENSE
+	    do
+      	if [ -f ${role}/${docfile} ]
+    	    then
+          	cp -p ${role}/${docfile} %{buildroot}/%{_pkgdocdir}/$(basename ${role})/${docfile}
+      	fi
+	   done
+  done
+
+
+  %files
+  %dir %{_datadir}/ansible
+  %dir %{_datadir}/ansible/roles
+  %dir %{_datadir}/ansible/collections
+  %dir %{_datadir}/ansible/collections/ansible_collections
+  %{_datadir}/ansible/roles/
+  %doc %{_pkgdocdir}/*/README.md
+  %doc %{_datadir}/ansible/roles/%{roleprefix}*/README.md
+  %{collection_dir}
+  %doc %{collection_dir}/roles/*/README.md
+  %license %{_pkgdocdir}/*/COPYING
+  %license %{_pkgdocdir}/*/LICENSE
diff --git a/docs/docsite/rst/user_guide/collections_using.rst b/docs/docsite/rst/user_guide/collections_using.rst
index 1ea1b1acf3798c..eca3941e1b0fe9 100644
--- a/docs/docsite/rst/user_guide/collections_using.rst
+++ b/docs/docsite/rst/user_guide/collections_using.rst
@@ -233,6 +233,7 @@ In addition to the ``namespace.collection_name:version`` format, you can provide
 
 Verifying against ``tar.gz`` files is not supported. If your ``requirements.yml`` contains paths to tar files or URLs for installation, you can use the ``--ignore-errors`` flag to ensure that all collections using the ``namespace.name`` format in the file are processed.
 
+.. _collections_using_playbook:
 
 Using collections in a Playbook
 ===============================


Code review comments:
diff_hunk: @@ -0,0 +1,411 @@
+
+.. _migrating_roles:
+
+*************************************************
+Migrating Roles to Roles in Collections on Galaxy
+*************************************************
+
+You can migrate an existing standalone role into a collection-based role that you can host on Galaxy. This offers you more flexibility and resources to have your role within a collection. With Ansible collections on Galaxy, you can use a single collection as a delivery mechanism for many roles, releasing them as a single cohesive unit of re-usable automation. Inside a collection, custom plugins that used to be shipped in the role's :file:`library/`` directory can now be shared across all roles within a
+collection without duplicating them in each role's :file:`library/`` directory.
+
+You also need to migrate roles to collections if you want them as part of certified Ansible content.
+
+.. note::
+
+	If you want to import your collection to Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+See :ref:`developing_collections` for details on collections.
+
+
+.. contents::
+   :local:
+   :depth: 1
+
+Comparing standalone roles to collection roles
+===============================================
+
+:ref:`Standalone roles <playbooks_reuse_roles>` have the following directory structure:
+
+.. code-block:: bash
+  :emphasize-lines: 5,7,8
+
+    role/
+    ├── defaults
+    ├── files
+    ├── handlers
+    ├── library
+    ├── meta
+    ├── module_utils
+    ├── [*_plugins]
+    ├── tasks
+    ├── templates
+    ├── tests
+    └── vars
+
+
+The highlighted directories above will change when you migrate to a collection-based role. The collection directory structure includes a :file:`roles/` directory:
+
+.. code-block:: bash
+
+    mynamespace/
+    └── mycollection/
+      ├── docs/
+      ├── galaxy.yml
+      ├── plugins/
+      │   ├── modules/
+      │   │   └── module1.py
+      │   ├── inventory/
+      │   └── .../
+      ├── README.md
+      ├── roles/
+      │   ├── role1/
+      │   ├── role2/
+      │   └── .../
+      ├── playbooks/
+      │   ├── files/
+      │   ├── vars/
+      │   ├── templates/
+      │   └── tasks/
+      └── tests/
+
+You will need to use the Fully Qualified Collection Name (FQCN) to use the roles and plugins when you migrate your role into a collection. The FQCN is the combination of the collection ``namespace``, collection ``name``, and the content item you are referring to.
+
+So for example, in the above collection, the FQCN to access  ``role1`` would be:
+
+.. code-block:: Python
+
+	mynamespace.mycollection.role1
+
+
+A collection can contain one or more roles in the :file:`roles/` directory and these are almost identical to standalone roles, except you need to move plugins out of the individual roles, and use the :abbr:`FQCN (Fully Qualified Collection Name)` in some places, as detailed in the next section.
+
+.. note::
+
+	 In standalone roles, some of the plugin directories referenced their plugin types in the plural sense; this is not the case in collections.
+
+.. _simple_roles_in_collections:
+
+Migrating a role to a collection
+=================================
+
+To migrate from a standalone role (with no plugins) to a collection role:
+
+1. Create a local :file:`ansible_collections` directory and ``cd`` to this new directory.
+
+2. Create a collection. If you want to import this collection to Ansible Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+.. code-block:: bash
+
+  $ ansible-galaxy collection init mynamespace.mycollection
+
+This creates the collection directory structure.
+
+3. Copy the standalone role directory into the :file:`roles/` subdirectory of the collection. Roles in collections cannot have hyphens in the role name. Rename any such roles to use underscores instead.
+
+.. code-block:: bash
+
+  $ mkdir mynamespace/mycollection/roles/my_role/
+  $ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
+
+4. Update ``galaxy.yml`` to include any role dependencies.
+
+5. Update the collection README.md file to add links to any role README.md files.
+
+
+.. _complex_roles_in_collections:
+
+Migrating a role with plugins to a collection
+==============================================
+
+To migrate from a standalone role that has plugins to a collection role:
+
+1. Create a local :file:`ansible_collections directory` and ``cd`` to this new directory.
+
+2. Create a collection. If you want to import this collection to Ansible Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+.. code-block:: bash
+
+  $ ansible-galaxy collection init mynamespace.mycollection
+
+This creates the collection directory structure.
+
+3. Copy the standalone role directory into the :file:`roles/` subdirectory of the collection. Roles in collections cannot have hyphens in the role name. Rename any such roles to use underscores instead.
+
+.. code-block:: bash
+
+  $ mkdir mynamespace/mycollection/roles/my_role/
+  $ cp -r /path/to/standalone/role/mynamespace/my_role/\* mynamespace/mycollection/roles/my_role/
+
+
+4. Move any modules to the :file:`plugins/modules/` directory.
+
+.. code-block:: bash
+
+  $ mv -r mynamespace/mycollection/roles/my_role/library/\* mynamespace/mycollection/plugins/modules/
+
+5. Move any other plugins to the appropriate :file:`plugins/PLUGINTYPE/` directory.  See :ref:`migrating_plugins_collection` for additional steps that may be required.
+
+6. Update ``galaxy.yml`` to include any role dependencies.
+
+7. Update the collection README.md file to add links to any role README.md files.
+
+8. Change any references to the role to use the :abbr:`FQCN (Fully Qualified Collection Name)`.
+
+.. code-block:: yaml
+
+  ---
+  - name: example role by FQCN
+    hosts: some_host_pattern
+    tasks:
+      - name: import FQCN role from a collection
+        import_role:
+          name: mynamespace.mycollection.my_role
+
+
+You can alternately use the ``collections`` keyword to simplify this:
+
+.. code-block:: yaml
+
+  ---
+  - name: example role by FQCN
+    hosts: some_host_pattern
+    collections:
+      - mynamespace.mycollection
+    tasks:
+      - name: import role from a collection
+        import_role:
+          name: my_role
+
+
+.. _migrating_plugins_collection:
+
+Migrating other role plugins to a collection
+---------------------------------------------
+
+To migrate other role plugins to a collection:
+
+
+1. Move any nonmodule plugins to the appropriate :file:`plugins/PLUGINTYPE/` directory. The :file:`mynamespace/mycollection/plugins/README.md` file explains the types of plugins that the collection can contain within optionally created subdirectories.
path: docs/docsite/rst/dev_guide/migrating_roles.rst
text: @user1:
```suggestion
1. Move each nonmodule plugins to the appropriate :file:`plugins/PLUGINTYPE/` directory. The :file:`mynamespace/mycollection/plugins/README.md` file explains the types of plugins that the collection can contain within optionally created subdirectories.
```

diff_hunk: @@ -0,0 +1,411 @@
+
+.. _migrating_roles:
+
+*************************************************
+Migrating Roles to Roles in Collections on Galaxy
+*************************************************
+
+You can migrate an existing standalone role into a collection-based role that you can host on Galaxy. This offers you more flexibility and resources to have your role within a collection. With Ansible collections on Galaxy, you can use a single collection as a delivery mechanism for many roles, releasing them as a single cohesive unit of re-usable automation. Inside a collection, custom plugins that used to be shipped in the role's :file:`library/`` directory can now be shared across all roles within a
+collection without duplicating them in each role's :file:`library/`` directory.
+
+You also need to migrate roles to collections if you want them as part of certified Ansible content.
+
+.. note::
+
+	If you want to import your collection to Galaxy, you need a `Galaxy namespace <https://galaxy.ansible.com/docs/contributing/namespaces.html>`_.
+
+See :ref:`developing_collections` for details on collections.
+
+
+.. contents::
+   :local:
+   :depth: 1
+
+Comparing standalone roles to collection roles
+===============================================
+
+:ref:`Standalone roles <playbooks_reuse_roles>` have the following directory structure:
+
+.. code-block:: bash
+  :emphasize-lines: 5,7,8
+
+    role/
+    ├── defaults
+    ├── files
+    ├── handlers
+    ├── library
+    ├── meta
+    ├── module_utils
+    ├── [*_plugins]
+    ├── tasks
+    ├── templates
+    ├── tests
+    └── vars
+
+
+The highlighted directories above will change when you migrate to a collection-based role. The collection directory structure includes a :file:`roles/` directory:
+
+.. code-block:: bash
+
+    mynamespace/
+    └── mycollection/
+      ├── docs/
+      ├── galaxy.yml
+      ├── plugins/
+      │   ├── modules/
+      │   │   └── module1.py
+      │   ├── inventory/
+      │   └── .../
+      ├── README.md
+      ├── roles/
+      │   ├── role1/
+      │   ├── role2/
+      │   └── .../
+      ├── playbooks/
+      │   ├── files/
+      │   ├── vars/
+      │   ├── templates/
+      │   └── tasks/
+      └── tests/
+
+You will need to use the Fully Qualified Collection Name (FQCN) to use the roles and plugins when you migrate your role into a collection. The FQCN is the combination of the collection ``namespace``, collection ``name``, and the content item you are referring to.
+
+So for example, in the above collection, the FQCN to access  ``role1`` would be:
+
+.. code-block:: Python
+
+	mynamespace.mycollection.role1
+
+
+A collection can contain one or more roles in the :file:`roles/` directory and these are almost identical to standalone roles, except you need to move plugins out of the individual roles, and use the :abbr:`FQCN (Fully Qualified Collection Name)` in some places, as detailed in the next section.
+
+.. note::
+
+	 In standalone roles, some of the plugin directories referenced their plugin types in the plural sense; this is not the case in collections.
+
+.. _simple_roles_in_collections:
+
+Migrating a role to a collection
+=================================
+
+To migrate from a standalone role (with no plugins) to a collection role:
path: docs/docsite/rst/dev_guide/migrating_roles.rst
text: @user1:
```suggestion
To migrate from a standalone role that contains no plugins to a collection role:
```