Create a Bitnami Jenkins Instance on AWS EC2 with Terraform
Tags: aws, terraform
Published: 2021-07-15 14:53:14


# Create a Bitnami Jenkins Instance on AWS EC2 with Terraform

The overview of this article is to show you how to deploy a Bitnami Jenkins instance on an Amazon EC2 instance using Terraform. This article is by no means the best way to perform this task or does it include the best terraform script in the world, but it does explain and show how to get you started with creating an EC2 instance on AWS.

**Requirements**

1. AWS Account

1. AWS CLI

1. Default profile configured with the AWS CLI

1. Terraform CLI

**Why not create the instance on the AWS EC2 Console.**

Yes, you could just create your instance on the AWS EC2 Console, it's how I performed this task for years. But, it's a manual step and in the world of DevOps, we have to try to automate where we can and this should be no different.

**Steps**

1. **Configure the AWS provider.**

This tells terraform which version of the AWS provider to use and also the AWS region to deploy to. In our case, we are deploying to us-east-1

    terraform {
      required_providers {
       aws = {
          source  = "hashicorp/aws"
          version = "~> 3.0"
       }
      }
    }

    # Configure the AWS Provider
    provider "aws" {
       region = "us-east-1"
    }

**2. Configure the input parameters**

For this use case, there are 2 input parameters/variables.

1. jenkinsInstanceName: the name of the EC2 instance that is created on AWS

1. public_key: this is the content from your local public key file. This public key will be created on AWS and you can use the associated private key to log onto your EC2 instance once it is created

    variable "jenkinsInstanceName" {
       type = string
    }

    variable "public_key" {
       type = string
    }

**3. Setup script variables**

Three further variables are set up. These differ from the input variables in that the user will not be prompted to enter these values due to the presence of a default value

1. instance_type: this is the size of the EC2 instance that is created — it defaults to t3.small to keep costs low

1. ingress_rules: these are used to set up the security groups. These ports define the allowed ports for accepting incoming traffic

1. egress_rules: these are also used to set up the security groups. These ports define the allowed ports for publishing outgoing traffic on

    variable "instance_type" {
       type    = string
       default = "t3.small"
    }
    variable "ingress_rules" {
      type    = list(number)
      default = [22, 80, 443]
    }
    variable "egress_rules" {
       type    = list(number)
       default = [0]
    }

**4. Create the AWS EC2 key**

This resource will create the key called jenkins-key using the values that were submitted for the input variable of public_key

    resource "aws_key_pair" "jenkins_key" {
       key_name   = "jenkins-key"
       public_key = var.public_key
    }

**5. Lookup the correct AMI id to use**

This step used a terraform datasource. A terraform datasource allows you to look up a value to be used elsewhere in your terraform configuration. In this case, we are using the aws_ami_ids datasource.

The datasource will query for AMI Ids that are owned by the aws-marketplace and start with a value called bitnami-jenkins. It is the Bitnami version of Jenkins that we are going to deploy.

By setting sort_ascending to false, this will return a list of all AMI ids starting with the newest. This value will be used further down in our script when creating the EC2 instance.

**But why not just set the AMI id and not bother with this terraform datasource?**

That is a good question (as I realise I am talking to myself). By not hardcoding the AMI ID, we can deploy this code on any AWS region. The AWS AMI id is unique per region so if we were to deploy this code on us-east-2, the AWS AMI id would be different.

[**Note: **I set the region in the AWS provider block. This could be circumvented by setting a default AWS region when you set up the AWS CLI.]

    data "aws_ami_ids" "jenkins_ami" {
       sort_ascending = false
       owners = ["aws-marketplace"]
       filter {
          name   = "name"
          values = ["bitnami-jenkins*"]
       }
    }

**6. Setup the security groups**

This step involves setting up the AWS security groups to define the traffic that is allowed in and out of the Jenkins instance.

For this resource, we use dynamic blocks and the ingress_rules and egress_rules variables specified aboce. The dynamic blocks provide a way to avoid repeated code blocks. Think of it as a for loop for setting up the security groups

    resource "aws_security_group" "jenkins_security_group" {
       name        = "Jenkins Security Group"
       description = "Allow traffic to access Jenkins instrance"
       dynamic "ingress" {
          iterator = port
          for_each = var.ingress_rules
          content {
            from_port   = port.value
            to_port     = port.value
            protocol    = "TCP"
            cidr_blocks = ["0.0.0.0/0"]
         }
       }
       dynamic "egress" {
          iterator = port
          for_each = var.egress_rules
          content {
            from_port   = port.value
            to_port     = port.value
            protocol    = "-1"
            cidr_blocks = ["0.0.0.0/0"]
         }
      }
    }

**7. Create Bitnami Jenkins EC2 instance**

This is the main part of this tutorial and where the action happens.

This is where the Amazon EC2 instance is created with the Bitnami Jenkins image. This a breakdown of the options passed into the aws_instance terraform resource

**ami: **This is the AMI id that is used. data.aws_ami_ids.jenkins_ami refers to the datasource where we looked up the Bitnami Jenkins instance ids. We take the first result ([0]), which will correspond to the newest AMI id for Bitnami Jenkins

**instance_type: **The type of instance to create. This value is obtained from the variable called instance_type which was defined at the top of this article

**security_groups: **The security group to assign to this EC2 instance. The security group was created further up in this article and used the Dynamic blocks

**key_name: **The key to use to allow ssh’ing onto this instance. Again, the public key was accepted as user input and the EC2 key was created based on this

**tags: **The tags to apply to this instance. The Name tag is important and this corresponds to the input parameter that was supplied by the user for jenkinsInstanceName

**depends_on: **This block states that the EC2 instance should not be created until the security group jenkins_security_group was created successfully.

    resource "aws_instance" "jenkins_instance" {
       ami             = data.aws_ami_ids.jenkins_ami.ids[0]
       instance_type   = var.instance_type
       security_groups =[aws_security_group.jenkins_security_group.name]
       key_name        = aws_key_pair.jenkins_key.key_name
       tags = {
         Name = var.jenkinsInstanceName
       }
      depends_on = [aws_security_group.jenkins_security_group]
    }

**8. Output the instance id**

The final step in the process involves outputting the instanceid that was created. This instance id will be outputted when the terraform script completes execution.

    output "jenkins_public_ip" {  
       description = "The ip address for the newly created jenkins instance"  
      value       = aws_instance.jenkins_instance.public_ip
    }

**9. Create the Resources**

Run these commands to create the resources on AWS

terraform init — installs the AWS provider and sets up any modules that were created

terraform apply — will apply the configuration. This command prompts the user to enter the jenkinsInstanceName and the public sshKey. It also prompts the user to approve the creation of the resources

terraform apply -var=”jenkinsInstanceName=[NAME OF INSTANCE]” -var=”public_key=[PUBLIC SSH KEY]” -auto-approve — this command will not prompt the user and will create the necessary resources.

e.g. terraform apply -var=”jenkinsInstanceName=JenkinsServer” -var=”public_key=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 [email@example.com](mailto:email@example.com)” -auto-approve

**10. Obtain the password for Bitnami Jenkins**

The 1 manual step involves retrieving the password for the Bitnami Jenkins Instance. We need to SSH onto the instance and retrieve the password from a file. The following commands can be used, just substitute your values for the path to the private ssh key and the EC2 instance public IP address

    ssh -i [PATH TO PRIVATE SSH KEY] bitnami@[EC2 INSTANCE PUBLIC IP]

    sudo cat /home/bitnami/bitnami_credentials

**11. Jenkins is installed**

When you navigate to the IP address that was retrieved in step 8 after a few moments, you should see the following screen, and Jenkins is set up. Log in with the username user and the password retrieved in step 10 to begin using Jenkins.

![](https://cdn-images-1.medium.com/max/2000/1*xmHBDLnTRrq_QDAsnaXe9A.png)

**12. Destroy infrastructure**

If you are just testing this tutorial out or no longer require the Jenkins instance, run the following command to delete the stack.

**Note:** If you leave the stack running, you may be charged by AWS. It is up to the user to ensure they have the infrastructure running that they are willing to pay for.

**Conclusion**

This was a quick tutorial showing you how to set up an instance of Bitnami Jenkins on an Amazon EC2 instance. Again, it is not a definitive guide and should be used to get started. Tweaks and improvements can be made and should be made, but hopefully, this article helps you get started on your journey with Jenkins.

The full terraform configuration file for this tutorial can be found [here](https://raw.githubusercontent.com/damogallagher/aws-jenkins/main/main.tf).