Skip to content

AWS with Terraform and Jenkins Pipeline

Published: (7 min read)  at 11:45 AM
TEXT

What is Terraform?

Terraform is an open-source infrastructure as code (IAC) platform for building, managing, and deploying production-ready environments. Terraform uses declarative configuration files to codify cloud APIs. Terraform is capable of managing both third-party services and unique in-house solutions.

What is Jenkins?

Jenkins is a free and open-source continuous integration and delivery (CI/CD) automation server. It aids in the automation of portions of the software development lifecycle, including as code development, testing, and deployment to numerous servers. CI/CD is a means of delivering apps to clients more often by incorporating automation into the app development process. CI/CD, in particular, adds continuous automation and monitoring across the app lifecycle, from integration and testing through delivery and deployment. Continuous Integration works by submitting tiny code chunks to your application’s codebase, which is maintained in a Git repository, and running a pipeline of scripts to build, test, and validate the code changes before merging them into the main branch.

What is Subnet?

A logical subdivision of an IP network is referred to as a subnet. Subnetting is the process of separating a network into two or more networks. The host component is identified by one part, while the network part is identified by the other.

Types of subnet:

In this article, I will explain how to create and manage the public and private subnets using terraform and create instance in the desired subnet.

Prerequisites:

Step 1:- Create a Provider

Since we are going to use AWS as our cloud provider, we are going to use the aws terraform provider and use the aws access and secret key as a variable which will be passed from the Jenkinsfile.

providers.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.70.0"
    }
  }
}provider "aws" {
    access_key = var.access_key
    secret_key = var.secret_key
    region     = var.region
}

Step 2:- Create a VPC

vpc.tf

resource "aws_vpc" "development-vpc" {
    cidr_block = var.cidr_blocks[0].cidr_block
    tags = {
        Name = "${lower(var.vendor)}-${lower(var.environment)}-vpc"
    }
}data "aws_vpc" "existing_vpc" {
    #"query existing resources"
    id = aws_vpc.development-vpc.id
}

Step 3:- Create Public and Private Subnet

subnets.tf

locals {
    availability_zones = "${var.region}a"
}resource "aws_subnet" "public-subnet-1" {
    vpc_id     = data.aws_vpc.existing_vpc.id
    cidr_block = var.cidr_blocks[1].cidr_block
    availability_zone = local.availability_zones
    tags = {
        Name = "${lower(var.vendor)}-${lower(var.environment)}-public-${local.availability_zones}"
    }
}resource "aws_subnet" "private-subnet-1" {
    vpc_id     = data.aws_vpc.existing_vpc.id
    cidr_block = var.cidr_blocks[2].cidr_block
    availability_zone = local.availability_zones
    tags = {
        Name = "${lower(var.vendor)}-${lower(var.environment)}-private-${local.availability_zones}"
    }
}

Step 4:- Create Internet and Nat Gateway

ig_natgw.tf

resource "aws_internet_gateway" "gw" {
  vpc_id = data.aws_vpc.existing_vpc.id
  tags = {
    Name = "${lower(var.vendor)}-${lower(var.environment)}-ig"
  }
}# CREATE ELASTIC IP WITH NAT GATEWAYresource "aws_eip" "lb" {
  depends_on    = [aws_internet_gateway.gw]
  vpc           = true
}resource "aws_nat_gateway" "natgw" {
  allocation_id = aws_eip.lb.id
  subnet_id     = aws_subnet.public-subnet-1.id
  depends_on = [aws_internet_gateway.gw]
  tags = {
    Name = "${lower(var.vendor)}-${lower(var.environment)}-nat-gw"
  }
}

Step 5:- Create a Route table for Public and Private Subnet

route-tables.tf

resource "aws_route_table" "route-table-public" {
  vpc_id = data.aws_vpc.existing_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
  tags = {
    Name = "${lower(var.vendor)}-${lower(var.environment)}-rt-public"
  }
}resource "aws_route_table" "route-table-private" {
  vpc_id = data.aws_vpc.existing_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.natgw.id
  }
  tags = {
    Name = "${lower(var.vendor)}-${lower(var.environment)}-rt-private"
  }
  lifecycle {
    ignore_changes = [
      route,
    ]
  }
}resource "aws_route_table_association" "route-table-public-association-1" {
  subnet_id      = aws_subnet.public-subnet-1.id
  route_table_id = aws_route_table.route-table-public.id
}resource "aws_route_table_association" "route-table-private-association-1" {
  subnet_id      = aws_subnet.private-subnet-1.id
  route_table_id = aws_route_table.route-table-private.id
}

Step 6:- Create Security Groups

security-groups.tf

resource "aws_security_group" "db-sg-grp" {
  name          = "${var.vendor}-${var.environment}-db-sg"
  description   = "Sg for DB"
  vpc_id        = data.aws_vpc.existing_vpc.idegress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }ingress {
    from_port   = 3306
    to_port     = 3306
    protocol    = "tcp"
    cidr_blocks = ["${aws_network_interface.private_network_interface.id}/32"]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["${aws_network_interface.private_network_interface.id}/32"]
  }
}# CREATE SG FOR App
resource "aws_security_group" "app-sg-grp" {
  name          = "${var.vendor}-${var.environment}-app-sg"
  description   = "Sg for app"
  vpc_id        = data.aws_vpc.existing_vpc.idegress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0" ]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0" ]
  }
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0" ]
  }
}

Step 7:- Create EC2 instances

ec2.tf

data "aws_ami" "latest_amazon_linux_img" {
  most_recent      = true
  owners           = ["amazon"]
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-gp2"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}resource "aws_network_interface" "private_network_interface" {
  subnet_id          = aws_subnet.public-subnet-1.id
  security_groups    = [aws_security_group.app-sg-grp.id]
  private_ips        = ["10.0.10.10"]
}resource "aws_instance" "app" {
    ami                       = data.aws_ami.latest_amazon_linux_img.id
    instance_type             = "t2.micro"
    root_block_device {
        volume_type         = "gp2"
        volume_size         = 30
    }
    associate_public_ip_address = true
    network_interface {
        network_interface_id = aws_network_interface.private_network_interface.id
        device_index = 0
    }
    key_name = "tests"
    tags = {
        Name = "${var.vendor}-${var.environment}-app"
    }
    lifecycle {
        ignore_changes = [
            ami,
        ]
    }
}resource "aws_network_interface" "network_interface" {
  subnet_id          = aws_subnet.private-subnet-1.id
  security_groups    = [aws_security_group.db-sg-grp.id]
  private_ips        = ["10.0.110.10"]
}resource "aws_instance" "db" {
    ami                       = data.aws_ami.latest_amazon_linux_img.id
    instance_type             = "t2.micro"
    root_block_device {
        volume_type         = "gp2"
        volume_size         = 50
    }
    network_interface {
        network_interface_id = aws_network_interface.network_interface.id
        device_index = 0
    }
    key_name = "tests"
    tags = {
        Name = "${var.vendor}-${var.environment}-db"
    }
    lifecycle {
        ignore_changes = [
            ami,
        ]
    }
}

Step 8:- Create Variables

variables.tf

variable "vendor" {
    type = string
}
variable "environment" {
    type = string
}
variable "region" {
    type = string
    default = "us-west-2"
}
variable "access_key" {
    type = string
}
variable "secret_key" {
    type = string
}variable "cidr_blocks" {
    description = "VPC CIDR BLOCK"
    type = list(object({
        cidr_block = string
    }))
}

Step 9:- Create tfvariables

terraform-dev.tfvars

This is the file which we can edit and change the values to the desired value.

vendor = "example"environment = "dev"cidr_blocks=[{cidr_block = "10.0.0.0/16"},{cidr_block = "10.0.10.0/24"},{cidr_block = "10.0.110.0/24"}]

Finally we will need the output of the Public IP for the application instance which can be gathered from the below code.

Step 10:- Create output

output.tf

output "ec2-app-public-ip" {
    value = aws_instance.app.public_ip
}

This will give us the public ip of our EC2 instance.

Step 11:- Create Jenkinsfile

So, now our entire code is ready. We need to run the below steps to create infrastructure.
Create a Jenkinsfile and add the following code.

pipeline {
    agent any
    parameters {
        string(name: 'AWS_ACCESS_KEY_ID', defaultValue: '', description: 'AWS Access Key ID')
        string(name: 'AWS_SECRET_ACCESS_KEY', defaultValue: '', description: 'AWS Secret Access Key')
        string(name: 'AWS_REGION', defaultValue: 'us-west-2', description: 'AWS Region')
    }
    environment {
        access_key = "${params.AWS_ACCESS_KEY_ID}"
        secret_key = "${params.AWS_SECRET_ACCESS_KEY}"
        region = "${params.AWS_REGION}"
    }
    stages {
        stage ('Terraform Init') {
            steps {
                sh """
                export TF_VAR_region='${env.region}'
                export TF_VAR_access_key='${env.access_key}'
                export TF_VAR_secret_key='${env.secret_key}'
                terraform init
                """
            }
        }
        stage ('Terraform Plan') {
            steps {
                sh """
                export TF_VAR_region='${env.region}'
                export TF_VAR_access_key='${env.access_key}'
                export TF_VAR_secret_key='${env.secret_key}'
                terraform plan -var-file=terraform-dev.tfvars
                """
            }
        }
        stage ('Terraform Apply') {
            steps {
                sh """
                export TF_VAR_region='${env.region}'
                export TF_VAR_access_key='${env.access_key}'
                export TF_VAR_secret_key='${env.secret_key}'
                terraform apply -var-file=terraform-dev.tfvars -auto-approve
                """
            }
        }
    }
}

Step 12:- Verify The Resources

Terraform will create below resources:

Author by:

[!Note] Join Our Telegram Community || Follow me for more DevOps Content