Building infrastructure with Terraform: EC2, Jenkins, S3 and more
I undertook a project to build out an AWS infrastructure using Terraform. The objectives of this project are as follows:
- Deploy an EC2 instance
- Bootstrap the EC2 instance with a script to install and start Jenkins
- Create and assign a security group that allows me to SSH from my PC and all web traffic
- Create an S3 bucket for Jenkins artifacts that is not open to the public
Prerequisites for this project:
- AWS Free-Tier account
- A Linux environment with Terraform installed to build out your infrastructure (I’ll be using AWS Cloud9 with Amazon Linux configured with my user access keys)
(link with info to configure AWS access keys in your system)
(link with info to install and setup Terraform in your system)
LET’S GET STARTED!
First I created a new directory to house my Terraform configurations and entered that directory:
mkdir <directory_name> [enter]
cd <directory_name> [enter]
Once inside my new directory, I needed to create a .tf file for some Terraform code and enter the file with vim to edit it:
touch provider.tf
vim provider.tf
The providers.tf file is where I will declare AWS as the provider that Terraform will work with to build my resources. The image below shows what code is necessary to declare here:
To insert text, I typed “I” to access the file. Once this is entered, I typed [esc] then “:wq!” to quit and save the file.
This brings me back to my newly created directory for the project. Next, I typed the command below to initialize Terraform:
terraform init [enter]
The above command should give a successful result that looks something like the image below:
Next, I created a new Terraform file that will house all of the code to build out my infrastructure:
touch week20build.tf
vim week20build.tf
From here, I am able to edit the file and enter all the code necessary to configure my AWS infrastructure. Terraform will run the file and build everything for me.
I started with the code necessary to build the EC2 instance:
resource "aws_instance" "week20" {
ami = "(Found in aws portal)"
instance_type = "t3.micro"
subnet_id = "(found in aws portal)"
tags = {
Name = "Week20"
}
}
This code lets Terraform know I want it to build an EC2 instance with the following parameters:
- -ami-0dfcb1ef8550277af — run Amazon Linux as the OS (can be found among AWS documentation)
- instanc_type — build a t3.micro machine
- subnet_id = add the EC2 to an existing security group
- name — name the instance “Week20"
Once this is done I created another file in my directory house a shell script to function as user data to bootstrap Jenkins in my EC2 when it is created:
vim script.sh
Then entered the script below in the file:
#! /bin/bash
sudo yum 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 upgrade
sudo amazon-linux-extras install java-openjdk11 -y
sudo yum install jenkins -y
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins
This BASH script gives instructions for my EC2 to run commands as soon as it is created. Now I just need to reference the script file from the .tf file I created earlier. To do so I added the following as a variable at the bottom of the code block in my current .tf file (Must be located in the same directory in order for this to work):
resource "aws_instance" "week20" {
ami = "ami-0dfcb1ef8550277af"
instance_type = "t3.micro"
subnet_id = "subnet-092f222bb8cbd8e22"
tags = {
Name = "Week20"
}
#USERDATA in AWS EC2 using Terraform - install and start Jenkins
user_data = file("script.sh")
}
Next, I need to build out a security group for my EC2 instance that allows SSH traffic from my computer and web traffic from anywhere. To do so, I added the following code to my .tf file:
#Build EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0dfcb1ef8550277af"
instance_type = "t3.micro"
subnet_id = "subnet-092f222bb8cbd8e22"
tags = {
Name = "Week20"
}
}
#USERDATA in AWS EC2 using Terraform - install and start Jenkins
user_data = file("script.sh")
}
#Build Security Group for EC2 Instance
resource "aws_security_group" "TF_SG" {
name = "Week20_SG"
description = "security group for Week 20 Terraform project"
vpc_id = "vpc-05cc2521e5c90548f"
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["x.x.x.x/32"]
ipv6_cidr_blocks = ["::/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "Week20_SG"
}
}
Important details to note:
The Ingress rule for SSH (Port 22), I left it unfilled as the setting for the IP address is my personal PC. If you want to replicate this project, please use your own IP address for the ingress SSH rule in the code.
You can find your own public IP address here.
Next, I ran:
terraform apply --auto-approve
Afterwards, I checked my AWS console to verify the creation of the security group and EC2 instance was reflected and the new security group was attached to the EC2 instance:
Next I added my EC2 instance to the security group in the console:
Finally, I needed to add a new section to my code to build an S3 bucket. I simply attached the last bit of code to the end of my .tf file:
resource "aws_s3_bucket" "jenkinsbucketweek20" {
bucket = "cpthegeniusjenkinsbucket2023"
tags = {
Name = "cpthegeniusjenkinsbucket2023"
Environment = "Dev"
}
}
resource "aws_s3_bucket_acl" "example" {
bucket = aws_s3_bucket.jenkinsbucketweek20.id
acl = "private"
}
IMPORTANT: Your S3 bucket has to follow specific naming rules you can find here
If you are following along, I recommend just using lowercase letters to keep it simple.
Next, I ran <terraform apply> again and waited for a good result:
I also checked the AWS console to confirm the bucket was created.
After all of this has been verified the project was complete!
Now the final step was to tear every part of the entire infrastructure down. That may sound like a lot to do or remember, but that’s the beauty of using Terraform — with one simple command it did all of it for me in seconds!
I headed back to the CLI in my directory and typed:
terraform destroy --auto-approve
The result will show every resource I deployed is now deleted