Ansible create AWS VPC

How to Create AWS VPC playbook using Ansible

In this tutorial, we are going to create a VPC using Ansible in AWS. Our VPC will include a router, subnet, IGW, Security Group and definitely an EC2 instance.

This is the topology we would create. (I have mentioned the Ansible Module so as to make it easier)

Prerequisites

  • IAM User with the required permission.
  • Boto installed. (Ansible uses the Boto Python library to handle AWS management)

If you are planning to improve your IAM security, please read my guide on it.

1. Creating a custom Role

We would first create an AWS VPC Role.

ansible-galaxy init aws-vpc

We would be using the two files below from our aws-vpc role.

- tasks
   - main.yml
- vars
   -main.yml

Variables

We will place the variables in the vars/main.yml file. This allows the role to be easily reusable. Modify the variables depending on your requirement but replace your AWS Access and Secret key.

---
#vars/main.yml
 
aws_access_key: ""
aws_secret_key: ""
region: "us-east-1"
 
# VPC
vpc_cidr: 10.10.0.0/24
vpc_name: "Ansible VPC"
 
# Subnet
subnet_name: "Ansible Subnet"
subnet_cidr: 10.10.0.0/26
 
# Internet Gateway Name
igw_name: "Traffic IGW"
 
securitygroup_name: "Ansible Security Group"
 
ec2_tag: "WebServer"
 
#The local path to which we would save our EC2 Private Key
ec2_key_directory: "/home/ansible/roles/aws-vpc/"
keypair_name: "ec2_key_pair"

 

Do not store your AWS credentials as a variable! Follow the best practice

2. Creating the VPC

Our VPC will include the following components.

  • VPC
  • Subnet
  • Router
  • IGW (Internet Gateway)
  • Security Group
  • EC2 Instance

a) VPC

To create a VPC, we have to use the ec2_vpc_net module. We will register the module returned data in the “vpc” variable.

- name: create VPC
  ec2_vpc_net:
    name: "{{ vpc_name }}"
    cidr_block: "{{ vpc_cidr }}"
    region: "{{ region }}"
    state: present
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
  register: vpc

b) Creating a subnet and associating it with an Internet Gateway

Now that we have successfully created a VPC. We would need to create a subnet and an IGW. We need an IGW so that our public subnet can access the internet.

In our subnet, we have set map_public to “YES”. This means instances created in this subnet is associated with a public IP address.

- name: associate subnet to the VPC
  ec2_vpc_subnet:
   state: present
   vpc_id: "{{ vpc_id }}"
   region: "{{ region }}"
   cidr: "{{ subnet_cidr }}"
   aws_access_key: "{{ aws_access_key }}"
   aws_secret_key: "{{ aws_secret_key }}"
   map_public: yes
   resource_tags:
     Name: "{{ subnet_name }}"
  register: subnet
 
- name: create IGW
  ec2_vpc_igw:
   vpc_id: "{{ vpc_id }}"
   region: "{{ region }}"
   aws_access_key: "{{ aws_access_key }}"
   aws_secret_key: "{{ aws_secret_key }}"
   state: "present"
   tags:
     Name: "{{ igw_name }}"
  register: igw

c) Routing traffic to IGW and creating a security group

Now, we would need to route the traffic to the IGW via the route table and create a security group.

- name: Route IGW
   ec2_vpc_route_table:
    vpc_id: "{{ vpc_id }}"
    region: "{{ region }}"
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    subnets:
      - "{{ subnet.subnet.id }}"
    routes:
      - dest: 0.0.0.0/0
        gateway_id: "{{ igw.gateway_id  }}"
    tags:
      Name: "{{ route_name }}"
 
# update the CIDR address in the SSH port section.
 
 - name: Create Security Group
   ec2_group:
    name: Web DMZ
    description: DMZ Security Group
    vpc_id: "{{ vpc_id }}"
    region: "{{ region }}"
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    rules:
      - proto: tcp
        ports:
        - 80
        cidr_ip: 0.0.0.0/0
      - proto: tcp
        ports:
        - 22
        cidr_ip: 0.0.0.0/0
   register: security_group

d) Creating an EC2 Key Pair and an EC2 instance

This is our last step, we will create an EC2 Key Pair and an EC2 instance.

Key points:

  • if you already have an EC2 key pair, skip the EC2 key pair and update the key_name with the name of the key pair.
  • exact_count is important! It determines the number of instances is created.
  • Image ID can be found on AWS Marketplace.
- name: create a new ec2 key pair
   ec2_key:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: ec2_keypair
    region: "{{ region }}"
   register: keypair
 
- name: Copy EC2 Private Key locally so it can be later on used to SSH into the instance
  copy: content="{{ keypair.key.private_key }}" dest={{ ec2_key_directory }}key.ppk
  when: keypair.changed == true
 
 - name: Create EC2 server
   ec2:
    image: ami-467ca739
    wait: yes
    instance_type: t2.micro
    region: "{{ region }}"
    group_id: "{{ security_group.group_id }}"
    vpc_subnet_id: "{{ subnet.subnet.id }}"
    key_name: "{{ keypair.key.name  }}"
    count_tag:
      Name: apacheserver
    exact_count: 1
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"

This creates a simple VPC but creating a complex VPC is still achievable with Ansible, as it provides all the necessary modules for a VPC creation in the AWS.

If you are provisioning your servers using Ansible, it would be good to have the AWS resource creation in Ansible as-well compared to CloudFormation. If the lack of GUI is a concern, have a look at Ansible Tower. Ansible Tower is a web-based interface which lets you manage and control Ansible roles.

While Ansible is a great tool, I highly recommend looking into Terraform or CloudFormation to manage cloud resources.  For example, Terraform has a module to create AWS VPC. How you use automation to provision cloud resources is a question that you might be often asked in a DevOps interview.

If you think something is missing in the post, please leave it as a comment!