Writing an Ansible module using PHP
Bootstrap your environment
The first thing you need to do is bootstrap your environment for development. I'm going to assume that you've set it up like me
bash
cd ansiblesource hacking/env-setupchmod +x hacking/test-modulesource .virtualenv/bin/activate
Next, we want to see if everything's set up correctly by running the test-module
command with no parameters.
bash
./hacking/test-moduleUsage: test-module -[options] (-h for help)
If you see the above, we're all set!
Creating a module
The first step is to make sure that we can run a custom module. Create a file named my_module
with the following contents:
php
#!/usr/bin/env php<?phpecho json_encode(array("foo" => "bar", "time" => time()));
Ansible modules talk JSON, so here all we're doing is returning the current time to see if our module works. You can run it with the following command:
php
./hacking/test-module -m ./my_module
You should see something that looks like this:
* including generated source, if any, saving to: /Users/michael/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"foo":"bar","time":1397926744} *********************************** PARSED OUTPUT { "foo": "bar", "time": 1397926744 }
Now, it's great that we've got our module working, but it doesn't actually do much. The next thing to do is make it read any input parameters:
php
$args_file = $argv[1];$args_data = file_get_contents($args_file);
Ansible writes input parameters to a file then passes the filename to our module, so we need to read it to find out what's going on.
It's up to each module to decide what format it wants it's arguments in, but as most of them take key=value
pairs, let's stick with that.
php
$args_file = $argv[1];$args_data = file_get_contents($args_file);$args = explode(" ", $args_data);$return_value = array();foreach ($args as $arg){// If it doesn't contain an equals sign, skip itif (strpos($arg, "=") == false) {continue;}list ($key, $value) = explode("=", $arg, 2);$return_value[$key] = $value;$path = '/tmp/'.$key;file_put_contents($path, $value);}echo json_encode($return_value);
Let's test our module again, this time giving it some parameters (the quoting is important):
bash
./hacking/test-module -m ./my_module -a "foo=bar bee baz=boo"
You'll see that our output changes to match what we passed in, skipping anything that didn't contain an equals sign:
*********************************** RAW OUTPUT {"foo":"bar","baz":"boo"} *********************************** PARSED OUTPUT { "baz": "boo", "foo": "bar" }
Thankfully, Ansible takes care of running everything on the target machines for us. If you check your target machine, you should see files created in /tmp
with the values you specified.
The only caveat with writing your own modules is that if you want to use PHP, you'll need PHP installed on the target machine, not just the host machine.
Using the module
For Ansible to know about your module, it needs to live somewhere special. There's two options for telling Ansible about your module.
The first is by adding a directory containing your modules to $ANSIBLE_LIBRARY
like the following:
bash
export ANSIBLE_LIBRARY="/Users/michael/my_ansible_modules:/usr/share/ansible/"
Then, you just use Ansible like normal.
Alternatively, you can use the --module-path
(or -M
) flag to specify where to search for modules. Of these two options, I prefer modifying $ANSIBLE_LIBRARY
.
Next steps
That's all there is to it. You've created a basic Ansible module. There's more to add, such as error conditions and exit codes, along with required parameters but that's a story for another time.