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
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.
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
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
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).
Deploy Stack:
aws cloudformation deploy --stack-name jenkins-vpc --template-file $(pwd)/vpc.yml \
--tags Stack=jenkins-vpc \
AvailabilityZone=your-zone
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
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
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
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.
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
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
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.