3

I have to process output from CloudFormation Outputs that is:

Ansible code that produces this output:

- debug: 
  var: stack.stack_outputs

Output:

ok: [localhost] => {
  "stack.stack_outputs": {
    "Roles": "webserver balancer dbserver",
    "dbserver": "54.0.1.1 54.0.1.2",
    "balancer": "54.0.2.3",
    "webserver": "54.0.2.5 54.0.2.7 54.0.3.1"
}}

With that, I want to create 3 (dynamic number!) groups named accordingly filled with appropriate IPs.

Ansible code that I want to HELP WITH:

- name: fill roles with proper hosts
  local_action: add_host hostname={{item}} groupname={{role}}
  with_whatever: ?...?

In pseudo ansible python it would look like this:

for role in stack.stack_outputs.Roles.split():                           # Python
  for ip in stack.stack_outputs[role].split():                           # Python
    local_action: add_host hostname={{ip}} groupname={{role}}            # Ansible

Note:

The way to do it for these three roles statically is obviously:

- name: fill role WEBSERVER
  local_action: add_host hostname={{item}} groupname=webserver
  with_items: stack.stack_outputs.webserver.split()
- name: fill role DBSERVER
  local_action: add_host hostname={{item}} groupname=dbserver
  with_items: stack.stack_outputs.dbserver.split()
- name: fill role BALANCER
  local_action: add_host hostname={{item}} groupname=balancer
  with_items: stack.stack_outputs.balancer.split()

I want to do it dynamically, is it even possible in Ansible?
Yes, I can use shell module to hack it putting everything in temporary file and then looping over that; but is there a better solution?

Thanks for any suggestions.

DinGODzilla
  • 1,611
  • 4
  • 21
  • 34

2 Answers2

3

I understand you want the answer to fit a very specific framework. Within that, a custom lookup_plugin is your best bet. Otherwise it'll be an ugly sequence of set_fact and add_host. Sophisticated control structures are the antithesis of Ansible.

You don't explicitly rule out the following, so even if it's too out of the box for you, consider it because I've been reconciling cfn and ansible for a good long while:

Don't use stack outputs to fill your inventory. Use a dynamic inventory script for that (e.g. one that goes over stack outputs or tags set in the templates).

I'm aware of the implications like, you can't have this in a single playbook, but if that's paramount, use group_by.

Hope this helps.

nik.shornikov
  • 1,869
  • 1
  • 17
  • 21
  • Dynamic inventory + group_by will be better solution. Thanks – DinGODzilla Feb 08 '15 at 22:35
  • Is there a way how to re-trigger *dynamic inventory* scan during a playbook run? Because when I update CF template, then 0) DI runs, 1) CF runs and for example add some hosts 2) I work with not-up-to-date version of inventory and have to parse **Outputs** of CF to remedy it. ***How did you solve this problem?*** – DinGODzilla Feb 16 '15 at 03:59
  • @DinGODzilla, no... afaik dynamic inventory updates resulting from your playbook cannot be picked up automatically. http://stackoverflow.com/a/29008654/1527814 Your best bet would be to register the output of whatever task produced your new inventory items and use that in your playbook. – Sankalp Jun 10 '15 at 08:50
  • @DinGodZilla Are you using ansible console or Ansible tower with Dynamic Inventory? That comment you made should be another question all on its own btw. – eco Jul 07 '17 at 23:06
  • @einarc - you're right, the solution in Ansible 2 is `- meta: refresh_inventory`. No solution in v1.9. – DinGODzilla Oct 09 '17 at 05:49
0

For anyone that comes asking without reading the documentation first. Ansible has been updated to support nested indexing:

http://docs.ansible.com/ansible/playbooks_loops.html#nested-loops

Cheers!

eco
  • 1,254
  • 1
  • 12
  • 22
  • Are you *sure* that `with_nested: [ '{{ stack.stack_outputs.Roles.split() }}', '{{ stack.stack_outputs[item[0]].split() }}' ]` ? Particullary that `[item[0]]` in line/item 2. – DinGODzilla Oct 09 '17 at 05:59