A production-ready Infrastructure as Code (IaC) solution for deploying Jenkins on AWS using Terraform. This project automates the provisioning of a complete AWS infrastructure with networking, security, load balancing, and SSL/TLS certificate management.
Created by: Harmeet Singh
- Overview
- Architecture
- Prerequisites
- Project Structure
- Configuration
- Getting Started
- AWS Resources
- Outputs
- Security
- Troubleshooting
- Contributing
This Terraform project automates the deployment of Jenkins CI/CD server on AWS with the following features:
- Automated Infrastructure Provisioning: VPC, subnets, security groups, and routing
- Jenkins Installation: Automated Jenkins and Java installation on EC2
- Load Balancing: Application Load Balancer (ALB) for traffic distribution
- SSL/TLS Security: AWS Certificate Manager (ACM) integration with HTTPS
- Domain Management: Route 53 DNS configuration with custom domain support
- High Availability: Multi-AZ support with public and private subnets
- Infrastructure as Code: Modular, reusable Terraform configuration
┌─────────────────────────────────────────────────────────────────┐
│ AWS Account │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ VPC (11.0.0.0/16) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Public Subnets (Multi-AZ) │ │ │
│ │ │ • eu-west-1a: 11.0.1.0/24 │ │ │
│ │ │ • eu-west-1b: 11.0.2.0/24 │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
│ │ │ │ Application Load Balancer (ALB) │ │ │ │
│ │ │ │ • Port 80 (HTTP) → Port 8080 │ │ │ │
│ │ │ │ • Port 443 (HTTPS) → Port 8080 with SSL │ │ │ │
│ │ │ └────────────────────────────────────────────┘ │ │ │
│ │ │ ↓ │ │ │
│ │ │ ┌────────────────────────────────────────────┐ │ │ │
│ │ │ │ EC2 Instance (t2.medium) │ │ │ │
│ │ │ │ • Jenkins on Port 8080 │ │ │ │
│ │ │ │ • Ubuntu 20.04 LTS │ │ │ │
│ │ │ │ • Java 11 + Jenkins + Terraform │ │ │ │
│ │ │ └────────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Private Subnets (Multi-AZ) │ │ │
│ │ │ • eu-west-1a: 11.0.3.0/24 (Reserved for future) │ │ │
│ │ │ • eu-west-1b: 11.0.4.0/24 (Reserved for future) │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Internet Gateway (IGW) │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Route 53 (DNS Management) │ │
│ │ • Domain: jenkins.jhooq.org │ │
│ │ • Alias Record → ALB DNS Name │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ AWS Certificate Manager (ACM) │ │
│ │ • SSL/TLS Certificate for jenkins.jhooq.org │ │
│ │ • DNS Validation via Route 53 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
-
Terraform >= 1.0
terraform version
-
AWS CLI >= 2.0
aws --version
-
AWS Account with appropriate permissions
-
Valid AWS Credentials configured locally
Your AWS user/role must have permissions for:
- VPC and Subnet management
- EC2 instance creation
- Security Group configuration
- Load Balancer (ALB) management
- Route 53 DNS management
- ACM Certificate management
- S3 (for remote state)
-
Install Terraform:
# macOS brew install terraform # Linux/Ubuntu sudo apt-get update && sudo apt-get install terraform # Windows choco install terraform
-
Configure AWS Credentials:
aws configure # Enter your AWS Access Key ID # Enter your AWS Secret Access Key # Enter default region (e.g., eu-west-1) # Enter default output format (json)
-
Generate SSH Key Pair (if not exists):
ssh-keygen -t rsa -b 4096 -f ~/.ssh/aws_ec2_terraform -N ""
terraform-jenkins/
├── README.md # Project documentation
├── main.tf # Root module configuration
├── variables.tf # Variable definitions
├── outputs.tf # Output definitions
├── terraform.tfvars # Variable values
├── provider.tf # AWS provider configuration
├── remote_backend_s3.tf # Remote state backend (S3)
│
├── networking/ # VPC & Networking Module
│ └── main.tf # VPC, Subnets, IGW, Route Tables
│
├── security-groups/ # Security Groups Module
│ └── main.tf # Security group rules
│
├── jenkins/ # Jenkins EC2 Module
│ └── main.tf # EC2 instance configuration
│
├── load-balancer/ # ALB Module
│ └── main.tf # Application Load Balancer
│
├── load-balancer-target-group/ # Target Group Module
│ └── main.tf # ALB Target Group
│
├── hosted-zone/ # Route 53 Module
│ └── main.tf # DNS configuration
│
├── certificate-manager/ # ACM Module
│ └── main.tf # SSL/TLS certificates
│
└── jenkins-runner-script/ # Installation Scripts
└── jenkins-installer.sh # Jenkins & Terraform setup script
bucket_name = "dev-proj-1-jenkins-remote-state-bucket-123456"
# Networking
vpc_cidr = "11.0.0.0/16"
vpc_name = "dev-proj-jenkins-eu-west-vpc-1"
cidr_public_subnet = ["11.0.1.0/24", "11.0.2.0/24"]
cidr_private_subnet = ["11.0.3.0/24", "11.0.4.0/24"]
eu_availability_zone = ["eu-west-1a", "eu-west-1b"]
# EC2 Configuration
public_key = "ssh-rsa AAAAB3NzaC1yc2EA..." # Your public SSH key
ec2_ami_id = "ami-0694d931cee176e7d" # Ubuntu 20.04 AMIEdit provider.tf:
provider "aws" {
region = "us-east-1" # Change from eu-west-1 to desired region
}Update availability zones in terraform.tfvars:
eu_availability_zone = ["us-east-1a", "us-east-1b"]Edit terraform.tfvars and the references in main.tf:
# In main.tf, update:
domain_name = "your-domain.com"Edit main.tf:
instance_type = "t2.large" # Change from t2.mediumEdit terraform.tfvars:
vpc_cidr = "10.0.0.0/16" # Change VPC CIDR
cidr_public_subnet = ["10.0.1.0/24", "10.0.2.0/24"]
cidr_private_subnet = ["10.0.3.0/24", "10.0.4.0/24"]cd terraform-jenkinsterraform initThis will:
- Download required providers (AWS)
- Initialize the local Terraform working directory
- Configure the remote backend (if defined)
terraform validateterraform plan -out=tfplanReview the planned changes and output. This shows:
- Resources to be created
- Changes to existing resources
- Destruction of removed resources
terraform apply tfplanOr directly without saving plan:
terraform applyExpected Time: 5-10 minutes for complete infrastructure provisioning.
After successful deployment:
-
Get ALB DNS Name:
terraform output aws_lb_dns_name
-
Get EC2 Public IP:
terraform output dev_proj_1_ec2_instance_public_ip
-
Access Jenkins:
- Via Domain:
https://jenkins.jhooq.org(once DNS propagates) - Via ALB DNS:
http://<alb-dns-name> - Via EC2 Direct:
http://<ec2-public-ip>:8080
- Via Domain:
-
SSH into Jenkins Server:
ssh -i ~/.ssh/aws_ec2_terraform ubuntu@<ec2-public-ip>
-
Retrieve Jenkins Admin Password:
ssh -i ~/.ssh/aws_ec2_terraform ubuntu@<ec2-public-ip> \ sudo cat /var/lib/jenkins/secrets/initialAdminPassword
| Resource | Details |
|---|---|
| VPC | 11.0.0.0/16 CIDR block |
| Public Subnets | 2 subnets (11.0.1.0/24, 11.0.2.0/24) in different AZs |
| Private Subnets | 2 subnets (11.0.3.0/24, 11.0.4.0/24) in different AZs |
| Internet Gateway | Provides internet access for public subnets |
| Route Tables | Separate routing for public and private subnets |
| Resource | Details |
|---|---|
| EC2 Instance | t2.medium, Ubuntu 20.04 LTS |
| Key Pair | For SSH access |
| Software | Java 11, Jenkins, Terraform 1.6.5 |
| Resource | Details |
|---|---|
| ALB | Application Load Balancer in public subnets |
| Listeners | Port 80 (HTTP) and 443 (HTTPS) |
| Target Group | Routes traffic to Jenkins on port 8080 |
| Health Checks | Path: /login, Interval: 5s, Timeout: 2s |
| Resource | Details |
|---|---|
| SG 1 | Allows SSH (22), HTTP (80), HTTPS (443) |
| SG 2 | Allows Jenkins port 8080 |
| Resource | Details |
|---|---|
| Route 53 | DNS management for jenkins.jhooq.org |
| ACM Certificate | SSL/TLS certificate with DNS validation |
After successful deployment, retrieve outputs:
# Get ALB DNS Name
terraform output aws_lb_dns_name
# Get Jenkins EC2 Instance ID
terraform output jenkins_ec2_instance_ip
# Get Jenkins EC2 Public IP
terraform output dev_proj_1_ec2_instance_public_ip
# Get all outputs
terraform outputaws_lb_dns_name: Application Load Balancer DNS nameaws_lb_zone_id: ALB Hosted Zone IDjenkins_ec2_instance_ip: Jenkins EC2 instance IDdev_proj_1_ec2_instance_public_ip: Jenkins EC2 public IP addressssh_connection_string_for_ec2: SSH command to connect to Jenkins server
-
Network Isolation:
- Public subnets for ALB and bastion access
- Private subnets for future internal resources
- Strict security group rules
-
Access Control:
- SSH access restricted (currently open to 0.0.0.0/0 - should be restricted to your IP)
- Jenkins accessible via ALB with SSL/TLS
- EC2 metadata protected with IMDSv2
-
Encryption:
- SSL/TLS certificates via AWS ACM
- HTTPS enforced on ALB listeners
-
Restrict SSH Access:
# Edit security-groups/main.tf ingress { description = "Allow remote SSH from specific IP" cidr_blocks = ["YOUR.IP.ADDRESS/32"] # Change to your IP from_port = 22 to_port = 22 protocol = "tcp" }
-
Enable MFA for AWS console access
-
Use AWS Secrets Manager for Jenkins credentials
-
Enable CloudTrail for audit logging
-
Configure CloudWatch for monitoring and alerts
-
Setup backup for Jenkins home directory
Solution:
rm -rf .terraform terraform.lock.hcl
terraform initSolution:
aws configure
# Or set environment variables
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="eu-west-1"Steps to debug:
# 1. Check EC2 instance status
aws ec2 describe-instances --region eu-west-1
# 2. Check ALB target health
aws elbv2 describe-target-health --target-group-arn <arn> --region eu-west-1
# 3. SSH into instance
ssh -i ~/.ssh/aws_ec2_terraform ubuntu@<ec2-ip>
# 4. Check Jenkins service
sudo systemctl status jenkins
# 5. Check logs
sudo tail -f /var/log/jenkins/jenkins.logSolution: DNS propagation can take 15-30 minutes. Meanwhile, use the ALB DNS name or EC2 public IP directly.
Steps:
# 1. Check ACM certificate status
aws acm describe-certificate --certificate-arn <arn> --region eu-west-1
# 2. Verify Route 53 records
aws route53 list-resource-record-sets --hosted-zone-id <zone-id>
# 3. Check validation records manually
dig jenkins.jhooq.orgCauses and Solutions:
- Jenkins not running: SSH and check
sudo systemctl status jenkins - Health check path incorrect: Verify
/loginreturns 200 - Security groups not allowing traffic: Check SG rules
- Instance not in target group: Check ALB target health
Solution:
# Refresh state
terraform refresh
# View state
terraform state list
# Remove resource from state (if needed)
terraform state rm 'resource.name'terraform destroyReview the plan carefully before confirming. This will remove:
- EC2 instances
- Load balancers
- VPC and all networking resources
- Security groups
- SSL certificates
- Route 53 records
# Remove specific resource
terraform destroy -target=module.jenkins
# Destroy with auto-approval (use carefully)
terraform destroy -auto-approveContributions are welcome! To contribute:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make changes and test thoroughly
- Commit with descriptive messages:
git commit -m 'Add your feature' - Push to branch:
git push origin feature/your-feature - Create a Pull Request
Before submitting:
terraform fmt -recursive # Format code
terraform validate # Validate syntax
terraform plan # Review changesFor issues, questions, or suggestions:
- Check existing GitHub issues
- Create a new GitHub issue with detailed information
- Include terraform output and logs
This project is available under the MIT License. See LICENSE file for details.
Author: Harmeet Singh
Last Updated: January 2026
Status: Production Ready