Search code examples
ansibleaws-cloudformationorchestration

How do I run an Ansible playbook for 'collections' of CloudFormation stacks?


I am using Ansible to manage numerous CloudFormation stacks all based on the same playbook and CloudFormation template. I would like to treat the stacks as if they were nodes in an inventory, is this possible?

For example, I have one CloudFormation template and one Ansible playbook configured as follows:

- name: CloudFormation
  hosts: localhost
  connection: local
  gather_facts: false
  vars_files:
    - vars/global.yml
  tasks:
    - name: Network Stack
      cloudformation:
        stack_name: "{{ name }}-{{ env }}-network"

I then have a stack specific var file that I pass in at runtime ansible-playbook -e @one-prod-network.yml play.yml.

I need a way to run the playbook against all stacks in a group as if they were inventory items, for example:

[test]
one-test-network
two-test-network

[prod]
three-prod-network
four-prod-network

I thought roles might be the answer, but after investigating that I'm not so sure - obviously I don't want to duplicate the templates for each role. At present I am using a Makefile to run the command for each stack, but would prefer to do it in Ansible.


Solution

  • I would like to treat the stacks as if they were nodes in an inventory, is this possible?

    From what I understand, yes you could do it by specifying that the ansible_connection is local on all hosts of a group cloud_formation then nest all your hosts (or hosts groups) inside it.

    Here would be an example inventory:

    [cloud_formation:children]
    test
    prod
    
    [cloud_formation:vars]
    ansible_connection=local
    
    [test]
    one-test-network name=one
    two-test-network name=two
    
    [test:vars]
    env=test
    
    [prod]
    three-prod-network name=three
    four-prod-network name=four
    
    [prod:vars]
    env=prod
    

    Running this playbook against it:

    - hosts: cloud_formation
      gather_facts: false
    
      tasks:
        - debug:
            msg:
              cloudformation:
                stack_name: "{{ name }}-{{ env }}-network"
    

    Would yield:

    ok: [one-test-network] => 
      msg:
        cloudformation:
          stack_name: one-test-network
    ok: [two-test-network] => 
      msg:
        cloudformation:
          stack_name: two-test-network
    ok: [three-prod-network] => 
      msg:
        cloudformation:
          stack_name: three-prod-network
    ok: [four-prod-network] => 
      msg:
        cloudformation:
          stack_name: four-prod-network
    

    Then, if my guess of what you are trying to achieve is correct, you could even simplify further, with the inventory:

    [cloud_formation:children]
    test
    prod
    
    [cloud_formation:vars]
    ansible_connection=local
    
    [test]
    one-test-network
    two-test-network
    
    [prod]
    three-prod-network
    four-prod-network
    

    Running against the playbook:

    - hosts: cloud_formation
      gather_facts: false
    
      tasks:
        - debug:
            msg:
              cloudformation:
                stack_name: "{{ inventory_hostname }}"
    

    Would yield:

    ok: [one-test-network] => 
      msg:
        cloudformation:
          stack_name: one-test-network
    ok: [two-test-network] => 
      msg:
        cloudformation:
          stack_name: two-test-network
    ok: [three-prod-network] => 
      msg:
        cloudformation:
          stack_name: three-prod-network
    ok: [four-prod-network] => 
      msg:
        cloudformation:
          stack_name: four-prod-network
    

    Or, without a parent group, the inventory:

    [test]
    one
    two
    
    [test:vars]
    ansible_connection=local
    
    [prod]
    three
    four
    
    [prod:vars]
    ansible_connection=local
    

    Run against the playbook:

    - hosts: test, prod
      gather_facts: false
    
      tasks:
        - debug:
            msg:
              cloudformation:
                stack_name: >-
                  {{ inventory_hostname -}}
                  -{{ group_names[0] -}}
                  -network
                ## /!\ Mind that this only works accuratly
                ## if the hosts are present in a single group
    

    Would yield:

    ok: [one] => 
      msg:
        cloudformation:
          stack_name: one-test-network
    ok: [two] => 
      msg:
        cloudformation:
          stack_name: two-test-network
    ok: [three] => 
      msg:
        cloudformation:
          stack_name: three-prod-network
    ok: [four] => 
      msg:
        cloudformation:
          stack_name: four-prod-network