/aws-jenkins

CloudFormation templates for Jenkins on AWS.

Apache License 2.0Apache-2.0

CloudFormation templates for Jenkins on AWS

Introduction

This repository contains several stacks for creating a Jenkins instance on AWS using CloudFormation templates (infrastructure as code).

The design is a mix of durability and cost effectiveness. The templates are not designed with HA in mind (there are already solutions for this, search GitHub for example), because this would require a load balancer (ELB) and certain storage.

The storage of this variant of Jenkins is stored outside of the instance template, allowing updates of the instance without pruning existent configurations. Previously, EFS was used instead of EBS, because it is more versatile and easier to maintain. Unfortunately its price is higher and it is not so performative.

Let’s Encrypt, to securely encrypt the traffic to Jenkins via HTTPS.

The following CloudFormation templates are available and each of these is deployed as Stack:

  • Alert topic (SNS): alert.yml
  • Hosted zone (Route53): zone.yml
  • Virtual private network (VPC): vpc.yml
  • Storage (EBS): storage.yml
  • Jenkins (EC2): jenkins.yml
artifact SNS
artifact Route53
artifact VPC
artifact EBS
artifact EC2
EBS --> SNS
EBS --> VPC
EBS --> SNS
EC2 --> SNS
EC2 --> EBS
EC2 --> VPC
EC2 --> Route53
EC2 --> SNS

./deps.png

Customization

Please consider investigation of your requirements before applying the templates blindly. Most templates are parameterized to allow easy adjustment to needs. Check the Simple Monthly Calculator to estimate the monthly costs.

Alert Topic (SNS), Optional

Deploy Stack:

aws cloudformation deploy --stack-name jenkins-alert --template-file $(pwd)/alert.yml \
    --tags Stack=jenkins-alert \
    --capabilities CAPABILITY_IAM \
    --parameter-overrides Email=mail@domain.tld

Hosted Zone (Route53), Optional but required for Let’s Encrypt

Deploy Stack

aws cloudformation deploy --stack-name sub-domain-tld \
    --template-file $(pwd)/zone.yml \
    --tags Stack=sub-domain-tld \
    --capabilities CAPABILITY_IAM \
    --parameter-overrides Domain=sub.domain.tld

Update Name servers

Retrieve the Route53 name servers for the zone:

aws cloudformation describe-stacks --stack-name domain-tld \
    --query 'Stacks[0].Outputs[1]'

Use the values to update the upstream DNS for this zone. Typically this is done by creating NS records for the subdomain prefix, where each NS record points to AWS Route 53 (the retrieved values).

VPC, Optional

Deploy Stack:

aws cloudformation deploy --stack-name jenkins-vpc --template-file $(pwd)/vpc.yml \
    --tags Stack=jenkins-vpc \
    AvailabilityZone=your-zone

Storage (EBS)

Deploy stack:

aws cloudformation deploy --stack-name jenkins-storage \
    --template-file $(pwd)/storage.yml \
    --tags Stack=jenkins-storage
    --paremeter-overrides \
    ParentAlertStack=jenkins-alert \
    ParentVpcStack=jenkins-vpc

Jenkins (EC2)

SSH Keys

Create SSH key pair (just once) for connecting to the EC2 instance. Store the generated key securely.

aws ec2 create-key-pair --key-name Jenkins --query 'KeyMaterial' \
    --output text > jenkins.pem
chmod 0400 jenkins.pem

Deploy Stack

Note: Remove the two parameters HostedZoneName and DnsName to omit the optional DNS registration with Route53.

Note: The following will block until the stack is provisioned.

aws cloudformation deploy --stack-name jenkins-ec2 \
    --template-file $(pwd)/jenkins.yml \
    --tags Stack=jenkins-ec2 \
    --capabilities CAPABILITY_IAM \
    --parameter-overrides \
    ParentAlertStack=jenkins-alert \
    ParentStorageStack=jenkins-storage \
    ParentVpcStack=jenkins-vpc \
    InstanceType=t2.small \
    KeyName=Jenkins \
    EnableEc2Ssh=true \
    HostedZoneName=sub.domain.tld \
    DnsName=build.sub.domain.tld \
    LetsEncryptEnable=false

Finish Jenkins Setup

Retrieve the public IP address of the EC2 instance:

aws cloudformation describe-stacks --stack-name jenkins-ec2 \
    --query 'Stacks[0].Outputs'

Connect to the instance via SSH.

ssh -o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null \
    -o UserKnownHostsFile=/dev/null -i jenkins.pem -l ec2-user IP-ADDRESS

To unlock Jenkins, get the initial admin password:

sudo less /var/lib/jenkins/secrets/initialAdminPassword

Open browser with http://ipaddress:8080 and paste the admin password into the corresponding input of the Jenkins setup instructions.

Continue with the online Jenkins setup by providing the respective inputs to the setup wizard and finish the setup by creating the initial admin account.

Enable Let’s Encrypt and disable SSH

The following step assumes that no more SSH interactions are intended. By enabling Let’s Encrypt support, the HTTP port 8080 will be disabled. A valid mail address should be supplied for Let’s Encrypt.

aws cloudformation update-stack --stack-name jenkins-ec2 \
    --template-body file://$(pwd)/jenkins.yml \
    --capabilities CAPABILITY_IAM \
    --parameters \
    ParameterKey=ParentAlertStack,UsePreviousValue=true \
    ParameterKey=ParentStorageStack,UsePreviousValue=true \
    ParameterKey=ParentVpcStack,UsePreviousValue=true \
    ParameterKey=InstanceType,UsePreviousValue=true \
    ParameterKey=KeyName,UsePreviousValue=true \
    ParameterKey=EnableEc2Ssh,UsePreviousValue=true \
    ParameterKey=HostedZoneName,UsePreviousValue=true \
    ParameterKey=DnsName,UsePreviousValue=true \
    ParameterKey=LetsEncryptEnable,ParameterValue=true \
    ParameterKey=LetsEncryptMailAddress,ParameterValue=valid.mail@address.tld

Developer notes

RegionMap

To update the region map:

regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)
for region in $regions; do
    ami=$(aws --region $region ec2 describe-images \
              --filters "Name=name,Values=amzn-ami-hvm-2017.09.1.20180115-x86_64-gp2" \
              --query "Images[0].ImageId" --output "text");
    printf "'$region':\n  AMI: '$ami'\\n";
done

License

Copyright 2018 Marcus Geiger

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.