Create the Jenkins Server in AWS using Terraform
Tags: terraform, aws, iaac, jenkins
Published: 2022-09-04 11:40:24

In this article, we will learn how to setup the Jenkins Server using the power of Terraform. 
There are number of ways to setup the Jenkins Server but that took some efforts and time to setup that. We can use Terraform to setup the infrastructure and configure the Jenkins Server without knowing any code complexity

## Pre-requisite:

- Basic knowledge of Terraform
- Terraform should be installed on your machine
- An Active AWS account to be used in this lab

## Terraform Configuration

### Provider

In the provider block, specify the name of provider (in our case its AWS) and region where you want to configure the infrastructure
Optional - you can provide the Access_KEY & SECRET_KEY inside the block code but it's not recommended to provide the keys in the code

```
provider "aws" {
  region = "us-east-1"
}
```
### Networking

In this lab we will be setting up our own VPC, Subnets, NAT Gateway, Route Table etc

```
# Internet gateway to reach the internet
resource "aws_vpc" "web_vpc" {
  cidr_block = var.network_cidr
}

resource "aws_internet_gateway" "web_igw" {
  vpc_id = aws_vpc.web_vpc.id
}
# Route table with a route to the internet
resource "aws_route_table" "public_rt" {
  vpc_id = aws_vpc.web_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.web_igw.id
  }
  #   tags {
  #     Name = "Public Subnet Route Table"
  #   }
}
# Subnets with routes to the internet
resource "aws_subnet" "public_subnet" {
  # Use the count meta-parameter to create multiple copies
  count             = 2
  vpc_id            = aws_vpc.web_vpc.id
  cidr_block        = cidrsubnet(var.network_cidr, 2, count.index + 2)
  availability_zone = element(var.availability_zones, count.index)
  #   tags =  {
  #     Name = "Public Subnet ${count.index + 1}"
  #   }
}

# Associate public route table with the public subnets
resource "aws_route_table_association" "public_subnet_rta" {
  count          = 2
  subnet_id      = aws_subnet.public_subnet.*.id[count.index]
  route_table_id = aws_route_table.public_rt.id
}

```
We will be using Amazon Linux-2 AMI, instead of hardcoding the code with the AMI id, we will take help of DataSource block to retrieve the value of AMI from AWS

```
data "aws_ami" "AmazonLinux2" {
  most_recent = true

  filter {
    name   = "name"
    values = ["amzn2-ami-kernel-5.10-hvm-2.0.20220805.0-x86_64-gp2"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["137112412989"] # Canonical
}
```
Now it's time to create the EC2 and configure the Jenkins Sever, however one last piece of the puzzle here. I want to expose my Jenkins Server on port 8080 and expose the InitialAdminPassword to configure Jenkins Server on port 80 so we can retrieve that information without login into the EC2 instance. For this we will have to create the Security Group and allow both of these ports

```
resource "aws_security_group" "jenkins-sg" {
  name        = "jenkins-sg-12345678"
  description = "Allow incoming HTTP traffic from the internet"
  vpc_id      = aws_vpc.web_vpc.id
  ingress {
    from_port = 8080
    to_port   = 8080
    protocol  = "tcp"
    #cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port = 80
    to_port   = 80
    protocol  = "tcp"
    #cidr_blocks = ["0.0.0.0/0"]
  }
  # Allow all outbound traffic
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
```
So now we are ready to create the EC2 instance and configure the Jenkins Server. I will be using the bash script and pass that in user data using the file("path") function

```
#!/bin/bash
            yum install update -y
            sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
            sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
            sudo yum install fontconfig java-11-openjdk
            sudo  yum install jenkins
            sudo systemctl start jenkins
            sudo amazon-linux-extras install nginx1 -y
            sudo systemctl start nginx
            cp /var/lib/jenkins/secrets/initialAdminPassword /var/www/html/index.html
            sudo amazon-linux-extras install java-openjdk11
            chown -R root:root /var/lib/jenkins 
            chown -R root:root /var/cache/jenkins 
            chown -R root:root /var/log/jenkins
            sudo systemctl restart docker.service
            sed -i 's/JENKINS_USER="jenkins"/JENKINS_USER="root"/' /etc/sysconfig/jenkins
            sudo systemctl restart jenkins
```
Save this Bash script file as install_jenkins.sh
### Configure Jenkins Server

```
resource "aws_instance" "jenkins" {
  ami                         = data.aws_ami.AmazonLinux2.id
  instance_type               = var.ec2_instance_type
  vpc_security_group_ids      = [aws_security_group.jenkins-sg.id]
  subnet_id                   = aws_subnet.public_subnet[0].id
  associate_public_ip_address = true
  user_data                   = file("install_jenkins.sh")
  root_block_device {
    delete_on_termination = true
    volume_size           = "20"
  }
  tags = {
    Name        = "Jenkins_Server"
    Environment = "Dev"
  }
}

```
### Variables Block
To make our code more dynamic, we use couple of variables above so you will have to define the variable.tf file and declare those variable

```
variable "ec2_instance_type" {
  type        = string
  default     = "t2.micro"
  description = "Please enter the instance type, if you want to provision different than T2 Micro"
}

variable "service_ports" {
  type        = list(number)
  description = "list of ingress ports"
  default     = [8080, 80]
}

variable "network_cidr" {
  default     = "192.168.0.0/24"
  description = "Please enter the CIDR"
}

variable "availability_zones" {
  type        = list(any)
  default     = ["us-east-1a", "us-east-1d"]
  description = "Please enter the AZs"
}

```
Ola, we have successfully configured the Jenkins Server without going to the AWS Console.

In order to retrieve the IP address of newly created instance, we will use the output { } block. 

### Output

To make the output more menaningful I'm using a temp file and will display output in more customize way. Save the following code as outputtemp.tpl

```
%{for ports in port ~}
    backend = ${ip_addr}:${ports}
%{endfor ~}
```
Now save the following to output.tf. The output will get the value form Port list and using for loop, will print Jenkins Sever IP and nginx IP on separate line

```
output "IPAddress" {
  value = templatefile("outputtemp.tpl", { port = ["8080", "80"], ip_addr = aws_instance.jenkins.public_ip })
}
```
We have successfully setup the Jenkins Sever. To destroy the above created infrastructure

```
terraform destroy --auto-approve
```
Thank you for reading my article, if you like this article then don't forget to share it with others. In case you have any question or suggestion please left the comments below, I would love to answer your queries

You can follow me on
[LinkedIn](https://www.linkedin.com/in/waqas-ahmed-tech-expert/)
[GitHub](https://github.com/waqasahmed1992)
[Terraform] (https://registry.terraform.io/namespaces/waqasahmed1992)