EC2 Playbook

Ansible Playbook

Create the playbook:

ec2.yml
- name: Setup VPC, Subnet, Security Group, and Launch EC2 Instance
  hosts: localhost
  gather_facts: no
  connection: local
  collections:
    - amazon.aws
  vars:
    region: us-east-1
    vpc_cidr_block: "10.10.0.0/16"
    subnet_cidr_block: "10.10.1.0/24"
    availability_zone: us-east-1a
    security_group_name: RailsSG
    security_group_description: Security Group created by Ansible for EC2
    instance_type: t2.micro
    ami_id: ami-0e21465cede02fd1e
    key_name: testmar25
  tasks:
    - name: Create a VPC
      ec2_vpc_net:
        name: "RailsVPC"
        cidr_block: "{{ vpc_cidr_block }}"
        region: "{{ region }}"
        tags:
          Name: RailsVPC
      register: vpc

    - name: Create an Internet Gateway
      ec2_vpc_igw:
        vpc_id: "{{ vpc.vpc.id }}"
        state: present
        region: "{{ region }}"
        tags:
          Name: "RailsIGW"
      register: igw

    - name: Attach Internet Gateway to VPC
      ec2_vpc_igw:
        state: present
        internet_gateway_id: "{{ igw.gateway_id }}"
        vpc_id: "{{ vpc.vpc.id }}"
        region: "{{ region }}"

    - name: Debug IGW ID
      debug:
        var: igw

    - name: Create a Subnet
      ec2_vpc_subnet:
        state: present
        vpc_id: "{{ vpc.vpc.id }}"
        cidr: "{{ subnet_cidr_block }}"
        az: "{{ availability_zone }}"
        tags:
          Name: RailsSubnet
      register: subnet

    - name: Create Security Group
      ec2_group:
        name: "{{ security_group_name }}"
        description: "{{ security_group_description }}"
        vpc_id: "{{ vpc.vpc.id }}"
        rules:
          - proto: tcp
            ports:
              - 22
            cidr_ip: 0.0.0.0/0
      register: sg

    - name: Create a route table for the VPC
      ec2_vpc_route_table:
        vpc_id: "{{ vpc.vpc.id }}"
        tags:
          Name: PublicRouteTable
        routes:
          - dest: 0.0.0.0/0
            gateway_id: "{{ igw.gateway_id }}"
      register: route_table

    - name: Associate subnet with route table
      ec2_vpc_route_table:
        vpc_id: "{{ vpc.vpc.id }}"
        subnets:
          - "{{ subnet.subnet.id }}"
        route_table_id: "{{ route_table.route_table.id }}"

    - name: Launch an EC2 instance
      amazon.aws.ec2_instance:
        name: "RailsServer"
        key_name: "{{ key_name }}"
        instance_type: "{{ instance_type }}"
        image_id: "{{ ami_id }}"
        wait: true
        region: "{{ region }}"
        network:
          vpc_subnet_id: "{{ subnet.subnet.id }}"
          assign_public_ip: true
          security_group: "{{ sg.group_id }}"
Check Ansible Collection Version
$ ansible-galaxy collection list
Ansible Collection Version
# /Users/bparanj/.ansible/collections/ansible_collections
Collection Version
---------- -------
amazon.aws 7.4.0  

I am using the latest version. You must refer the latest Ansible AWS EC2 module docs to fix any issues with the playbook. The examples at the bottom are very useful for quick reference.

I had to iterate many times before I got the playbook working. I had to create the VPC, subnet, security group, and then finally launch the EC2 instance. I had to use the wait flag to make sure the EC2 instance was created before I could use it.

SSH Connection Problem

This playbook for some reason does not work when connecting to the EC2 instance using a SSH key. When EC2 instance is created manually, I am able to connect to it using SSH. The code that uses boto3 Python SDK for AWS does not have the SSH connection problem.

Python SDK vs Ansible

Ansible is useful for those who have some devops experience. As a developer I find boto3 to be easier to use than Ansible. Ansible internally uses boto3. The advantage of using boto3 is the flexibility. However you have to write code to make it idempotent. Ansible is idempotent by default. You also have to manually cleanup the resources when you iterate on your boto3 project. By default you can only have 5 VPCs.

Here is the boto3 project that I am using to experiment with EC2. It can create an EC2 instance, assign a key pair and I am able to SSH into EC2 using that key pair.

Why Python SDK instead of Ruby SDK? The main reason is that the community is bigger and it has more momentum. You can see it in the github repo. I am no longer sticking to one favorite programming language. Main reason is that AI tools like Github Copilot, Claude and ChatGPT make it easier to experiment in any language. I am no longer bogged down by syntax of any language.