diff --git a/.azure-pipelines/README.md b/.azure-pipelines/README.md
deleted file mode 100644
index 9e8ad74104..0000000000
--- a/.azure-pipelines/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-<!--
-Copyright (c) Ansible Project
-GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-SPDX-License-Identifier: GPL-3.0-or-later
--->
-
-## Azure Pipelines Configuration
-
-Please see the [Documentation](https://github.com/ansible/community/wiki/Testing:-Azure-Pipelines) for more information.
diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml
deleted file mode 100644
index a66ac551f8..0000000000
--- a/.azure-pipelines/azure-pipelines.yml
+++ /dev/null
@@ -1,258 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-trigger:
-  batch: true
-  branches:
-    include:
-      - main
-      - stable-*
-
-pr:
-  autoCancel: true
-  branches:
-    include:
-      - main
-      - stable-*
-
-schedules:
-  - cron: 0 8 * * *
-    displayName: Nightly (main)
-    always: true
-    branches:
-      include:
-        - main
-  - cron: 0 10 * * *
-    displayName: Nightly (active stable branches)
-    always: true
-    branches:
-      include:
-        - stable-7
-        - stable-6
-  - cron: 0 11 * * 0
-    displayName: Weekly (old stable branches)
-    always: true
-    branches:
-      include:
-        - stable-5
-
-variables:
-  - name: checkoutPath
-    value: ansible_collections/community/general
-  - name: coverageBranches
-    value: main
-  - name: pipelinesCoverage
-    value: coverage
-  - name: entryPoint
-    value: tests/utils/shippable/shippable.sh
-  - name: fetchDepth
-    value: 0
-
-resources:
-  containers:
-    - container: default
-      image: quay.io/ansible/azure-pipelines-test-container:4.0.1
-
-pool: Standard
-
-stages:
-### Sanity
-  - stage: Sanity_2_15
-    displayName: Sanity 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Test {0}
-          testFormat: 2.15/sanity/{0}
-          targets:
-            - test: 1
-            - test: 2
-            - test: 3
-            - test: 4
-            - test: extra
-  - stage: Sanity_2_14
-    displayName: Sanity 2.14
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Test {0}
-          testFormat: 2.14/sanity/{0}
-          targets:
-            - test: 1
-            - test: 2
-            - test: 3
-            - test: 4
-### Units
-  - stage: Units_2_15
-    displayName: Units 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Python {0}
-          testFormat: 2.15/units/{0}/1
-          targets:
-            - test: 2.7
-            - test: 3.5
-            - test: 3.6
-            - test: 3.7
-            - test: 3.8
-            - test: 3.9
-            - test: '3.10'
-            - test: '3.11'
-  - stage: Units_2_14
-    displayName: Units 2.14
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Python {0}
-          testFormat: 2.14/units/{0}/1
-          targets:
-            - test: 3.9
-
-## Remote
-  - stage: Remote_2_15
-    displayName: Remote 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          testFormat: 2.15/{0}
-          targets:
-            - name: macOS 13.2
-              test: macos/13.2
-            - name: RHEL 9.1
-              test: rhel/9.1
-            - name: RHEL 7.9
-              test: rhel/7.9
-            - name: FreeBSD 13.1
-              test: freebsd/13.1
-            - name: FreeBSD 12.4
-              test: freebsd/12.4
-          groups:
-            - 1
-            - 2
-            - 3
-  - stage: Remote_2_14
-    displayName: Remote 2.14
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          testFormat: 2.14/{0}
-          targets:
-            - name: RHEL 9.0
-              test: rhel/9.0
-            #- name: FreeBSD 12.4
-            #  test: freebsd/12.4
-          groups:
-            - 1
-            - 2
-            - 3
-
-### Docker
-  - stage: Docker_2_15
-    displayName: Docker 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          testFormat: 2.15/linux/{0}
-          targets:
-            - name: Fedora 37
-              test: fedora37
-            - name: openSUSE 15
-              test: opensuse15
-            - name: Ubuntu 20.04
-              test: ubuntu2004
-            - name: Ubuntu 22.04
-              test: ubuntu2204
-            - name: Alpine 3
-              test: alpine3
-            - name: CentOS 7
-              test: centos7
-          groups:
-            - 1
-            - 2
-            - 3
-  - stage: Docker_2_14
-    displayName: Docker 2.14
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          testFormat: 2.14/linux/{0}
-          targets:
-            - name: Fedora 36
-              test: fedora36
-          groups:
-            - 1
-            - 2
-            - 3
-
-### Community Docker
-  - stage: Docker_community_2_15
-    displayName: Docker (community images) 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          testFormat: 2.15/linux-community/{0}
-          targets:
-            - name: Debian Bullseye
-              test: debian-bullseye/3.9
-            - name: ArchLinux
-              test: archlinux/3.11
-            - name: CentOS Stream 8
-              test: centos-stream8/3.9
-          groups:
-            - 1
-            - 2
-            - 3
-
-### Generic
-  - stage: Generic_2_15
-    displayName: Generic 2.15
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Python {0}
-          testFormat: 2.15/generic/{0}/1
-          targets:
-            - test: 2.7
-            - test: 3.9
-            - test: '3.11'
-  - stage: Generic_2_14
-    displayName: Generic 2.14
-    dependsOn: []
-    jobs:
-      - template: templates/matrix.yml
-        parameters:
-          nameFormat: Python {0}
-          testFormat: 2.14/generic/{0}/1
-          targets:
-            - test: '3.10'
-
-  - stage: Summary
-    condition: succeededOrFailed()
-    dependsOn:
-      - Sanity_2_14
-      - Sanity_2_15
-      - Units_2_14
-      - Units_2_15
-      - Remote_2_14
-      - Remote_2_15
-      - Docker_2_14
-      - Docker_2_15
-      - Docker_community_2_15
-# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
-#      - Generic_2_14
-#      - Generic_2_15
-    jobs:
-      - template: templates/coverage.yml
diff --git a/.azure-pipelines/scripts/aggregate-coverage.sh b/.azure-pipelines/scripts/aggregate-coverage.sh
deleted file mode 100755
index ca2b19de97..0000000000
--- a/.azure-pipelines/scripts/aggregate-coverage.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# Aggregate code coverage results for later processing.
-
-set -o pipefail -eu
-
-agent_temp_directory="$1"
-
-PATH="${PWD}/bin:${PATH}"
-
-mkdir "${agent_temp_directory}/coverage/"
-
-options=(--venv --venv-system-site-packages --color -v)
-
-ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}"
-
-if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then
-    # Only analyze coverage if the installed version of ansible-test supports it.
-    # Doing so allows this script to work unmodified for multiple Ansible versions.
-    ansible-test coverage analyze targets generate "${agent_temp_directory}/coverage/coverage-analyze-targets.json" "${options[@]}"
-fi
diff --git a/.azure-pipelines/scripts/combine-coverage.py b/.azure-pipelines/scripts/combine-coverage.py
deleted file mode 100755
index 3b2fd993db..0000000000
--- a/.azure-pipelines/scripts/combine-coverage.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-"""
-Combine coverage data from multiple jobs, keeping the data only from the most recent attempt from each job.
-Coverage artifacts must be named using the format: "Coverage $(System.JobAttempt) {StableUniqueNameForEachJob}"
-The recommended coverage artifact name format is: Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)
-Keep in mind that Azure Pipelines does not enforce unique job display names (only names).
-It is up to pipeline authors to avoid name collisions when deviating from the recommended format.
-"""
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import os
-import re
-import shutil
-import sys
-
-
-def main():
-    """Main program entry point."""
-    source_directory = sys.argv[1]
-
-    if '/ansible_collections/' in os.getcwd():
-        output_path = "tests/output"
-    else:
-        output_path = "test/results"
-
-    destination_directory = os.path.join(output_path, 'coverage')
-
-    if not os.path.exists(destination_directory):
-        os.makedirs(destination_directory)
-
-    jobs = {}
-    count = 0
-
-    for name in os.listdir(source_directory):
-        match = re.search('^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$', name)
-        label = match.group('label')
-        attempt = int(match.group('attempt'))
-        jobs[label] = max(attempt, jobs.get(label, 0))
-
-    for label, attempt in jobs.items():
-        name = 'Coverage {attempt} {label}'.format(label=label, attempt=attempt)
-        source = os.path.join(source_directory, name)
-        source_files = os.listdir(source)
-
-        for source_file in source_files:
-            source_path = os.path.join(source, source_file)
-            destination_path = os.path.join(destination_directory, source_file + '.' + label)
-            print('"%s" -> "%s"' % (source_path, destination_path))
-            shutil.copyfile(source_path, destination_path)
-            count += 1
-
-    print('Coverage file count: %d' % count)
-    print('##vso[task.setVariable variable=coverageFileCount]%d' % count)
-    print('##vso[task.setVariable variable=outputPath]%s' % output_path)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.azure-pipelines/scripts/process-results.sh b/.azure-pipelines/scripts/process-results.sh
deleted file mode 100755
index 1f4b8e4f10..0000000000
--- a/.azure-pipelines/scripts/process-results.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# Check the test results and set variables for use in later steps.
-
-set -o pipefail -eu
-
-if [[ "$PWD" =~ /ansible_collections/ ]]; then
-    output_path="tests/output"
-else
-    output_path="test/results"
-fi
-
-echo "##vso[task.setVariable variable=outputPath]${output_path}"
-
-if compgen -G "${output_path}"'/junit/*.xml' > /dev/null; then
-    echo "##vso[task.setVariable variable=haveTestResults]true"
-fi
-
-if compgen -G "${output_path}"'/bot/ansible-test-*' > /dev/null; then
-    echo "##vso[task.setVariable variable=haveBotResults]true"
-fi
-
-if compgen -G "${output_path}"'/coverage/*' > /dev/null; then
-    echo "##vso[task.setVariable variable=haveCoverageData]true"
-fi
diff --git a/.azure-pipelines/scripts/publish-codecov.py b/.azure-pipelines/scripts/publish-codecov.py
deleted file mode 100755
index 58e32f6d37..0000000000
--- a/.azure-pipelines/scripts/publish-codecov.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-"""
-Upload code coverage reports to codecov.io.
-Multiple coverage files from multiple languages are accepted and aggregated after upload.
-Python coverage, as well as PowerShell and Python stubs can all be uploaded.
-"""
-
-import argparse
-import dataclasses
-import pathlib
-import shutil
-import subprocess
-import tempfile
-import typing as t
-import urllib.request
-
-
-@dataclasses.dataclass(frozen=True)
-class CoverageFile:
-    name: str
-    path: pathlib.Path
-    flags: t.List[str]
-
-
-@dataclasses.dataclass(frozen=True)
-class Args:
-    dry_run: bool
-    path: pathlib.Path
-
-
-def parse_args() -> Args:
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-n', '--dry-run', action='store_true')
-    parser.add_argument('path', type=pathlib.Path)
-
-    args = parser.parse_args()
-
-    # Store arguments in a typed dataclass
-    fields = dataclasses.fields(Args)
-    kwargs = {field.name: getattr(args, field.name) for field in fields}
-
-    return Args(**kwargs)
-
-
-def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
-    processed = []
-    for file in directory.joinpath('reports').glob('coverage*.xml'):
-        name = file.stem.replace('coverage=', '')
-
-        # Get flags from name
-        flags = name.replace('-powershell', '').split('=')  # Drop '-powershell' suffix
-        flags = [flag if not flag.startswith('stub') else flag.split('-')[0] for flag in flags]  # Remove "-01" from stub files
-
-        processed.append(CoverageFile(name, file, flags))
-
-    return tuple(processed)
-
-
-def upload_files(codecov_bin: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None:
-    for file in files:
-        cmd = [
-            str(codecov_bin),
-            '--name', file.name,
-            '--file', str(file.path),
-        ]
-        for flag in file.flags:
-            cmd.extend(['--flags', flag])
-
-        if dry_run:
-            print(f'DRY-RUN: Would run command: {cmd}')
-            continue
-
-        subprocess.run(cmd, check=True)
-
-
-def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None:
-    if dry_run:
-        print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
-        return
-
-    with urllib.request.urlopen(url) as resp:
-        with dest.open('w+b') as f:
-            # Read data in chunks rather than all at once
-            shutil.copyfileobj(resp, f, 64 * 1024)
-
-    dest.chmod(flags)
-
-
-def main():
-    args = parse_args()
-    url = 'https://ansible-ci-files.s3.amazonaws.com/codecov/linux/codecov'
-    with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
-        codecov_bin = pathlib.Path(tmpdir) / 'codecov'
-        download_file(url, codecov_bin, 0o755, args.dry_run)
-
-        files = process_files(args.path)
-        upload_files(codecov_bin, files, args.dry_run)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.azure-pipelines/scripts/report-coverage.sh b/.azure-pipelines/scripts/report-coverage.sh
deleted file mode 100755
index c08154b6f8..0000000000
--- a/.azure-pipelines/scripts/report-coverage.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# Generate code coverage reports for uploading to Azure Pipelines and codecov.io.
-
-set -o pipefail -eu
-
-PATH="${PWD}/bin:${PATH}"
-
-if ! ansible-test --help >/dev/null 2>&1; then
-    # Install the devel version of ansible-test for generating code coverage reports.
-    # This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs).
-    # Since a version of ansible-test is required that can work the output from multiple older releases, the devel version is used.
-    pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
-fi
-
-ansible-test coverage xml --group-by command --stub --venv --venv-system-site-packages --color -v
diff --git a/.azure-pipelines/scripts/run-tests.sh b/.azure-pipelines/scripts/run-tests.sh
deleted file mode 100755
index 2cfdcf61ef..0000000000
--- a/.azure-pipelines/scripts/run-tests.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# Configure the test environment and run the tests.
-
-set -o pipefail -eu
-
-entry_point="$1"
-test="$2"
-read -r -a coverage_branches <<< "$3"  # space separated list of branches to run code coverage on for scheduled builds
-
-export COMMIT_MESSAGE
-export COMPLETE
-export COVERAGE
-export IS_PULL_REQUEST
-
-if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then
-    IS_PULL_REQUEST=true
-    COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD^2)
-else
-    IS_PULL_REQUEST=
-    COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD)
-fi
-
-COMPLETE=
-COVERAGE=
-
-if [ "${BUILD_REASON}" = "Schedule" ]; then
-    COMPLETE=yes
-
-    if printf '%s\n' "${coverage_branches[@]}" | grep -q "^${BUILD_SOURCEBRANCHNAME}$"; then
-        COVERAGE=yes
-    fi
-fi
-
-"${entry_point}" "${test}" 2>&1 | "$(dirname "$0")/time-command.py"
diff --git a/.azure-pipelines/scripts/time-command.py b/.azure-pipelines/scripts/time-command.py
deleted file mode 100755
index 85a7c3c171..0000000000
--- a/.azure-pipelines/scripts/time-command.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-"""Prepends a relative timestamp to each input line from stdin and writes it to stdout."""
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import sys
-import time
-
-
-def main():
-    """Main program entry point."""
-    start = time.time()
-
-    sys.stdin.reconfigure(errors='surrogateescape')
-    sys.stdout.reconfigure(errors='surrogateescape')
-
-    for line in sys.stdin:
-        seconds = time.time() - start
-        sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line))
-        sys.stdout.flush()
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.azure-pipelines/templates/coverage.yml b/.azure-pipelines/templates/coverage.yml
deleted file mode 100644
index 3c8841aa26..0000000000
--- a/.azure-pipelines/templates/coverage.yml
+++ /dev/null
@@ -1,44 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# This template adds a job for processing code coverage data.
-# It will upload results to Azure Pipelines and codecov.io.
-# Use it from a job stage that completes after all other jobs have completed.
-# This can be done by placing it in a separate summary stage that runs after the test stage(s) have completed.
-
-jobs:
-  - job: Coverage
-    displayName: Code Coverage
-    container: default
-    workspace:
-      clean: all
-    steps:
-      - checkout: self
-        fetchDepth: $(fetchDepth)
-        path: $(checkoutPath)
-      - task: DownloadPipelineArtifact@2
-        displayName: Download Coverage Data
-        inputs:
-          path: coverage/
-          patterns: "Coverage */*=coverage.combined"
-      - bash: .azure-pipelines/scripts/combine-coverage.py coverage/
-        displayName: Combine Coverage Data
-      - bash: .azure-pipelines/scripts/report-coverage.sh
-        displayName: Generate Coverage Report
-        condition: gt(variables.coverageFileCount, 0)
-      - task: PublishCodeCoverageResults@1
-        inputs:
-          codeCoverageTool: Cobertura
-          # Azure Pipelines only accepts a single coverage data file.
-          # That means only Python or PowerShell coverage can be uploaded, but not both.
-          # Set the "pipelinesCoverage" variable to determine which type is uploaded.
-          # Use "coverage" for Python and "coverage-powershell" for PowerShell.
-          summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
-        displayName: Publish to Azure Pipelines
-        condition: gt(variables.coverageFileCount, 0)
-      - bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
-        displayName: Publish to codecov.io
-        condition: gt(variables.coverageFileCount, 0)
-        continueOnError: true
diff --git a/.azure-pipelines/templates/matrix.yml b/.azure-pipelines/templates/matrix.yml
deleted file mode 100644
index 4876375855..0000000000
--- a/.azure-pipelines/templates/matrix.yml
+++ /dev/null
@@ -1,60 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# This template uses the provided targets and optional groups to generate a matrix which is then passed to the test template.
-# If this matrix template does not provide the required functionality, consider using the test template directly instead.
-
-parameters:
-  # A required list of dictionaries, one per test target.
-  # Each item in the list must contain a "test" or "name" key.
-  # Both may be provided. If one is omitted, the other will be used.
-  - name: targets
-    type: object
-
-  # An optional list of values which will be used to multiply the targets list into a matrix.
-  # Values can be strings or numbers.
-  - name: groups
-    type: object
-    default: []
-
-  # An optional format string used to generate the job name.
-  # - {0} is the name of an item in the targets list.
-  - name: nameFormat
-    type: string
-    default: "{0}"
-
-  # An optional format string used to generate the test name.
-  # - {0} is the name of an item in the targets list.
-  - name: testFormat
-    type: string
-    default: "{0}"
-
-  # An optional format string used to add the group to the job name.
-  # {0} is the formatted name of an item in the targets list.
-  # {{1}} is the group -- be sure to include the double "{{" and "}}".
-  - name: nameGroupFormat
-    type: string
-    default: "{0} - {{1}}"
-
-  # An optional format string used to add the group to the test name.
-  # {0} is the formatted test of an item in the targets list.
-  # {{1}} is the group -- be sure to include the double "{{" and "}}".
-  - name: testGroupFormat
-    type: string
-    default: "{0}/{{1}}"
-
-jobs:
-  - template: test.yml
-    parameters:
-      jobs:
-        - ${{ if eq(length(parameters.groups), 0) }}:
-          - ${{ each target in parameters.targets }}:
-            - name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }}
-              test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }}
-        - ${{ if not(eq(length(parameters.groups), 0)) }}:
-          - ${{ each group in parameters.groups }}:
-            - ${{ each target in parameters.targets }}:
-              - name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }}
-                test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }}
diff --git a/.azure-pipelines/templates/test.yml b/.azure-pipelines/templates/test.yml
deleted file mode 100644
index 700cf629d7..0000000000
--- a/.azure-pipelines/templates/test.yml
+++ /dev/null
@@ -1,50 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# This template uses the provided list of jobs to create test one or more test jobs.
-# It can be used directly if needed, or through the matrix template.
-
-parameters:
-  # A required list of dictionaries, one per test job.
-  # Each item in the list must contain a "job" and "name" key.
-  - name: jobs
-    type: object
-
-jobs:
-  - ${{ each job in parameters.jobs }}:
-    - job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }}
-      displayName: ${{ job.name }}
-      container: default
-      workspace:
-        clean: all
-      steps:
-        - checkout: self
-          fetchDepth: $(fetchDepth)
-          path: $(checkoutPath)
-        - bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
-          displayName: Run Tests
-        - bash: .azure-pipelines/scripts/process-results.sh
-          condition: succeededOrFailed()
-          displayName: Process Results
-        - bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
-          condition: eq(variables.haveCoverageData, 'true')
-          displayName: Aggregate Coverage Data
-        - task: PublishTestResults@2
-          condition: eq(variables.haveTestResults, 'true')
-          inputs:
-            testResultsFiles: "$(outputPath)/junit/*.xml"
-          displayName: Publish Test Results
-        - task: PublishPipelineArtifact@1
-          condition: eq(variables.haveBotResults, 'true')
-          displayName: Publish Bot Results
-          inputs:
-            targetPath: "$(outputPath)/bot/"
-            artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
-        - task: PublishPipelineArtifact@1
-          condition: eq(variables.haveCoverageData, 'true')
-          displayName: Publish Coverage Data
-          inputs:
-            targetPath: "$(Agent.TempDirectory)/coverage/"
-            artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml
deleted file mode 100644
index 81ac52a2f7..0000000000
--- a/.github/workflows/ansible-test.yml
+++ /dev/null
@@ -1,287 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-# For the comprehensive list of the inputs supported by the ansible-community/ansible-test-gh-action GitHub Action, see
-# https://github.com/marketplace/actions/ansible-test
-
-name: EOL CI
-on:
-  # Run EOL CI against all pushes (direct commits, also merged PRs), Pull Requests
-  push:
-    branches:
-      - main
-      - stable-*
-  pull_request:
-  # Run EOL CI once per week (at 11:00 UTC)
-  schedule:
-    - cron: '0 11 * * 0'
-
-concurrency:
-  # Make sure there is at most one active run per PR, but do not cancel any non-PR runs
-  group: ${{ github.workflow }}-${{ (github.head_ref && github.event.number) || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  sanity:
-    name: EOL Sanity (Ⓐ${{ matrix.ansible }})
-    strategy:
-      matrix:
-        ansible:
-          - '2.11'
-          - '2.12'
-          - '2.13'
-    # Ansible-test on various stable branches does not yet work well with cgroups v2.
-    # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
-    # image for these stable branches. The list of branches where this is necessary will
-    # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
-    # for the latest list.
-    runs-on: >-
-      ${{ contains(fromJson(
-          '["2.9", "2.10", "2.11"]'
-      ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
-    steps:
-      - name: Perform sanity testing
-        uses: felixfontein/ansible-test-gh-action@main
-        with:
-          ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
-          ansible-core-version: stable-${{ matrix.ansible }}
-          coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
-          pull-request-change-detection: 'true'
-          testing-type: sanity
-
-  units:
-    # Ansible-test on various stable branches does not yet work well with cgroups v2.
-    # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
-    # image for these stable branches. The list of branches where this is necessary will
-    # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
-    # for the latest list.
-    runs-on: >-
-      ${{ contains(fromJson(
-          '["2.9", "2.10", "2.11"]'
-      ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
-    name: EOL Units (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }})
-    strategy:
-      # As soon as the first unit test fails, cancel the others to free up the CI queue
-      fail-fast: true
-      matrix:
-        ansible:
-          - ''
-        python:
-          - ''
-        exclude:
-          - ansible: ''
-        include:
-          - ansible: '2.11'
-            python: '2.7'
-          - ansible: '2.11'
-            python: '3.5'
-          - ansible: '2.12'
-            python: '2.6'
-          - ansible: '2.12'
-            python: '3.8'
-          - ansible: '2.13'
-            python: '2.7'
-          - ansible: '2.13'
-            python: '3.8'
-
-    steps:
-      - name: >-
-          Perform unit testing against
-          Ansible version ${{ matrix.ansible }}
-        uses: felixfontein/ansible-test-gh-action@main
-        with:
-          ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
-          ansible-core-version: stable-${{ matrix.ansible }}
-          coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
-          pre-test-cmd: >-
-            mkdir -p ../../ansible
-            ;
-            git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools
-          pull-request-change-detection: 'true'
-          target-python-version: ${{ matrix.python }}
-          testing-type: units
-
-  integration:
-    # Ansible-test on various stable branches does not yet work well with cgroups v2.
-    # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
-    # image for these stable branches. The list of branches where this is necessary will
-    # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
-    # for the latest list.
-    runs-on: >-
-      ${{ contains(fromJson(
-          '["2.9", "2.10", "2.11"]'
-      ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
-    name: EOL I (Ⓐ${{ matrix.ansible }}+${{ matrix.docker }}+py${{ matrix.python }}:${{ matrix.target }})
-    strategy:
-      fail-fast: false
-      matrix:
-        ansible:
-          - ''
-        docker:
-          - ''
-        python:
-          - ''
-        target:
-          - ''
-        exclude:
-          - ansible: ''
-        include:
-          # 2.11
-          - ansible: '2.11'
-            docker: fedora32
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.11'
-            docker: fedora32
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.11'
-            docker: fedora32
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.11'
-            docker: fedora33
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.11'
-            docker: fedora33
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.11'
-            docker: fedora33
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.11'
-            docker: alpine3
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.11'
-            docker: alpine3
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.11'
-            docker: alpine3
-            python: ''
-            target: azp/posix/3/
-          # Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
-          # - ansible: '2.11'
-          #   docker: default
-          #   python: '2.7'
-          #   target: azp/generic/1/
-          # - ansible: '2.11'
-          #   docker: default
-          #   python: '3.5'
-          #   target: azp/generic/1/
-          # 2.12
-          - ansible: '2.12'
-            docker: centos6
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.12'
-            docker: centos6
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.12'
-            docker: centos6
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.12'
-            docker: fedora34
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.12'
-            docker: fedora34
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.12'
-            docker: fedora34
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.12'
-            docker: ubuntu1804
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.12'
-            docker: ubuntu1804
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.12'
-            docker: ubuntu1804
-            python: ''
-            target: azp/posix/3/
-          # Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
-          # - ansible: '2.12'
-          #   docker: default
-          #   python: '3.8'
-          #   target: azp/generic/1/
-          # 2.13
-          - ansible: '2.13'
-            docker: fedora35
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.13'
-            docker: fedora35
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.13'
-            docker: fedora35
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.13'
-            docker: opensuse15py2
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.13'
-            docker: opensuse15py2
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.13'
-            docker: opensuse15py2
-            python: ''
-            target: azp/posix/3/
-          - ansible: '2.13'
-            docker: alpine3
-            python: ''
-            target: azp/posix/1/
-          - ansible: '2.13'
-            docker: alpine3
-            python: ''
-            target: azp/posix/2/
-          - ansible: '2.13'
-            docker: alpine3
-            python: ''
-            target: azp/posix/3/
-          # Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
-          # - ansible: '2.13'
-          #   docker: default
-          #   python: '3.9'
-          #   target: azp/generic/1/
-
-    steps:
-      - name: >-
-          Perform integration testing against
-          Ansible version ${{ matrix.ansible }}
-          under Python ${{ matrix.python }}
-        uses: felixfontein/ansible-test-gh-action@main
-        with:
-          ansible-core-github-repository-slug: ${{ contains(fromJson('["2.10", "2.11"]'), matrix.ansible) && 'felixfontein/ansible' || 'ansible/ansible' }}
-          ansible-core-version: stable-${{ matrix.ansible }}
-          coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }}
-          docker-image: ${{ matrix.docker }}
-          integration-continue-on-error: 'false'
-          integration-diff: 'false'
-          integration-retry-on-error: 'true'
-          pre-test-cmd: >-
-            mkdir -p ../../ansible
-            ;
-            git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git ../../ansible/posix
-            ;
-            git clone --depth=1 --single-branch https://github.com/ansible-collections/community.crypto.git ../../community/crypto
-            ;
-            git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools
-          pull-request-change-detection: 'true'
-          target: ${{ matrix.target }}
-          target-python-version: ${{ matrix.python }}
-          testing-type: integration
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index f7ab9450cc..0000000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,61 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-name: "Code scanning - action"
-
-on:
-  schedule:
-    - cron: '26 19 * * 1'
-
-permissions:
-  contents: read
-
-jobs:
-  CodeQL-Build:
-
-    permissions:
-      actions: read  # for github/codeql-action/init to get workflow details
-      contents: read  # for actions/checkout to fetch code
-      security-events: write  # for github/codeql-action/autobuild to send a status report
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Checkout repository
-      uses: actions/checkout@v3
-      with:
-        # We must fetch at least the immediate parents so that if this is
-        # a pull request then we can checkout the head.
-        fetch-depth: 2
-
-    # If this run was triggered by a pull request event, then checkout
-    # the head of the pull request instead of the merge commit.
-    - run: git checkout HEAD^2
-      if: ${{ github.event_name == 'pull_request' }}
-
-    # Initializes the CodeQL tools for scanning.
-    - name: Initialize CodeQL
-      uses: github/codeql-action/init@v2
-      # Override language selection by uncommenting this and choosing your languages
-      # with:
-      #   languages: go, javascript, csharp, python, cpp, java
-
-    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
-    # If this step fails, then you should remove it and run the build manually (see below)
-    - name: Autobuild
-      uses: github/codeql-action/autobuild@v2
-
-    # ℹ️ Command-line programs to run using the OS shell.
-    # 📚 https://git.io/JvXDl
-
-    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
-    #    and modify them (or add more) to build your code if your project
-    #    uses a compiled language
-
-    #- run: |
-    #   make bootstrap
-    #   make release
-
-    - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml
deleted file mode 100644
index a63b325f6c..0000000000
--- a/.github/workflows/reuse.yml
+++ /dev/null
@@ -1,32 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-name: Verify REUSE
-
-on:
-  push:
-    branches: [main]
-  pull_request:
-    branches: [main]
-  # Run CI once per day (at 07:30 UTC)
-  schedule:
-    - cron: '30 7 * * *'
-
-jobs:
-  check:
-    permissions:
-      contents: read
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Install dependencies
-        run: |
-          pip install reuse
-
-      - name: Check REUSE compliance
-        run: |
-          reuse lint
diff --git a/tests/utils/shippable/aix.sh b/tests/utils/shippable/aix.sh
deleted file mode 120000
index 6ddb776854..0000000000
--- a/tests/utils/shippable/aix.sh
+++ /dev/null
@@ -1 +0,0 @@
-remote.sh
\ No newline at end of file
diff --git a/tests/utils/shippable/freebsd.sh b/tests/utils/shippable/freebsd.sh
deleted file mode 120000
index 6ddb776854..0000000000
--- a/tests/utils/shippable/freebsd.sh
+++ /dev/null
@@ -1 +0,0 @@
-remote.sh
\ No newline at end of file
diff --git a/tests/utils/shippable/generic.sh b/tests/utils/shippable/generic.sh
deleted file mode 100755
index 5fd1fb55aa..0000000000
--- a/tests/utils/shippable/generic.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-python="${args[1]}"
-group="${args[2]}"
-
-target="azp/generic/${group}/"
-
-stage="${S:-prod}"
-
-# shellcheck disable=SC2086
-ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
-    --remote-terminate always --remote-stage "${stage}" \
-    --docker --python "${python}"
diff --git a/tests/utils/shippable/linux-community.sh b/tests/utils/shippable/linux-community.sh
deleted file mode 100755
index 48d0d8687d..0000000000
--- a/tests/utils/shippable/linux-community.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-image="${args[1]}"
-python="${args[2]}"
-
-if [ "${#args[@]}" -gt 3 ]; then
-    target="azp/posix/${args[3]}/"
-else
-    target="azp/posix/"
-fi
-
-# shellcheck disable=SC2086
-ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
-    --docker "quay.io/ansible-community/test-image:${image}" --python "${python}"
diff --git a/tests/utils/shippable/linux.sh b/tests/utils/shippable/linux.sh
deleted file mode 100755
index 6e1e2350b9..0000000000
--- a/tests/utils/shippable/linux.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-image="${args[1]}"
-
-if [ "${#args[@]}" -gt 2 ]; then
-    target="azp/posix/${args[2]}/"
-else
-    target="azp/posix/"
-fi
-
-# shellcheck disable=SC2086
-ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
-    --docker "${image}"
diff --git a/tests/utils/shippable/macos.sh b/tests/utils/shippable/macos.sh
deleted file mode 120000
index 6ddb776854..0000000000
--- a/tests/utils/shippable/macos.sh
+++ /dev/null
@@ -1 +0,0 @@
-remote.sh
\ No newline at end of file
diff --git a/tests/utils/shippable/osx.sh b/tests/utils/shippable/osx.sh
deleted file mode 120000
index 6ddb776854..0000000000
--- a/tests/utils/shippable/osx.sh
+++ /dev/null
@@ -1 +0,0 @@
-remote.sh
\ No newline at end of file
diff --git a/tests/utils/shippable/remote.sh b/tests/utils/shippable/remote.sh
deleted file mode 100755
index 84c1ebbe0e..0000000000
--- a/tests/utils/shippable/remote.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-platform="${args[0]}"
-version="${args[1]}"
-
-if [ "${#args[@]}" -gt 2 ]; then
-    target="azp/posix/${args[2]}/"
-else
-    target="azp/posix/"
-fi
-
-stage="${S:-prod}"
-provider="${P:-default}"
-
-if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then
-    echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt
-fi
-
-# shellcheck disable=SC2086
-ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \
-    --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}"
diff --git a/tests/utils/shippable/rhel.sh b/tests/utils/shippable/rhel.sh
deleted file mode 120000
index 6ddb776854..0000000000
--- a/tests/utils/shippable/rhel.sh
+++ /dev/null
@@ -1 +0,0 @@
-remote.sh
\ No newline at end of file
diff --git a/tests/utils/shippable/sanity.sh b/tests/utils/shippable/sanity.sh
deleted file mode 100755
index 5b88a26778..0000000000
--- a/tests/utils/shippable/sanity.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-group="${args[1]}"
-
-if [ "${BASE_BRANCH:-}" ]; then
-    base_branch="origin/${BASE_BRANCH}"
-else
-    base_branch=""
-fi
-
-if [ "${group}" == "extra" ]; then
-    ../internal_test_tools/tools/run.py --color --bot --junit
-    exit
-fi
-
-case "${group}" in
-    1) options=(--skip-test pylint --skip-test ansible-doc --skip-test validate-modules) ;;
-    2) options=(                   --test      ansible-doc      --test validate-modules) ;;
-    3) options=(--test pylint           plugins/modules/) ;;
-    4) options=(--test pylint --exclude plugins/modules/) ;;
-esac
-
-# allow collection migration sanity tests for groups 3 and 4 to pass without updating this script during migration
-network_path="lib/ansible/modules/network/"
-
-if [ -d "${network_path}" ]; then
-    if [ "${group}" -eq 3 ]; then
-        options+=(--exclude "${network_path}")
-    elif [ "${group}" -eq 4 ]; then
-        options+=("${network_path}")
-    fi
-fi
-
-# shellcheck disable=SC2086
-ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
-    --docker --base-branch "${base_branch}" \
-    "${options[@]}" --allow-disabled
diff --git a/tests/utils/shippable/shippable.sh b/tests/utils/shippable/shippable.sh
deleted file mode 100755
index 4f97771c28..0000000000
--- a/tests/utils/shippable/shippable.sh
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-ansible_version="${args[0]}"
-script="${args[1]}"
-
-function join {
-    local IFS="$1";
-    shift;
-    echo "$*";
-}
-
-# Ensure we can write other collections to this dir
-sudo chown "$(whoami)" "${PWD}/../../"
-
-test="$(join / "${args[@]:1}")"
-
-docker images ansible/ansible
-docker images quay.io/ansible/*
-docker ps
-
-for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do
-    docker rm -f "${container}" || true  # ignore errors
-done
-
-docker ps
-
-if [ -d /home/shippable/cache/ ]; then
-    ls -la /home/shippable/cache/
-fi
-
-command -v python
-python -V
-
-function retry
-{
-    # shellcheck disable=SC2034
-    for repetition in 1 2 3; do
-        set +e
-        "$@"
-        result=$?
-        set -e
-        if [ ${result} == 0 ]; then
-            return ${result}
-        fi
-        echo "@* -> ${result}"
-    done
-    echo "Command '@*' failed 3 times!"
-    exit 255
-}
-
-command -v pip
-pip --version
-pip list --disable-pip-version-check
-if [ "${ansible_version}" == "devel" ]; then
-    retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
-else
-    retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check
-fi
-
-if [ "${SHIPPABLE_BUILD_ID:-}" ]; then
-    export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible"
-    SHIPPABLE_RESULT_DIR="$(pwd)/shippable"
-    TEST_DIR="${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general"
-    mkdir -p "${TEST_DIR}"
-    cp -aT "${SHIPPABLE_BUILD_DIR}" "${TEST_DIR}"
-    cd "${TEST_DIR}"
-else
-    export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../"
-fi
-
-if [ "${test}" == "sanity/extra" ]; then
-    retry pip install junit-xml --disable-pip-version-check
-fi
-
-# START: HACK install dependencies
-if [ "${script}" != "sanity" ] || [ "${test}" == "sanity/extra" ]; then
-    # Nothing further should be added to this list.
-    # This is to prevent modules or plugins in this collection having a runtime dependency on other collections.
-    retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/internal_test_tools"
-    # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
-    # retry ansible-galaxy -vvv collection install community.internal_test_tools
-fi
-
-if [ "${script}" != "sanity" ] && [ "${script}" != "units" ] && [ "${test}" != "sanity/extra" ]; then
-    # To prevent Python dependencies on other collections only install other collections for integration tests
-    retry git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/ansible/posix"
-    retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.crypto.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/crypto"
-    # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
-    # retry ansible-galaxy -vvv collection install ansible.posix
-    # retry ansible-galaxy -vvv collection install community.crypto
-fi
-
-# END: HACK
-
-export PYTHONIOENCODING='utf-8'
-
-if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then
-    COVERAGE=yes
-    COMPLETE=yes
-fi
-
-if [ -n "${COVERAGE:-}" ]; then
-    # on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value
-    export COVERAGE="--coverage"
-elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then
-    # on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message
-    export COVERAGE="--coverage"
-else
-    # on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled)
-    export COVERAGE="--coverage-check"
-fi
-
-if [ -n "${COMPLETE:-}" ]; then
-    # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value
-    export CHANGED=""
-elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then
-    # disable change detection triggered by having 'ci_complete' in the latest commit message
-    export CHANGED=""
-else
-    # enable change detection (default behavior)
-    export CHANGED="--changed"
-fi
-
-if [ "${IS_PULL_REQUEST:-}" == "true" ]; then
-    # run unstable tests which are targeted by focused changes on PRs
-    export UNSTABLE="--allow-unstable-changed"
-else
-    # do not run unstable tests outside PRs
-    export UNSTABLE=""
-fi
-
-# remove empty core/extras module directories from PRs created prior to the repo-merge
-find plugins -type d -empty -print -delete
-
-function cleanup
-{
-    # for complete on-demand coverage generate a report for all files with no coverage on the "sanity/5" job so we only have one copy
-    if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/5" ]; then
-        stub="--stub"
-        # trigger coverage reporting for stubs even if no other coverage data exists
-        mkdir -p tests/output/coverage/
-    else
-        stub=""
-    fi
-
-    if [ -d tests/output/coverage/ ]; then
-        if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then
-            process_coverage='yes'  # process existing coverage files
-        elif [ "${stub}" ]; then
-            process_coverage='yes'  # process coverage when stubs are enabled
-        else
-            process_coverage=''
-        fi
-
-        if [ "${process_coverage}" ]; then
-            # use python 3.7 for coverage to avoid running out of memory during coverage xml processing
-            # only use it for coverage to avoid the additional overhead of setting up a virtual environment for a potential no-op job
-            virtualenv --python /usr/bin/python3.7 ~/ansible-venv
-            set +ux
-            . ~/ansible-venv/bin/activate
-            set -ux
-
-            # shellcheck disable=SC2086
-            ansible-test coverage xml --color -v --requirements --group-by command --group-by version ${stub:+"$stub"}
-            cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/"
-
-            if [ "${ansible_version}" != "2.9" ]; then
-                # analyze and capture code coverage aggregated by integration test target
-                ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json"
-            fi
-
-            # upload coverage report to codecov.io only when using complete on-demand coverage
-            if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then
-                for file in tests/output/reports/coverage=*.xml; do
-                    flags="${file##*/coverage=}"
-                    flags="${flags%-powershell.xml}"
-                    flags="${flags%.xml}"
-                    # remove numbered component from stub files when converting to tags
-                    flags="${flags//stub-[0-9]*/stub}"
-                    flags="${flags//=/,}"
-                    flags="${flags//[^a-zA-Z0-9_,]/_}"
-
-                    bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \
-                        -f "${file}" \
-                        -F "${flags}" \
-                        -n "${test}" \
-                        -t 20636cf5-4d6a-4b9a-8d2d-6f22ebbaa752 \
-                        -X coveragepy \
-                        -X gcov \
-                        -X fix \
-                        -X search \
-                        -X xcode \
-                    || echo "Failed to upload code coverage report to codecov.io: ${file}"
-                done
-            fi
-        fi
-    fi
-
-    if [ -d  tests/output/junit/ ]; then
-      cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/"
-    fi
-
-    if [ -d tests/output/data/ ]; then
-      cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/"
-    fi
-
-    if [ -d  tests/output/bot/ ]; then
-      cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/"
-    fi
-}
-
-if [ "${SHIPPABLE_BUILD_ID:-}" ]; then trap cleanup EXIT; fi
-
-if [[ "${COVERAGE:-}" == "--coverage" ]]; then
-    timeout=60
-else
-    timeout=50
-fi
-
-ansible-test env --dump --show --timeout "${timeout}" --color -v
-
-if [ "${SHIPPABLE_BUILD_ID:-}" ]; then "tests/utils/shippable/check_matrix.py"; fi
-"tests/utils/shippable/${script}.sh" "${test}" "${ansible_version}"
diff --git a/tests/utils/shippable/units.sh b/tests/utils/shippable/units.sh
deleted file mode 100755
index f591ec25aa..0000000000
--- a/tests/utils/shippable/units.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-set -o pipefail -eux
-
-declare -a args
-IFS='/:' read -ra args <<< "$1"
-
-version="${args[1]}"
-group="${args[2]}"
-
-if [[ "${COVERAGE:-}" == "--coverage" ]]; then
-    timeout=90
-else
-    timeout=30
-fi
-
-group1=()
-
-case "${group}" in
-    1) options=("${group1[@]:+${group1[@]}}") ;;
-esac
-
-ansible-test env --timeout "${timeout}" --color -v
-
-if [ "$2" == "2.9" ]; then
-    # 1.5.0+ will not install for Python 3.6+ in the 2.9 setting (due to `enum` being installed)
-    echo "pynacl >= 1.4.0, < 1.5.0; python_version >= '3.6'" >> tests/unit/requirements.txt
-fi
-
-if [ "$2" == "2.10" ]; then
-    sed -i -E 's/^redis($| .*)/redis < 4.1.0/g' tests/unit/requirements.txt
-    sed -i -E 's/^python-gitlab($| .*)/python-gitlab < 2.10.1 ; python_version >= '\'3.6\''/g' tests/unit/requirements.txt
-    echo "python-gitlab ; python_version < '3.6'" >> tests/unit/requirements.txt
-fi
-
-# shellcheck disable=SC2086
-ansible-test units --color -v --docker default --python "${version}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \
-    "${options[@]:+${options[@]}}" \