Skip to content

Blog

How to Create and Test Your Custom Ansible Modules

Ansible is the configuration management tool of choice at Mission, in part beacause it’s so easy to write your own modules. Modules are reusable, standalone scripts that allow the Ansible API to call tasks like archiving, copying, ec2_facts, etc. These individual modules are what makes up a single Ansible Playbook or role in lieu of a shell command, which can quickly become cluttered as needs change.

Many new Ansible users jump in and begin building custom modules without stopping to ask themselves three simple questions:

  1. Does this module already exist? Always check out the Ansible community on github for existing modules; there’s a good chance someone has already built it!
  2. Should it be an action plugin instead of a module? Action plugins make changes locally first, like grabbing a local file, making a change and then sending it out.
  3. Should this be a role instead of a module? If you’re making a module that just keeps calling other modules, that isn’t a module; it’s a role. A module should not reference other scripts or files; it should be standalone.

The Basics of Building an Ansible Module

Ansible has a clearly documented set of coding standards and best practices. Here are some of the must-follow steps to make your life infinitely easier:

  • Use common aliases across modules – name, src, dest, state
  • Modules should only be one file. If you need multiple files, you’re looking to make an action plugin not a module.
  • Modules should only return JSON; it shouldn’t be giving any errors or tracebacks.
  • All modules need to start with a shebang! Literally every unit test will fail if you do not have this.

Note that you don’t have to use Python; you just have to use a language that supports File I/O and writing like Bash, Ruby or C++. Python is useful, though, because many of the Ansible support tools use it.

Writing a custom module can seem like a challenge at first, so let’s walk through a basic one. Aside from missing a name and documentation, this is a complete Ansible module for a JSON dump:

#!/usr/bin/python import datetime import json date = str(datetime.datetime.now()) print json.dumps({ “time” : date })

The Ansible Module Format

  • Shebang: #!/usr/bin/python
  • License agreement: the Ansible free software agreement since this is an open source product
  • Documentation: properly formatted yaml documentation (and documentation can reference other modules if needed)
  • Examples: use case for this module
  • Returns: you need to include a list of what is returned by the module
  • Functions: various functions your module needs, like changing a port on an ELB
  • Main: testing will be a nightmare if you don’t use a Main

Contributing to Ansible Modules

If you want to contribute your custom Ansible module to the community (and you should!), there are a list of requirements for formatting and functionality. Here are the most important ones:

  • Must start with a shebang
  • Support Python 2.6 (preferred, not required)
  • Informative responses to updates, even when nothing changes
  • Handle errors and avoid stacktraces
  • Are module actions idempotent?
  • Use no_log=True for passwords and secrets

Testing Your Ansible Module

There are four main pieces of testing you need to do for your custom Ansible Module: compile test, sanity tests, integration tests and unit tests. Each round of testing focuses on an individual part of the code base, like checking for valid syntax or properly following Ansible coding standards.

Compile Test

Compile tests are for syntax testing and ensure that your code will actually run and not crash immediately. Compile Tests are run against a single module and run against your specific version of Python (or whatever language you wrote your module in) to ensure the module has been properly compiled.

Sanity Tests

There are a variety of automated tests that keep you from ripping your hair out when something is missing that you can’t seem to find. Sanity tests look at the code of your module and check it for elements unique to Ansible’s standards.

Code Smell Tests are the miscellaneous scripts to enforce coding standards, like ensuring that your Shebangs, line endings and documentation all exist in the module. Ansible also offers built-in tests for all the scripts built into ansible-test, which Ansible provides to users out of the box.

Ansible-doc verifies all of your documentation is formatted properly and ensures you’re in line with Ansible standards. Finally, there are coding standard confirmations, like pep8, pylint, shellcheck and yamllint, so depending on how you wrote the module, it will still be verified. Validated modules return error codes like a 101 for a bad shebang or a 106, which means your imports are before documentation instead of after. There’s a nice cheat sheet on coding standards available here.

Integration Tests

Making a custom Ansible module is great, but if it can’t integrate with your tools, it’s going to be worthless. Integration tests confirm the functionality of your module through three steps: non-destructive, destructive and containers.

Non-destructive tests will not reconfigure or bounce any services and are run locally on your machine, but they doesn’t do a complete test on what you wrote. Destructive tests, on the other hand, do test everything you wrote, but they will install or remove packages or bounce services as needed. Do not do this locally; instead test in a container like Vagrant or Docker. We recommend using Docker containers because there is built-in functionality to make your testing process easier.

Unit Tests

Unit tests allow you to test individual parts of your code base as needed, without running the entire module all of the time. Unit tests are run against a single file and run against the specific version of Python for incremental checks.

So…. where do I start?

This is a lot of information to dump all at once about creating and testing Ansible modules. But remember that you don’t have to start from scratch. Ansible Issues allows you to see common issues (and solutions when they exist) along with feature update requests. It’s quite likely you aren’t the only one facing a specific issue, and the community can really help!

Even if you aren’t interested in coding modules yourself, you can contribute to Ansible by sending in bug reports and feature requests!

Start solving your DevOps Automation challenges by scheduling a 30-minute SA On-Demand where you can talk to one of our engineers about the steps you need to take to start automating!

 

FAQ

  1. Are there any common pitfalls or challenges when transitioning from using standard Ansible modules to developing custom ones?

When transitioning from standard Ansible modules to developing custom ones, common challenges include ensuring idempotency, handling error exceptions correctly, and designing modules to be reusable and maintainable. Developers must also familiarize themselves with Python programming and Ansible's architecture.

  1. How can developers ensure their custom Ansible modules remain compatible with future versions?

To ensure compatibility with future Ansible versions, developers should adhere to Ansible development guidelines, regularly test modules against Ansible’s development branch, and stay updated with Ansible's release notes and deprecation policies.

  1. What best practices should be followed when contributing custom modules to the Ansible community to ensure they are accepted and maintained?
  2. When contributing custom modules to the Ansible community, it is important to follow the community's contribution guidelines, write comprehensive documentation, including unit and integration tests, and engage with the community for feedback and suggestions to ensure the module meets the community's needs and standards.

Author Spotlight:

Katie Paugh

Keep Up To Date With AWS News

Stay up to date with the latest AWS services, latest architecture, cloud-native solutions and more.