Build a Reusable VPC Module and Deploy an EC2 Instance


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:
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.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 pressEnter
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.
Subscribe to my newsletter
Read articles from Mercy Aderemi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
