Ansible Plus Flask

I was lurking on the networktocode Slack channel, when someone asked how to get variables from an external system into an Ansible playbook. Intrigued, I mocked up a quick Flask server to serve a REST API, and a playbook to consume it. This proof of concept happily ignores a lot of issues you'd face in production:

The Server

In prod, this would hopefully be some high-powered IPAM with a REST API that dealt properly with auth and could be a reliable source of truth.

  • server.py
from flask import Flask, jsonify
app = Flask(__name__)


# returns
# {

#     "result": {
#         "chassis": "chassis-007",
#         "ip": "0.0.0.0"
#     }

# }
@app.route('/get_ip')
def hello_world():
    # In reality, this info could come from a form, or a db or whatever
    # Don't forget to deal with auth!
    return jsonify(dict(result=dict(ip='0.0.0.0',
                                    chassis='chassis-007')))


def main():
    # Don't run these options in prod
    app.run(host='0.0.0.0', debug=True)


if __name__ == "__main__":
    main()

Run it with python server.py

The Hosts file

Since I'm not really doing anything, I'm using a minimal hosts file. In prod, you could use a static hosts file, or a dynamic hosts system that also queries your IPAM.

  • hosts
[prod]
localhost ansible_connection=local

The Playbook

This is the real meat of the work. I can decode the REST response from the HTTP request by calling from_json filter on the content from the HTTP response, then walking the JSON object. I'll admit, this isn't the prettiest way to do something like this, but working ugly code is better than nonworking pretty code...

  • playbook.yaml
---
- hosts: all
  gather_facts: false
  tasks:
    # https://serverfault.com/q/722852/383537
  - name: Get JSON from Source of Truth
    uri:
      # this shouldn't be hardcoded
      url: 'http://localhost:5000/get_ip'
      return_content: true
    register: json_response
  - name: Print the whole response to help with parsing
    debug:
      msg: "chassis is {% raw %}{{ json_response }}{% endraw %}"
  - name: Tell the world I got my chassis
    debug:
      # need to decode the response from the content part of the http response, then index into it
      msg: "chassis is {% raw %}{{ (json_response.content|from_json).result.chassis }}{% endraw %}"

Then run it with:

ansible-playbook -i hosts playbook.yaml

Ta-da! It prints chassis-007