digitalspeed logo

How to Create and Configure a VPC on AWS Using Ansible; 8 STeps

vpc deployment on aws using ansible

Ansible, an open-source automation tool, simplifies cloud infrastructure management, making it easier to deploy, manage, and scale applications. When it comes to creating a Virtual Private Cloud (VPC) on AWS and setting up servers, Ansible stands out with its straightforward playbooks and powerful automation capabilities.

This guide will walk you through using Ansible to efficiently create a VPC on AWS and provision servers, ensuring a streamlined and error-free deployment process. As you leverage Ansible’s declarative language, you can achieve consistent and repeatable infrastructure setups, enhancing your cloud operations and reducing manual effort.

Prerequisite

  • Have an AWS account
  • GitHub account, git
  • Will we be creating multi-subnet VPC on AWS as shown in the diagram below

We will use Ohio throughout this project

Here are the steps;

STEP 1 : Create Ansible Server

Create an EC2 instance with the following details;

AMI: Ubuntu 22.04
Instance Type: t2.micro //free tier eligible
SecGrp: allow SSH on port 22
Create a new keypair

Add the code below to your user data

#!/bin/bash
apt update
apt install ansible -y

Create an IAM Role, give the instance these details for the role

Service: EC2
Policy: AdministratorAccess
Name: ansible-admin-role

Check out this post to learn more about the IAM role and ec2 instance

Connect to your instance using SSH using the key pair you created before 

Check out this post to learn how to connect to your ec2 instance

Confirm Ansible is installed on the instance

sudo apt install awscli -y
aws sts get-caller-identity

STEP 2 : Try Out PlayBook Code

The code below creates a new directory and a test playbook

mkdir vpc-stack-vprofile
cd vpc-stack-vprofile
vim test-aws.yml

Most people prefer Vim for accessing files on Linux but nano has always worked well for me so you can also use nano to create and edit the test-aws.yml file

Copy and paste this line into the file

- hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: sample ec2 key
      ec2_key:
        name: my_keypair
        region: us-east-2

This Ansible playbook is designed to create an EC2 key pair on AWS. It operates locally on the ‘localhost’ and does not gather system facts. The playbook includes a single task named “sample ec2 key” that utilizes the ec2_key module. This task generates a key pair named “my_keypair” in the specified AWS region “us-east-2”. The key pair is essential for securely accessing and managing EC2 instances. This simple yet effective playbook ensures that your key pairs are consistently created and managed through automation.

To run the playbook we need to install either pip(python package manager) or apt. Since we use AWS, Boto is Python SDK in AWS. search boto in apt.

Run the code below to search and install Boto

ansible-playbook test-aws.yml
sudo apt search boto
sudo apt install python3-boto3 -y

We need to modify the code so we can download the key pair to our local machine

we will be able to see our file but we have to add another task to store the key in our local machine as it wasn’t downloaded in our machine. From the previous run, we know the exact path to get the private_key from the JSON file.

- hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: sample ec2 key
      ec2_key:
        name: my_keypair
        region: us-east-2
      register: keyout

    - debug:
        var: keyout

    - name: store login key
      copy:
        content: "{{keyout.key.private_key}}"
        dest: ./sample-key.pem
      when: keyout.changed

Our key pair is stored

STEP 3 : Creating PlayBook Variables

Create a GitHub repository to store our VPC playbook or you can use any other Git tool

We will create two variable files, one for our VPC and another for our bastion host

Copy the content below and post on vpc_setup with no file extension

vpc_name: "Vprofile-vpc"

#VPC Range
vpcCidr: '172.20.0.0./16'

#Subnets Range
PubSub1Cidr: 172.20.1.0/24
PubSub2Cidr: 172.20.2.0/24
PubSub3Cidr: 172.20.3.0/24
PrivSub1Cidr: 172.20.4.0/24
PrivSub2Cidr: 172.20.5.0/24
PrivSub3Cidr: 172.20.6.0/24

#Region Name
region: "us-east-2"

#Zone Names
zone1: us-east-2a
zone2: us-east-2b
zone3: us-east-2c

state: present

Copy the content below and paste it on the bastion_host file with no extension.

bastion_ami: ami-0beaa649c482330f7 # Amazon Linux-2 AMI-ID from us-east-2 region
region: us-east-2
MYIP: IP_address_of_your_laptop/32
keyName: vprofile-key
instanceType: t2.micro

When the files are ready commit/push to them GitHub, clone this repository in our Ansible server.

Git clone <your github repo>

STEP 4 : Create a VPC PlayBook

Create a VPC playbook ‘vpc_setup’ in your main directory, and commit the changes to GitHub

- hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Import VPC Variables
      include_vars: vars/vpc_setup

    - name: Create Vprofile VPC
      ec2_vpc_net:
        name: "{{vpc_name}}"
        cidr_block: "{{vpcCidr}}"
        region: "{{region}}"
        dns_support: yes
        dns_hostnames: yes
        tenancy: default
        state: "{{state}}"
      register: vpcout

Pull the changes to your ansible server, I will show you the code to do this just this once after which you check this step again because we will be doing this a lot after now.

git pull origin main  //you already have the repo in your directory
executing ansible playbook

Change the Directory to your repo

Run the vpc_setup playbook

ansible-playbook vpc_setup.yml

Check your console to confirm the changes in your VPC

STEP 5 : Subnet PlayBook

Add the extra config to your vpc_setup.yml, I mean you should append it to the already ensure the indentation is consistent

#    - debug:
#        var: vpcout

     - name: create Public Subnet 1 in Zone1
       ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone1}}"
        state: "{{state}}"
        cidr: "{{PubSub1Cidr}}"
        map_public: yes
        tags:
            Name: vprofile-pubsub1
      register: pubsub1_out

    - name: create Public Subnet 2 in Zone2
      ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone2}}"
        state: "{{state}}"
        cidr: "{{PubSub2Cidr}}"
        map_public: yes
        tags:
          Name: vprofile-pubsub2
      register: pubsub2_out

    - name: create Public Subnet 3 in Zone3
      ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone3}}"
        state: "{{state}}"
        cidr: "{{PubSub3Cidr}}"
        map_public: yes
        tags:
          Name: vprofile-pubsub3
      register: pubsub3_out

    - name: create Private Subnet 1 in Zone1
      ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone1}}"
        state: "{{state}}"
        cidr: "{{PrivSub1Cidr}}"
        map_public: yes
        tags:
            Name: vprofile-privsub1
      register: privsub1_out

    - name: create Private Subnet 2 in Zone2
      ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone2}}"
        state: "{{state}}"
        cidr: "{{PrivSub2Cidr}}"
        map_public: yes
        tags:
          Name: vprofile-privsub2
      register: privsub2_out

    - name: create Private Subnet 3 in Zone3
      ec2_vpc_subnet:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        az: "{{zone3}}"
        state: "{{state}}"
        cidr: "{{PrivSub3Cidr}}"
        map_public: yes
        tags:
          Name: vprofile-privsub3
      register: privsub3_out

Commit your changes to your GitHub and poll the changes to your server

Check your subnet management console, confirm the changes

STEP 6 : Internet Gateway and Public Route Table

Add or append the config to your vpc_setup file

Commit the changes to your playbook

    - name: Internet Gateway Setup
      ec2_vpc_igw:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        state:  "{{state}}"
        tags:
          Name: vprofile-igw
      register: igw_out

    - name: Setup Public Subnet Route Table
      ec2_vpc_route_table:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        tags:
          Name: Vprofile-PubRT
        subnets:
            - "{{ pubsub1_out.subnet.id }}"
            - "{{ pubsub2_out.subnet.id }}"
            - "{{ pubsub3_out.subnet.id }}"
        routes:
          - dest: 0.0.0.0/0
            gateway_id: "{{ igw_out.gateway_id }}"
      register: pubRT_out

Run the playbook, Check the AWS console

STEP 7 : NAT Gateway Private Route Table

Add the info below and commit the changes to your GitHub

    - name: NAT Gateway1 Setup and allocate new EIP if NATGW does not exist yet in the subnet
      ec2_vpc_nat_gateway:
        subnet_id: "{{ pubsub1_out.subnet.id }}"
        region: "{{region}}"
        state:  "{{state}}"
        wait: yes
        if_exist_do_not_create: yes
      register: natgw1_out

    - name: Setup Private Subnet Route Table
      ec2_vpc_route_table:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        tags:
          Name: Vprofile-PrivRT1
        subnets:
            - "{{ privsub1_out.subnet.id }}"
            - "{{ privsub2_out.subnet.id }}"
        routes:
          - dest: 0.0.0.0/0
            gateway_id: "{{ natgw1_out.nat_gateway_id }}"
      register: privRT1_out

    - name: NAT Gateway3 Setup and allocate new EIP if NATGW does not exist yet in the subnet
      ec2_vpc_nat_gateway:
        subnet_id: "{{ pubsub3_out.subnet.id }}"
        region: "{{region}}"
        state:  "{{state}}"
        wait: yes
        if_exist_do_not_create: yes
      register: natgw3_out

    - name: Setup Private Subnet Route Table2
      ec2_vpc_route_table:
        vpc_id: "{{vpcout.vpc.id}}"
        region: "{{region}}"
        tags:
          Name: Vprofile-PrivRT3
        subnets:
            - "{{ privsub3_out.subnet.id }}"
        routes:
          - dest: 0.0.0.0/0
            gateway_id: "{{ natgw3_out.nat_gateway_id }}"
      register: privRT3_out

    - debug:
        var: "{{ item }}"
      loop:
        - vpcout.vpc.id
        - pubsub1_out.subnet.id
        - pubsub2_out.subnet.id
        - pubsub3_out.subnet.id
        - privsub1_out.subnet.id
        - privsub2_out.subnet.id
        - privsub3_out.subnet.id
        - igw_out.gateway_id
        - pubRT_out.route_table.id
        - natgw1_out.nat_gateway_id
        - privRT1_out.route_table.id
        - natgw3_out.nat_gateway_id
        - privRT3_out.route_table.id

    - set_fact:
        vpcid: "{{ vpcout.vpc.id }}"
        pubsub1id: "{{ pubsub1_out.subnet.id }}"
        pubsub2id: "{{ pubsub2_out.subnet.id }}"
        pubsub3id: "{{ pubsub3_out.subnet.id }}"
        privsub1id: "{{ privsub1_out.subnet.id }}"
        privsub2id: "{{ privsub2_out.subnet.id }}"
        privsub3id: "{{ privsub3_out.subnet.id }}"
        igwid: "{{ igw_out.gateway_id }}"
        pubRTid: "{{ pubRT_out.route_table.id }}"
        natgw1id: "{{ natgw1_out.nat_gateway_id }}"
        privRT1id: "{{ privRT1_out.route_table.id }}"
        natgw3id: "{{ natgw3_out.nat_gateway_id }}"
        privRT3id: "{{ privRT3_out.route_table.id }}"
        cacheable: yes

    - name: Create variables file for vpc Output
      copy:
        content: "vpcid: {{ vpcout.vpc.id }}\npubsub1id: {{ pubsub1_out.subnet.id }}\npubsub2id: {{ pubsub2_out.subnet.id }}\npubsub3id: {{ pubsub3_out.subnet.id }}\nprivsub1id: {{ privsub1_out.subnet.id }}\nprivsub2id: {{ privsub2_out.subnet.id }}\nprivsub3id: {{ privsub3_out.subnet.id }}\nigwid: {{ igw_out.gateway_id }}\npubRTid: {{ pubRT_out.route_table.id }}\nnatgw1id: {{ natgw1_out.nat_gateway_id }}\nprivRT1id: {{ privRT1_out.route_table.id }}\nnatgw3id: {{ natgw3_out.nat_gateway_id }}\nprivRT3id: {{ privRT3_out.route_table.id }}"

        dest: vars/output_vars

Pull your changes to your Ansible servers and run the vpc_setup.yml, confirm that the resources have been created

The result should be that Ansible generates a file output_vars on your vars folder. Run ls ./vars to confirm this

Commit the new file or changes to your GitHub repo using your GitHub credentials and merge it with the main directory

git add .
git commit -m "your commit message"
git config --global user.name "your username"
git config --global user.email "your email"
git push origin main
git pull origin main

You should see your output_vars files in your vars folder. Alternatively, you can copy the output_vars file content and paste it into a new file in the same location on your local git CLI commit it, and push it to your GitHub, ensure you also commit it on your Ansible server before you pull from GitHub else you will merge issues

After your Ansible server repo and your GitHub repo have become consistent (No distinct branch) can now carry out the final step to create your Bastion host

STEP  8 : Bastion Host

Create your bastion host playbook “bastion_host.yml’ and put in the content below

Commit your changes to GitHub and pull it on your Ansible server run the playbook 

Ansible-playbook bastion_host.yml

Check through all your resources created; EC2, VPC, subnets, NAT Gateway, Internet Gateway, Elastic IP, etc confirm you can see them and use it

STEP 9 : Delete the Resources

Delete all the resources created to reduce the cost you will accumulate

Don’t fail to delete; EC2 instances, NAT Gateway, Elastic IP, etc. These are resources that cost lots of money.

You can check out the ansible AWS documentation here for more details   https://docs.ansible.com/ansible/2.9/modules/list_of_cloud_modules.html

Check the GitHub repository for the full code https://github.com/willie191998/ansible-vpc-setup/tree/main

Conclusion

We have been able to configure infrastructure on AWS using Ansible, let’s understand how Ansible differs from Terraform, Terraform is an open-source Infrastructure as Code (IaC) tool that focuses on provisioning and managing infrastructure on various cloud providers, including AWS.

It uses your AWS credentials to create and manage AWS services through its API.

Ansible, on the other hand, primarily focuses on configuration management, application deployment, and task automation. It executes its infrastructure configuration tasks internally on AWS instances or other remote systems using SSH.

Check out my post on How to create and upload object to an S3 bucket using Terraform

Please share this article at the top of the page under the share this button. Thanks

Recent Post

Send Us A Message

Related Post

Join our newsletter to stay updated

digitalspeed-logo

At DIGITALSPEED, you can get updates, reviews and learn about new digital tools and features on existing tools. check us on social media.

Get In Touch

Lagos, Nigeria

DIGITALSPEED © All Rights Reserved.

2024

Scroll to Top

Seach for Articles