5

After going through several documentation I concluded that, I can't use with_items for roles.

So, I created a filter_plugin to generate a list of dictionaries for roles.

Here is my Play:

---
- name: Boostrap vpc and subnets with route table
  hosts: localhost
  connection: local
  gather_facts: no
  pre_tasks:
    - include_vars: ec2_vars/common/regions.yml
    - include_vars: environment.yml
  roles:
    - {
        role: vpc,
        ec2_region: 'ap-southeast-2'
      }
    - {
        role: vpc,
        ec2_region: "ap-southeast-1",
      }
    - {
        role: vpc,
        ec2_region: "us-west-2",
      }

I want to generate above roles dynamically and for that I created a filter_plugin which generates a list of dictionaries and that is working right.

Here is my plugin:

# this is for generating vpc roles

def roles(ec2_regions):
    return [{'role': 'vpc', 'ec2_region': ec2_region} for ec2_region in ec2_regions]


class FilterModule(object):
    def filters(self):
        return {'vpcroles': roles}

My plan was to generate roles like following:

roles: "{{ EC2_REGIONS | vpcroles }}"

where EC2_REGIONS is ['ap-southeast-2', 'us-east-1']

But roles are not working in that way.

I am getting following error:

ERROR! A malformed role declaration was encountered.

Any thoughts/ideas ?

techraf
  • 53,268
  • 22
  • 149
  • 166
Suku
  • 3,540
  • 19
  • 22
  • Not solving your problem, but your YAML is inconsistent. You use either single and double quotes (for `ec2_region`) where they are not needed, and no quotes for other string scalar values. You also inconsistently apply a trailing comma in a flow style mapping (the elements of the value for `roles`). IMO if you do multiline flow style mappings you should always use trailing comma, or better use block style mappings. – Anthon Aug 22 '16 at 06:05
  • @Anthon Noted, Thank you. – Suku Aug 22 '16 at 06:12

3 Answers3

2

Very rough proof-of-concept. I was curious, if it would work and it does.

The major problem is that dynamically-created playbook is called from inside a task and its stdout doesn't go into main Ansible log (can be registered in the variable of the main playbook and displayed as such). Errors propagate to the parent-playbook.

Main playbook:

---
- hosts: localhost
  connection: local
  vars:
    params:
      - val1
      - val2
  tasks:
    - template:
        src: role_call.yml.j2
        dest: ./dynamic/role_call.yml
    - command: ansible-playbook ./dynamic/role_call.yml

Dynamic playbook template in templates/role_call.yml.j2 file:

- hosts: localhost
  connection: local
  roles:
  {% for param in params %}
    - { role: role1, par: {{param}} }
  {% endfor %}

roles/role1/tasks/main.yml:

- debug: var=par

I guess the internal ansible-playbook command could be called with a separate ansible.cfg as an argument to save the log to a different file.

Overall not worth the hassle in your case, I guess, but for a problem that cannot be solved otherwise like this it looks promising.

Community
  • 1
  • 1
techraf
  • 53,268
  • 22
  • 149
  • 166
  • thanks techraf. My colleague showed me another way and decided to go with that. I will update the thread with the same. – Suku Aug 22 '16 at 23:05
2

My colleague showed me a way to achieve a dynamic role. Here is that.

Directory Structure:

- vpc.yml
|
- roles/vpc/tasks/main.yml
|
- roles/vpc/tasks/real.yml

Play - vpc.yml:

---
- name: Boostrap vpc and subnets with route table
  hosts: localhost
  connection: local
  gather_facts: no
  vars:
  pre_tasks:
    - include_vars: environment.yml
  roles:
    - { role: "vpc", ec2_regions: "{{ EC2_REGIONS }}"}

Role - roles/vpc/tasks/main.yml:

- include: real.yml ec2_region="{{ _region }}"
  with_items: "{{ ec2_regions }}"
  loop_control:
    loop_var: _region

And then added my tasks in roles/vpc/tasks/real.yml

Suku
  • 3,540
  • 19
  • 22
  • I edited the answer. replaced `{{ role_name }}` with actual role name. Lmk if its not clear now. – Suku Aug 23 '16 at 00:35
  • its the `loop_var`. I can't use `item` as the variable name since I am already using `with_items` in the `real.yml`. So need a different variable name – Suku Aug 23 '16 at 00:41
  • There was a mistake in the directory path. I missed `tasks` directory when wrote the answer. by default `main.yml` will get executed. – Suku Aug 23 '16 at 00:44
  • Bottom line is that you modify the role, however the question reads like you wanted to create a "dynamic" wrapper (`with_items` for a role). Nested loops might have solved your problem, good for you, but I can't see how they answer this particular question. – techraf Aug 23 '16 at 01:37
  • Actually, I did n't modify the role. I just renamed my `main.yml` in `roles/vpc/tasks` to `real.yml`. And created a `main.yml` in same directory and used `with_items` to call `real.yml` in a loop along with a variable `ec2_region` . So in effect I am getting a dynamic role. This answer is not solving the problem (why `{{ EC2_REGIONS | vpcroles }}` is not expanding?) but I am getting a work around. – Suku Aug 23 '16 at 01:50
  • Now I am confused with your usage of words. If you did something that changed the role's input specification (now `vpc` accepts lists as input, before it didn't), doesn't it mean you modified it? – techraf Aug 23 '16 at 02:01
  • And I'm not criticising, your solution is a very neat answer to "how to modify a role to accept a list, with as minimal intrusion as possible". :-) – techraf Aug 23 '16 at 05:01
1

You may use dummy-hosts as a workaround:

---
- hosts: localhost
  vars:
    ec2_regions:
      - ap-southeast-2
      - ap-southeast-1
      - us-west-2
  tasks:
    - add_host: name=vpc_{{ item }} ansible_connection=local group=vpcs ec2_region={{ item }}
      with_items: "{{ ec2_regions }}"

- hosts: vpcs
  roles:
    - role: my_role
Konstantin Suvorov
  • 55,178
  • 7
  • 115
  • 152