Credits

This post is based on this page by Gerard Lynch

General

In the following ansible playbook a VPC is set up with a NAT vm to be used by servers in the Private subnet and a single Web Server vm in the Public subnet

Prerequisites

Install Python package boto

sudo pip install boto

AWS Key and Secret are added to the Environment Variables

Add

export AWS_ACCESS_KEY_ID=AAAAAAAAAAAAAAAAAAAA
export AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

To your ~/.bashrc

Basic ansible directory structure

  ├── ansible.cfg
  ├── ansible_plugins
  │   └── filter_plugins
  │       └── get_subnets.py
  └── vpc.yml

Ansible Plugin get_subnets.py

# ansible_plugins/filter_plugins/get_subnets.py
from jinja2.utils import soft_unicode

def get_subnets(value, tag_key, tag_value, return_key='id'):
    # return an attribute for all subnets that match
    subnets = []
    for item in value:
      for key, value in item['resource_tags'].iteritems():
        if key == tag_key and value == tag_value:
          subnets.append(item[return_key])

    return subnets

class FilterModule(object):
    ''' Ansible core jinja2 filters '''

    def filters(self):
        return {
            'get_subnets': get_subnets,
        }

ansible.cfg

[defaults]
filter_plugins = ansible_plugins/filter_plugins

The playbook vpc.yml


---
# play.yml
- hosts: localhost
  vars:
    vpc_list:
      - region: us-east-1
        state: present
        cidr_block: 10.1.0.0/16
        resource_tags:
          Environment: dev 
        internet_gateway: True
        subnets:
          - cidr: 10.1.0.0/24
            az: us-east-1a
            resource_tags: { "Name": "dev_public", "Environment": "dev", "Tier": "public" }
          - cidr: 10.1.100.0/24
            az: us-east-1a
            resource_tags: { "Name": "dev_private", "Environment": "dev", "Tier": "private" }
    nat_list:
      - region: us-east-1
        keypair: our_ansible_key
        instance_type: "t2.small" 
        image: "ami-b0210ed8" # amzn-ami-vpc-nat-hvm-2015.03.0.x86_64-ebs
        instance_tags: { "Name": "dev_nat", "Environment": "dev" }
        exact_count: 1
        count_tag: { "Name": "dev_nat" }
    webserver_list:
      - region: us-east-1
        keypair: our_ansible_key
        instance_type: "m4.4xlarge" 
        image: "ami-fce3c696" # Ubuntu Server 14.04 LTS (HVM), SSD Volume Type
        instance_tags: { "Name": "Web_Server", "Environment": "dev" }
        exact_count: 1
        count_tag: { "Name": "Web_Server" }

  tasks:

    - name: process vpc
      ec2_vpc:
        region: "{{ item.region }}" 
        state: "{{ item.state }}" 
        cidr_block: "{{ item.cidr_block }}" 
        resource_tags: "{{ item.resource_tags }}" 
        internet_gateway: "{{ item.internet_gateway }}" 
        subnets: "{{ item.subnets }}" 
      with_items: vpc_list
      register: ec2_vpc_out

    - name: ssh-ec2-group
      ec2_group:
        description: HTTP and SSH only from office
        name: open https and ssh
        vpc_id: "{{ ec2_vpc_out.results.0.vpc.id }}"
        region: "{{ ec2_vpc_out.results.0.vpc.region }}"
        rules:
        - proto: tcp
          from_port: 22
          to_port: 22
          cidr_ip: 80.80.80.80/32 # Our office IP
        - proto: tcp
          from_port: 80
          to_port: 80
          cidr_ip: 0.0.0.0/0
        - proto: tcp
          from_port: 443
          to_port: 443
          cidr_ip: 0.0.0.0/0
      register: ec2_sec_group

    - name: nat instance
      ec2:
        region: "{{ item.0.region }}" 
        keypair: "{{ item.0.keypair }}" 
        instance_type: "{{ item.0.instance_type }}" 
        image: "{{ item.0.image }}" 
        instance_tags: "{{ item.0.instance_tags }}" 
        exact_count: "{{ item.0.exact_count }}" 
        count_tag: "{{ item.0.count_tag }}" 
        vpc_subnet_id: "{{ item.1.subnets | get_subnets('Tier', 'public') | first }}"
        assign_public_ip: yes
        wait: yes 
        wait_timeout: 500 
      with_together:
        - "{{ nat_list }}" 
        - "{{ ec2_vpc_out.results }}"
      register: ec2_nat_out

    - name: webserver ec2 instance 
      ec2:
        region: "{{ item.0.region }}" 
        keypair: "{{ item.0.keypair }}" 
        instance_type: "{{ item.0.instance_type }}" 
        image: "{{ item.0.image }}" 
        instance_tags: "{{ item.0.instance_tags }}" 
        exact_count: "{{ item.0.exact_count }}" 
        count_tag: "{{ item.0.count_tag }}" 
        vpc_subnet_id: "{{ item.1.subnets | get_subnets('Tier', 'private') | first }}"
        assign_public_ip: yes
        group: "{{ ec2_sec_group.group_id }}"
        wait: yes 
        wait_timeout: 500 
      with_together:
        - "{{ webserver_list }}" 
        - "{{ ec2_vpc_out.results }}" 
      register: ec2_webserver_out

    - name: update vpc routing tables
      ec2_vpc:
        region: "{{ item.0.region }}"
        state: "{{ item.0.state }}"
        cidr_block: "{{ item.0.cidr_block }}"
        resource_tags: "{{ item.0.resource_tags }}"
        internet_gateway: "{{ item.0.internet_gateway }}"
        subnets: "{{ item.0.subnets }}"
        route_tables:
          - subnets: "{{ item.0.subnets | get_subnets('Tier', 'public', 'cidr') }}"
            routes:
              - dest: 0.0.0.0/0
                gw: igw
# Place holder for setting up the Private network
#          - subnets: "{{ item.0.subnets | get_subnets('Tier', 'private', 'cidr') }}"
#            routes:
#              - dest: 0.0.0.0/0
#                gw: "{{ ec2_nat_out.results.0.instance_ids[0] }}"
      with_together:
        - "{{ vpc_list }}"
        - "{{ ec2_webserver_out.results }}"
        - "{{ ec2_nat_out.results }}"