Contents:
Introduction
What is Terraform and why it is the easiest configuration management tool?
Creating root modules
Creating chile modules
Steps for execution
AWS services console output
Introduction:
In this project, we will deploy the AWS services like VPC, SUBNET, NIC, Security Group and EC2 instances using Terraform as a configuration management tool. Also, we will see the concept of the Root and Child Module which is being used.
Terraform:
Terraform is an open-source infrastructure as code (IaC) tool developed by HashiCorp. It allows you to define and manage your infrastructure resources in a declarative manner. With Terraform, you can describe your infrastructure requirements using a simple and human-readable language, called HashiCorp Configuration Language (HCL), or optionally, JSON.
The main goal of Terraform is to enable you to provision and manage infrastructure resources across various cloud providers (such as AWS, Azure, Google Cloud, etc.) as well as on-premises systems. It follows the principle of infrastructure as code, where you define your infrastructure resources in a text file, version control it, and treat it like any other code artifact.
Here are some key reasons why Terraform is commonly used as a DevOps tool:
Infrastructure as Code (IaC): Terraform allows you to define your infrastructure as code, enabling you to version control, review, and collaborate on infrastructure changes, just like you would with application code. This approach brings automation, reproducibility, and consistency to your infrastructure provisioning process.
Multi-Cloud and Hybrid Cloud Support: Terraform provides a consistent workflow to manage resources across multiple cloud providers and on-premises systems. This allows you to adopt a multi-cloud or hybrid cloud strategy, leveraging the best features and services from different providers without being locked into a single platform.
Declarative Syntax: With its declarative syntax, Terraform allows you to define the desired state of your infrastructure. It automatically figures out the execution plan to reach that desired state, taking care of resource dependencies and order of operations. This simplifies infrastructure management and reduces the risk of configuration drift.
Infrastructure Lifecycle Management: Terraform manages the entire lifecycle of your infrastructure resources, including provisioning, updating, and destroying. It keeps track of the current state of your infrastructure and performs the necessary changes to bring it in line with your desired state. This helps with maintaining consistency and simplifies resource management.
Infrastructure Orchestration: Terraform provides the ability to orchestrate complex infrastructure setups by defining dependencies between resources, creating resource graphs, and applying changes in a controlled manner. This allows you to manage complex infrastructure deployments, such as multi-tier applications, networking, security groups, and more.
Community and Ecosystem: Terraform has a large and active community, which means there is a wealth of shared knowledge, best practices, and community-contributed modules available. The ecosystem around Terraform includes a vast collection of pre-built modules and plugins that can be leveraged to accelerate infrastructure provisioning and configuration.
Overall, Terraform simplifies and automates the process of infrastructure provisioning and management, bringing agility, scalability, and consistency to your infrastructure-as-code practices in a multi-cloud or hybrid cloud environment.
Root and Child Modules in Terraform:
In Terraform, root modules and child modules are concepts used to organize and modularize your infrastructure code. Let's explore each of them:
- Root Modules: A root module is the top-level module in your Terraform configuration. It represents the entry point of your infrastructure code and can contain one or more child modules. The root module typically defines the overall infrastructure architecture, global settings, and variables that are shared across multiple child modules.
The root module is usually represented by the main.tf file in your Terraform project and is responsible for initializing the provider configuration, declaring variables, and defining resources that are not specific to any particular module.
- Child Modules: Child modules are reusable components that encapsulate a set of resources and configurations for a specific infrastructure component or functionality. They provide a way to break down your infrastructure code into smaller, manageable pieces, promoting reusability and modularity.
Child modules are typically defined in separate directories, each containing its own set of Terraform configuration files (such as main.tf, variables.tf, outputs.tf). These modules can be referenced and used within the root module or other child modules.
Child modules allow you to abstract complex configurations into self-contained units, making it easier to maintain, test, and reuse infrastructure code across different projects or environments.
Using root and child modules in Terraform offers several benefits, including:
Modularity: Modules allow you to break down your infrastructure code into smaller, reusable components, making it easier to manage and maintain.
Code Organization: Modules provide a logical structure to your Terraform project, promoting organization and readability.
Reusability: By creating modular components, you can reuse them across different projects, and environments, or even share them with the community.
Encapsulation: Modules encapsulate resources and configurations, making it easier to manage their state and apply changes.
Dependency Management: Modules can define input and output variables, enabling dependency management and establishing connections between different components of your infrastructure.
When using root and child modules, it's important to define clear interfaces (input variables and output values) between them to ensure proper communication and avoid tight coupling. Additionally, leveraging module best practices and following naming conventions can enhance the maintainability and scalability of your Terraform projects.
The File Structure should look like this:
Root Modules
Main.tf file content
#initial
module "vpc" {
source = "./modules/aws_vpc"
vpc_cidr = var.vpc_cidr
vpc_tag = var.vpc_tag
}
module "subnet" {
source = "./modules/aws_subnet"
vpc_id = module.vpc.vpc_id
subnet_cidr = var.subnet_cidr
subnet_name = var.subnet_name
}
module "sg" {
source = "./modules/aws_sg"
sg_name = var.sg_name
sg_description = var.sg_description
vpc_id = module.vpc.vpc_id
}
module "nic" {
source = "./modules/aws_nic"
subnet_id = module.subnet.subnet_id
private_ips = var.private_ips
nic_name = var.nic_name
}
module "instance" {
source = "./modules/aws_instance"
instance_ami = var.instance_ami
instance_type = var.instance_type
nic_id = module.nic.nic_id
instance_name = var.instance_name
}
This Terraform code is using modules to provision resources in an AWS environment. Each module represents a specific resource or a set of related resources. Let's go through each module and explain its purpose:
vpc: This module is defined using the
aws_vpc
module located in the "./modules/aws_vpc" directory. It creates a Virtual Private Cloud (VPC) in AWS. The module expects two input variables:vpc_cidr
(the CIDR block for the VPC) andvpc_tag
(tags to assign to the VPC).subnet: This module is defined using the
aws_subnet
module located in the "./modules/aws_subnet" directory. It creates a subnet within the VPC created by thevpc
module. The module expects three input variables:vpc_id
(the ID of the VPC),subnet_cidr
(the CIDR block for the subnet), andsubnet_name
(the name to assign to the subnet).sg: This module is defined using the
aws_sg
module located in the "./modules/aws_sg" directory. It creates a security group within the VPC. The module expects three input variables:sg_name
(the name to assign to the security group),sg_description
(a description of the security group), andvpc_id
(the ID of the VPC).nic: This module is defined using the
aws_nic
module located in the "./modules/aws_nic" directory. It creates a network interface within the subnet created by thesubnet
module. The module expects four input variables:subnet_id
(the ID of the subnet),private_ips
(a list of private IP addresses to assign to the network interface),nic_name
(the name to assign to the network interface).instance: This module is defined using the
aws_instance
module located in the "./modules/aws_instance" directory. It creates an EC2 instance within the VPC, using the specified Amazon Machine Image (AMI) and instance type. The module expects four input variables:instance_ami
(the ID of the AMI to use),instance_type
(the type of EC2 instance to launch),nic_id
(the ID of the network interface created by thenic
module), andinstance_name
(the name to assign to the EC2 instance).
Variable.tf file content
#initial
variable "vpc_cidr" {
type = string
default = "172.16.0.0/16"
}
variable "vpc_tag" {
type = map(any)
default = {
"Name" = "my_vpc"
}
}
variable "subnet_cidr" {
type = string
default = "172.16.10.0/24"
}
variable "subnet_name" {
type = map(any)
default = {
"Name" = "my_subnet"
}
}
variable "sg_name" {
type = map(any)
default = {
"Name" = "allow_tls"
}
}
variable "sg_description" {
type = string
default = "allow_tls inbound traffic"
}
variable "nic_name" {
type = map(any)
default = {
"Name" = "my_nic"
}
}
variable "private_ips" {
type = list(string)
default = [ "172.16.10.100" ]
}
variable "instance_ami" {
type = string
default = "ami-03a0c45ebc70f98ea"
}
variable "instance_type" {
type = string
default = "t2.micro"
}
variable "instance_name" {
type = map(any)
default = {
"Name" = "Prod-Server"
}
}
The variable.tf
file contains variable declarations for the Terraform code. These variables allow you to customize the configurations of your infrastructure when running the Terraform scripts. Let's go through each variable and explain its purpose:
vpc_cidr: This variable is of type
string
and represents the CIDR block for the VPC. It has a default value of "172.16.0.0/16".vpc_tag: This variable is of type
map(any)
and represents the tags to assign to the VPC. It is a map with a single key-value pair, where the key is "Name" and the value is "my_vpc".subnet_cidr: This variable is of type
string
and represents the CIDR block for the subnet. It has a default value of "172.16.10.0/24".subnet_name: This variable is of type
map(any)
and represents the name to assign to the subnet. It is a map with a single key-value pair, where the key is "Name" and the value is "my_subnet".sg_name: This variable is of type
map(any)
and represents the name to assign to the security group. It is a map with a single key-value pair, where the key is "Name" and the value is "allow_tls".sg_description: This variable is of type
string
and represents the description of the security group. It has a default value of "allow_tls inbound traffic".nic_name: This variable is of type
map(any)
and represents the name to assign to the network interface. It is a map with a single key-value pair, where the key is "Name" and the value is "my_nic".private_ips: This variable is of type
list(string)
and represents a list of private IP addresses to assign to the network interface. It has a default value of ["172.16.10.100"].instance_ami: This variable is of type
string
and represents the ID of the Amazon Machine Image (AMI) to use for the EC2 instance. It has a default value of "ami-03a0c45ebc70f98ea".instance_type: This variable is of type
string
and represents the type of the EC2 instance to launch. It has a default value of "t2.micro".
instance_name: This variable is of type map(any)
and represents the name to assign to the EC2 instance. It is a map with a single key-value pair, where the key is "Name" and the value is "Prod-Server".
Provider.tf contents:
#initial
provider "aws" {
# profile = "myaws" #####using the default profile at the moment#####
region = "us-east-2"
}
provider
: This block specifies the provider configuration for AWS."aws"
: This is the name of the provider, indicating that we are configuring AWS.region
: This parameter specifies the AWS region to operate in. In this case, the region is set to "us-east-2", which corresponds to the US East (Ohio) region.
The provider configuration is essential because it allows Terraform to authenticate and connect to your AWS account using your credentials. By specifying the region, you define the target AWS region where the resources will be provisioned.
Note that there is a commented line # profile = "myaws"
. This line is currently commented out, but it can be uncommented and set to the appropriate AWS profile name if you are using named profiles in your AWS credentials file. By specifying a profile, you can separate different sets of AWS credentials for different environments or purposes.
By including this provider configuration, you enable Terraform to manage AWS resources within the specified region using the configured AWS credentials.
Child Modules
AWS Modules
- AWS_VPC
main.tf file contents
#inital
resource "aws_vpc" "my_vpc" {
cidr_block = var.vpc_cidr
tags = var.vpc_tag
}
variable.tf file contents
#inital
variable "vpc_cidr" {
type = string
default = "172.16.0.0/16"
}
variable "vpc_tag" {
type = map(any)
default = {
"Name" = "my_vpc"
}
}
output.tf file contents
#inital
output "vpc_id" {
value = aws_vpc.my_vpc.id
}
- AWS_SUBNET
main.tf file contents
#inital
resource "aws_subnet" "my_subnet" {
vpc_id = var.vpc_id
cidr_block = var.subnet_cidr
tags = var.subnet_name
}
variable.tf file contents
#inital
variable "vpc_id" {
type = string
#as of now we don't have the vpc id
}
variable "subnet_cidr" {
type = string
default = "172.16.10.0/24"
}
variable "subnet_name" {
type = map(any)
default = {
"Name" = "my_subnet"
}
}
output.tf file contents
#inital
output "subnet_id" {
value = aws_subnet.my_subnet.id
}
- AWS_SG
main.tf file contents
#inital
resource "aws_security_group" "my_sg" {
name = "allow_tls"
description = var.sg_description
vpc_id = var.vpc_id
ingress {
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = var.sg_name
}
variable.tf file contents
#inital
variable "vpc_id" {
type = string
# as we don't have vpc id
}
variable "sg_name" {
type = map(any)
default = {
"Name" = "allow_tls"
}
}
variable "sg_description" {
type = string
default = "allow_tls inbound traffic"
}
- AWS_NIC
main.tf file contents
#inital
resource "aws_network_interface" "name" {
subnet_id = var.subnet_id
private_ips = var.private_ips
tags = var.nic_name
}
variable.tf file contents
#inital
variable "subnet_id" {
type = string
# as we don't have a subnet id
}
variable "nic_name" {
type = map(any)
default = {
"Name" = "my_nic"
}
}
variable "private_ips" {
type = list(string)
default = [ "172.16.10.100" ]
}
output.tf file contents
#inital
output "nic_id" {
value = aws_network_interface.name.id
}
- AWS_EC2_Instance
main.tf file contents
#inital
resource "aws_network_interface" "name" {
subnet_id = var.subnet_id
private_ips = var.private_ips
tags = var.nic_name
}
variable.tf file contents
#inital
variable "subnet_id" {
type = string
# as we don't have a subnet id
}
variable "nic_name" {
type = map(any)
default = {
"Name" = "my_nic"
}
}
variable "private_ips" {
type = list(string)
default = [ "172.16.10.100" ]
}
output.tf file contents
#inital
output "nic_id" {
value = aws_network_interface.name.id
}
Steps to execute:
Clone the git hub repo from https://github.com/kunalrepo/Terraform_withAWS.git into your local machine.
Go to the root main.tf file and open an integrated terminal from your favourite code editor, here we have used VS code for simplicity.
First, configure the AWS CLI in the integrated terminal by using the command
aws configure
and provide the access key id and secret key generated for an IAM user/role with sufficient privileges/permissions to deploy services on the AWS console. Keep the region as specified in the provider.tf file "us-east-2".Execute the command
terraform init
to initialize the Terraform project in the directory. It prepares the working directory for Terraform and also checks the vulnerability in the code.Execute the command
terraform plan -out=tfplan.out
to examine the Terraform configuration and determine what actions need to be taken to achieve the desired state of the infrastructure. Also saving the plan in the tfplan.out file as the best practices.Execute the command
terraform apply tfplan.out
to apply the changes specified in the Terraform configuration to create, modify, or delete resources in the infrastructure. It takes the execution plan generated byterraform plan
in the output file tfplan.out and performs the necessary actions to achieve the desired state.
AWS Console Output
my_vpc created in AWS console through terraform:
my_subnet created in AWS console through terraform:
security group created in AWS console through terraform:
Network ACL's created in AWS console through terraform:
Prod-server ec2 instance created in AWS console through Terraform:
NOTE: Please use terraform destroy
to remove all the infrastructure created to deploy AWS services otherwise it may cost unnecessary billing to your AWS account.
Reference: Please refer to the Hashicorp documentation for any syntax related information.