Build a Reusable VPC Module and Deploy an EC2 Instance

Mercy AderemiMercy Aderemi
7 min read

Introduction

Terraform is an open-source Infrastructure as Code (IaC) tool that enables you to define and provision infrastructure using a declarative configuration language. One of Terraform's powerful features is its support for modules, which allow you to encapsulate and reuse configurations across projects.

In this guide, we'll walk through creating a basic Terraform module to provision a Virtual Private Cloud (VPC) and a subnet in AWS. We'll then use this module in a main configuration to launch an EC2 instance.

Understanding Terraform Modules

Before we start building, it’s helpful to understand what Terraform modules are and how they fit into your infrastructure code.

A module in Terraform is a container that groups multiple related resources. Every Terraform configuration includes a root module, which is simply the set of .tf files in your main working directory. To keep your code organized and reusable, the root module can call child modules—separate directories or repositories that package specific infrastructure components.

Modules can come from various sources:

  • Local modules: Stored within your project folder (e.g., ./modules/vpc).

  • Public modules: Shared on the Terraform Registry.

  • Remote modules: Hosted in Git repositories or cloud storage.

In this tutorial, we’ll create a local module for a VPC and subnet. Our root configuration will then call this module and also deploy an EC2 instance.

Here’s how the root module references the VPC module:

module "vpc" {
  source = "./modules/vpc"
  region = var.main_region
}

This structure keeps your VPC setup clean, reusable, and easier to manage across projects.

Prerequisites

Before we begin, ensure you have the following installed and configured on your Linux virtual machine:

  1. AWS CLI: This tool allows you to interact with AWS services from the command line. Install it using:

     sudo snap install aws-cli --classic
    

    After installation, configure it with:

     aws configure
    

    You'll need your AWS Access Key ID and Secret Access Key, which you can generate from the AWS Management Console.
    These steps are also available in the official AWS documentation.

  2. Terraform: Install Terraform by following the official guide.

    Verify the installation with:

     terraform version
    

    Ensure that the version returned is compatible with your project requirements.

Step-by-Step Guide

Step 1: Set Up the Project Directory Structure

Organizing your project directory from the start is essential for clarity and maintainability. Begin by creating the main project directory and navigating into it. You can choose any name you prefer for the directory.

mkdir terraform_project
cd terraform_project

This structure clearly separates the root configuration from modules, making the project easier to manage and reuse.

Here is how your directory should look after setup:

Step 2: Create the VPC Module Directory

Inside the modules/ directory, create a vpc module:

mkdir -p modules/vpc
cd modules/vpc

The modules/vpc folder will contain all the configuration files related to your Virtual Private Cloud (VPC). Organizing resources into modules like this helps keep your project clean and makes it easier to reuse and manage components independently.

Here is a screenshot of what your results should look like:

Step 3: Create the VPC Module Files

Now that you’ve created the modules/vpc directory, the next step is to add the necessary configuration files that will define how this module works. This VPC module will consist of three core files:

  • main.tf – This file defines the resources for the VPC module. This is where you define the actual infrastructure resources, like your VPC definition and any related resources like subnets, internet gateways, etc.

  • variables.tf – This file contains all the input variables that your module needs in order to function. For example, you'll likely want to pass in a CIDR block and a name for the VPC, rather than hardcoding those values inside the module. Using variables makes the module reusable and customizable.

  • outputs.tf – This is where you define which values should be exposed by the module once it's created i.e. the outputs of the module. Typically, you'd want to output things like the VPC ID, which you might need to reference in other modules (like subnets or route tables) later on.

Use Vim (or any other CLI editor) to create the necessary files:

vim main.tf
vim variables.tf
vim outputs.tf

Define the VPC in main.tf. This file will contain the actual AWS VPC resource configuration.

Open main.tf in Vim.

vim main.tf

You’ll now be inside the Vim editor. Here’s how to use it:

  • Press i to enter Insert Mode (so you can type text).

Here is a basic example you can use or modify. You can copy and paste the following configuration:

provider "aws" {
  region = var.region
}

resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "this" {
  vpc_id     = aws_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

data "aws_ssm_parameter" "this" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
  • When you’re done, press Esc to leave Insert Mode.

  • Type :wq (write and quit), then press Enter to save and exit Vim.

Here is a screenshot of what your results should look like:

Declare Inputs in variables.tf. Here you'll define the variables used in main.tf.

Open variables.tf in Vim.

vim variables.tf

You’ll now be inside the Vim editor.

Here is a basic example you can use or modify. You can copy and paste the following configuration:

variable "region" {
  type    = string
  default = "us-east-1"
}

Define Outputs in outputs.tf

Open outputs.tf in Vim.

vim outputs.tf

You’ll now be inside the Vim editor.

Here is a basic example you can use or modify. You can copy and paste the following configuration:

output "subnet_id" {
  value = aws_subnet.this.id
}

output "ami_id" {
  value = data.aws_ssm_parameter.this.value
}

Step 4: Create the Root Module Files

We’ve been working inside the modules/vpc directory to build a reusable module. Now that the module is ready, the next step is to define how this module will be used in your actual infrastructure.

That happens in the root Terraform configuration — where you specify inputs, outputs, and any additional resources that tie everything together.

First, navigate back to the main project directory:

We've been working inside the modules/vpc directory. , the next step is to connect it to your root Terraform configuration.

First, navigate back to the main project directory to define the root Terraform configuration that will use this module:

cd ../../

This takes you back to terraform_project/.

The next step is to configure the provider and module in main.tf

Create a file named main.tf. This file integrates the VPC module and defines an EC2 instance.

Here is a basic example you can use or modify. You can copy and paste the following configuration:

variable "main_region" {
  type    = string
  default = "us-east-1"
}

provider "aws" {
  region = var.main_region
}

module "vpc" {
  source = "./modules/vpc"
  region = var.main_region
}

resource "aws_instance" "my-instance" {
  ami           = module.vpc.ami_id
  subnet_id     = module.vpc.subnet_id
  instance_type = "t2.micro"
}

Here is a screenshot of what your results should look like:

After creating the main.tf file, the next step is to define outputs in outputs.tf. This file defines the output for the EC2 instance's private IP.

output "PrivateIP" {
  description = "Private IP of EC2 instance"
  value       = aws_instance.my-instance.private_ip
}

Here is a screenshot of what your results should look like:

Step 5: Deploying and Testing the Module

Format the code:

terraform fmt -recursive

This command ensures consistent formatting across all Terraform files.

Initialize Terraform:

terraform init

This command initializes the working directory and downloads necessary provider plugins.

Here is a screenshot of what your results should look like:

Validate the configuration:

terraform validate

This command checks the configuration for syntax errors and internal consistency.

Here is a screenshot of what your results should look like:

Review the execution plan:

terraform plan

This command applies the changes required to reach the desired state without prompting for confirmation.

Here is a screenshot of what your results should look like:

Apply the configuration:

terraform apply --auto-approve

This command displays the actions Terraform will take to achieve the desired state.

Here is a screenshot of what your results should look like:

List the state resources:

terraform state list

This command lists all resources tracked in the Terraform state.

Here is a screenshot of what your results should look like:

Destroy the infrastructure:

terraform destroy

This command destroys all resources managed by Terraform.

Here is a screenshot of what your results should look like:

Conclusion

In this guide, we created a basic Terraform module and used it to deploy a VPC and EC2 instance. This approach keeps infrastructure organized and easier to manage as it grows.

0
Subscribe to my newsletter

Read articles from Mercy Aderemi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mercy Aderemi
Mercy Aderemi