Ansible Collections for Reusable Modules Development

When standard Ansible modules fall short for non-standard tasks, we develop Ansible Collections — reusable automation packages. Our team builds custom modules, inventory plugins, and collections turnkey, publishing them to Ansible Galaxy or automation hub. The result is reliable solutions that are versioned, tested, and scale with your business.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Ansible Collections for Reusable Modules Development

We often face situations where standard Ansible modules don't cover project needs: integration with internal APIs, custom inventory sources, or distributing automation across teams. In such cases, we develop Ansible Collections—packages that can be versioned, tested, and published independently of Ansible Core. Over the years, we have delivered more than 50 projects with custom collections, ranging from simple modules to full inventory plugins for internal CMDBs.

Collections were introduced in Ansible 2.9 as an answer to namespace and versioning chaos. Before collections, everything lived in a single repository: modules for AWS, GCP, VMware sat next to core modules and were updated together with Core. A collection is a package containing roles, plugins, modules, and filters with an independent release cycle. Today it is the de facto standard for distributing Ansible content.

Problems We Solve

  • Custom modules. No standard module for your service's API? We write a module that interacts with REST API, handles errors, and supports check mode. Example: the NetBox host registration module (see below) replaces 50+ lines of bash scripts, runs 10x faster, and is more secure (token not exposed in logs).
  • Inventory plugins. Is your data source (AD, OpenStack, custom CMDB) not supported by community.general? We develop a plugin that loads inventory dynamically—without wrapper scripts or workarounds.
  • Content unification. Are roles, modules, and filters scattered across git repositories? We consolidate them into a collection with unified versioning and dependencies, simplifying onboarding for new engineers.

How Is an Ansible Collection Structured?

ansible-galaxy collection init mycompany.infrastructure 

Creates a structure:

mycompany/infrastructure/
├── galaxy.yml # metadata, version, dependencies
├── README.md
├── plugins/
│   ├── modules/ # custom modules
│   ├── inventory/ # inventory plugins
│   ├── filter/ # Jinja2 filters
│   └── lookup/ # lookup plugins
├── roles/
│   ├── hardening/
│   └── monitoring/
└── playbooks/

galaxy.yml:

---
namespace: mycompany
name: infrastructure
version: 1.2.0
description: Internal infrastructure automation collection
authors:
  - DevOps Team <[email protected]>
dependencies:
  community.general: ">=7.0.0"
  ansible.posix: ">=1.5.0"
---

Why Use Custom Modules Instead of Scripts?

A custom module is a Python script using AnsibleModule. Unlike shell or command tasks, a module:

  • Supports check mode (idempotent preview);
  • Returns structured data (result) for use in subsequent tasks;
  • Logs sensitive parameters safely with no_log;
  • Integrates with Ansible testing.

Example module for registering a host in NetBox:

# plugins/modules/netbox_register.py
from ansible.module_utils.basic import AnsibleModule
import requests

DOCUMENTATION = r'''
module: netbox_register
short_description: Register host in NetBox CMDB
options:
    hostname:
        description: Hostname to register
        required: true
    ip_prefix:
        description: IP prefix to allocate from (e.g. 10.20.30.0/24)
        required: true
    netbox_url:
        description: NetBox URL
        required: true
    netbox_token:
        description: NetBox API token
        required: true
        no_log: true
'''

def run_module():
    module_args = dict(
        hostname=dict(type='str', required=True),
        ip_prefix=dict(type='str', required=True),
        netbox_url=dict(type='str', required=True),
        netbox_token=dict(type='str', required=True, no_log=True),
    )

    result = dict(changed=False, ip_address=None)
    module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

    headers = {'Authorization': f"Token {module.params['netbox_token']}"}
    nb_url = module.params['netbox_url']

    # Check if already registered
    resp = requests.get(
        f"{nb_url}/api/ipam/ip-addresses/",
        params={'dns_name': module.params['hostname']},
        headers=headers
    )
    if resp.json()['count'] > 0:
        result['ip_address'] = resp.json()['results'][0]['address']
        module.exit_json(**result)

    if module.check_mode:
        result['changed'] = True
        module.exit_json(**result)

    # Allocate IP from prefix
    prefix_resp = requests.get(
        f"{nb_url}/api/ipam/prefixes/",
        params={'prefix': module.params['ip_prefix']},
        headers=headers
    )
    prefix_id = prefix_resp.json()['results'][0]['id']

    ip_resp = requests.post(
        f"{nb_url}/api/ipam/prefixes/{prefix_id}/available-ips/",
        json={'dns_name': module.params['hostname'], 'status': 'active'},
        headers=headers
    )
    result['changed'] = True
    result['ip_address'] = ip_resp.json()['address']
    module.exit_json(**result)

def main():
    run_module()

if __name__ == '__main__':
    main()

Usage in playbook:

---
- name: Register server in NetBox
  mycompany.infrastructure.netbox_register:
    hostname: "{{ inventory_hostname }}"
    ip_prefix: "10.20.30.0/24"
    netbox_url: "https://netbox.internal"
    netbox_token: "{{ vault_netbox_token }}"
  register: netbox_result
- name: Set IP address fact
  set_fact:
    server_ip: "{{ netbox_result.ip_address | ansible.utils.ipaddr('address') }}"
---

Comparison: Collection vs Role

Criterion Role Collection
Scope Configuration of a single service Package of modules, plugins, roles
Custom modules No Yes, in plugins/modules
Inventory plugins No Yes
Filters and lookup No Yes
Versioning Depends on repo Independent, via galaxy.yml
Distribution Git clone Galaxy / Automation Hub

How to Test an Ansible Collection?

Testing is a key phase. We use three levels:

  • Unit tests (pytest with ansible-test units) – verify module and filter logic on mock data.
  • Sanity tests (ansible-test sanity) – validate compatibility with Ansible API, PEP8, absence of deprecated calls.
  • Integration tests (ansible-test integration) – deploy test infrastructure (Docker or Vagrant) and check module operation in a real environment.

Example CI config in GitHub Actions:

---
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install ansible-core
      - run: ansible-test sanity --docker -v
      - run: ansible-test units --python 3.11 -v
      - run: ansible-test integration --docker -v

Official Ansible testing documentation: Ansible Testing Guide

Comparison of Testing Tools

Tool Purpose Speed When to Use
ansible-test units Unit tests for modules Fast Each commit
ansible-test sanity Static analysis Fast Before PR
ansible-test integration Integration tests Slow Before release
Molecule Testing roles Medium For isolated roles

Process

  1. Analysis – define collection composition: modules, plugins, dependencies.
  2. Design – develop structure, module interfaces, test scenarios.
  3. Development – write code for modules, plugins, roles.
  4. Testing – unit, sanity, integration tests in CI.
  5. Deployment – publish to private Galaxy or Automation Hub, documentation.

What Is Included

  • Development of 2–3 custom modules (or plugins) with documentation.
  • Creation of galaxy.yml with dependencies and metadata.
  • CI setup (GitLab CI / GitHub Actions) with test execution on every PR.
  • Publication to private Galaxy server or Automation Hub.
  • Source code and README delivery.
  • Team training on collection usage (1 hour).
  • Compatibility guarantee with current Ansible versions (at delivery).

Estimated Timelines

  • Collection with 2–3 modules and basic tests – 3–5 days.
  • Inventory plugin for a specific source – 2–3 days.
  • CI setup with publication – 1–2 days.

Cost is calculated individually after analyzing your requirements. Contact us – we will evaluate your project within one business day.

Common Mistakes in Collection Development
  • Not including check mode – breaks idempotency.
  • Storing sensitive parameters without no_log.
  • Skipping sanity tests before publication (Ansible Galaxy requires passing them).
  • Not versioning the collection semantically (follow SemVer).
  • Not specifying dependencies in galaxy.yml – collection won't build on a clean environment.

Developing Ansible Collections is a way to standardize and accelerate automation, reducing operational costs by 30–50%. Order a custom collection for your tasks – get a ready package with tests and CI.