Skip to content

Instantly share code, notes, and snippets.

@randika
Created March 30, 2018 03:01
Show Gist options
  • Save randika/c08e911cf7a8ed52e6642a9606ae3ea2 to your computer and use it in GitHub Desktop.
Save randika/c08e911cf7a8ed52e6642a9606ae3ea2 to your computer and use it in GitHub Desktop.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Launches a Jenkins server",
"Parameters": {
"InstanceType": {
"Description": "EC2 instance type",
"Type": "String",
"Default": "t2.small",
"AllowedValues": [
"t1.micro",
"t2.small",
"m1.small",
"m1.medium",
"m1.large",
"m1.xlarge",
"m2.xlarge",
"m2.2xlarge",
"m2.4xlarge",
"m3.xlarge",
"m3.2xlarge",
"c1.medium",
"c1.xlarge",
"cc1.4xlarge",
"cc2.8xlarge",
"cg1.4xlarge"
],
"ConstraintDescription": "must be a valid EC2 instance type."
},
"AppId": {
"Description": "Application Identifier",
"Type": "String",
"Default": ""
},
"AppComponentId": {
"Description": "Application Component name. (ex: web, db, build)",
"Type": "String"
},
"AppEnv": {
"Description": "Application Environment",
"Type": "String",
"Default": "dev"
},
"SshKey": {
"Description": "Name of an existing EC2 keypair to enable SSH access to the instances",
"Default": "",
"Type": "AWS::EC2::KeyPair::KeyName"
},
"IPWhitelist": {
"Description": "IP Address to Whitelist (your IP address followed by /32)",
"MinLength": "9",
"MaxLength": "18",
"Type": "String",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x.",
"Default": "0.0.0.0/0"
},
"EmailAddress": {
"Description": "What email address will receive the default Jenkins password",
"Type": "String",
"Default": ""
},
"S3Bucket": {
"Description": "Existing S3 bucket to use for Jenkins backups and restores",
"Type": "String"
},
"S3Prefix": {
"Description": "[Optional] Key prefix to use for Jenkins backups",
"Type": "String",
"Default": ""
},
"Subnets": {
"Description": "List of VPC subnet IDs for the cluster",
"Type": "List<AWS::EC2::Subnet::Id>"
},
"VpcId": {
"Description": "VPC associated with the provided subnets",
"Type": "AWS::EC2::VPC::Id"
},
"AdminSecurityGroup": {
"Description": "Existing security group that should be granted administrative access to Jenkins (e.g., 'sg-123456')",
"Default": "Primary",
"Type": "AWS::EC2::SecurityGroup::Id"
}
},
"Mappings": {
"RegionMap": {
"us-east-1": {
"AMI": "ami-6869aa05"
},
"us-west-1": {
"AMI": "ami-7172b611"
},
"us-west-2": {
"AMI": "ami-31490d51"
},
"eu-west-1": {
"AMI": "ami-f9dd458a"
}
}
},
"Resources": {
"CloudFormationLogs": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"RetentionInDays": 7
}
},
"IamJenkinsUser": {
"Type": "AWS::IAM::User",
"Properties": {
"Policies": [
{
"PolicyName": "S3Access",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "S3Bucket"
},
"/*"
]
]
}
}
]
}
},
{
"PolicyName": "IAMAccess",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"NotAction": "iam:*",
"Resource": "*"
}
]
}
},
{
"PolicyName": "EC2Access",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
}
]
}
}
]
}
},
"BuildRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
}
]
}
},
"RolePolicies": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "ec2:*",
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "elasticloadbalancing:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "cloudwatch:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "autoscaling:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ecs:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ecr:*",
"Resource": "*"
}
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:ListInstanceProfiles",
"ec2:*"
],
"Resource": "*"
}
]
},
"Roles": [
{
"Ref": "BuildRole"
}
]
}
},
"BuildInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [
{
"Ref": "BuildRole"
}
]
}
},
"HostKeys": {
"Type": "AWS::IAM::AccessKey",
"Properties": {
"UserName": {
"Ref": "IamJenkinsUser"
}
}
},
"ServerGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"VPCZoneIdentifier" : { "Ref" : "Subnets" },
"LaunchConfigurationName": {
"Ref": "LaunchConfig"
},
"MinSize": "1",
"MaxSize": "1",
"DesiredCapacity": "1",
"LoadBalancerNames": [
{
"Ref": "ElasticLoadBalancer"
}
],
"Tags" : [
{
"Key" : "app_id",
"Value" : { "Ref": "AppId"},
"PropagateAtLaunch" : "true"
},
{
"Key" : "env",
"Value" : { "Ref": "AppEnv"},
"PropagateAtLaunch" : "true"
}
]
}
},
"LaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Metadata": {
"Comment": "Install a simple application",
"AWS::CloudFormation::Init": {
"configSets": {
"install_all": ["install_cfn", "install_base", "install_nginx", "install_logs"]
},
"install_cfn": {
"files": {
"/etc/cfn/cfn-hup.conf": {
"content": {
"Fn::Join": ["", [
"[main]\n",
"stack=", {
"Ref": "AWS::StackId"
}, "\n",
"region=", {
"Ref": "AWS::Region"
}, "\n"
]]
},
"mode": "000400",
"owner": "root",
"group": "root"
},
"/etc/cfn/hooks.d/cfn-auto-reloader.conf": {
"content": {
"Fn::Join": ["", [
"[cfn-auto-reloader-hook]\n",
"triggers=post.update\n",
"path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
"action=/opt/aws/bin/cfn-init -v ",
" --stack ", {
"Ref": "AWS::StackName"
},
" --resource WebServerInstance ",
" --configsets install_all ",
" --region ", {
"Ref": "AWS::Region"
}, "\n",
"runas=root\n"
]]
}
}
},
"services": {
"sysvinit": {
"cfn-hup": {
"enabled": "true",
"ensureRunning": "true",
"files": ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"]
}
}
}
},
"install_base": {
"packages": {
"yum": {
"git": [],
"docker": [],
"httpd-tools": [],
"jq": [],
"java-1.8.0-openjdk.x86_64": []
}
},
"services": {
"sysvinit": {
"docker": {
"enabled": "true",
"ensureRunning": "true"
}
}
}
},
"install_nginx": {
"packages": {
"yum": {
"nginx": []
}
},
"files": {
"/etc/nginx/nginx.conf": {
"content": {
"Fn::Join": ["", [
"user nginx;\n",
"worker_processes 1;\n\n",
"error_log /var/log/nginx/error.log;\n",
"pid /var/run/nginx.pid;\n\n",
"events {\n",
" worker_connections 1024;\n",
"}\n\n",
"http {\n",
" include /etc/nginx/mime.types;\n",
" default_type application/octet-stream;\n",
" log_format main '$remote_addr - $remote_user [$time_local] \"$request\" '\n",
" '$status $body_bytes_sent \"$http_referer\" '\n",
" '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n",
" access_log /var/log/nginx/access.log main;\n",
" sendfile on;\n",
" keepalive_timeout 65;\n",
" include /etc/nginx/conf.d/*.conf;\n",
" index index.html index.htm;\n",
" server {\n",
" listen 80;\n",
" server_name _;\n",
" location / {\n",
" proxy_pass http://127.0.0.1:8080;\n",
" proxy_set_header Host $host;\n",
" proxy_set_header X-Real-IP $remote_addr;\n",
" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",
" proxy_connect_timeout 150;\n",
" proxy_send_timeout 100;\n",
" proxy_read_timeout 100;\n",
" proxy_buffers 4 32k;\n",
" client_max_body_size 8m;\n",
" client_body_buffer_size 128k;\n",
" }\n",
" }\n",
"}\n"
]]
},
"mode": "000444",
"owner": "root",
"group": "root"
}
},
"services": {
"sysvinit": {
"nginx": {
"enabled": "true",
"ensureRunning": "true",
"files": ["/etc/nginx/nginx.conf"]
}
}
}
},
"install_logs": {
"packages": {
"yum": {
"awslogs": []
}
},
"files": {
"/etc/awslogs/awslogs.conf": {
"content": {
"Fn::Join": ["", [
"[general]\n",
"state_file= /var/awslogs/state/agent-state\n",
"[/var/log/cloud-init.log]\n",
"file = /var/log/cloud-init.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/cloud-init.log\n",
"datetime_format = \n",
"[/var/log/cloud-init-output.log]\n",
"file = /var/log/cloud-init-output.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/cloud-init-output.log\n",
"datetime_format = \n",
"[/var/log/cfn-init.log]\n",
"file = /var/log/cfn-init.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/cfn-init.log\n",
"datetime_format = \n",
"[/var/log/cfn-hup.log]\n",
"file = /var/log/cfn-hup.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/cfn-hup.log\n",
"datetime_format = \n",
"[/var/log/cfn-wire.log]\n",
"file = /var/log/cfn-wire.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/cfn-wire.log\n",
"datetime_format = \n",
"[/var/log/nginx/access.log]\n",
"file = /var/log/nginx/access.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/nginx-access.log\n",
"datetime_format = \n",
"[/var/log/nginx/error.log]\n",
"file = /var/log/nginx/error.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/nginx-error.log\n",
"datetime_format = \n",
"[/var/log/jenkins/jenkins.log]\n",
"file = /var/log/jenkins/jenkins.log\n",
"log_group_name = ", {
"Ref": "CloudFormationLogs"
}, "\n",
"log_stream_name = {instance_id}/jenkins.log\n",
"datetime_format = \n"
]]
},
"mode": "000444",
"owner": "root",
"group": "root"
}
},
"commands": {
"01_create_state_directory": {
"command": "mkdir -p /var/awslogs/state"
},
"02_change_log_region": {
"command": {
"Fn::Join": ["", ["sed -i 's/region = us-east-1/region = ",
{ "Ref": "AWS::Region" },
"/g' /etc/awslogs/awscli.conf"]]
}
}
},
"services": {
"sysvinit": {
"awslogs": {
"enabled": "true",
"ensureRunning": "true",
"files": ["/etc/awslogs/awslogs.conf"]
}
}
}
}
}
},
"Properties": {
"KeyName": {
"Ref": "SshKey"
},
"ImageId": {
"Fn::FindInMap": [
"RegionMap",
{
"Ref": "AWS::Region"
},
"AMI"
]
},
"BlockDeviceMappings": [{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": "50",
"VolumeType": "gp2"
}
}],
"SecurityGroups": [{
"Ref": "ServerSecurityGroup"
}],
"InstanceType": {
"Ref": "InstanceType"
},
"IamInstanceProfile": {
"Ref": "BuildInstanceProfile"
},
"UserData": {
"Fn::Base64": {
"Fn::Join": ["", [
"#!/bin/bash -xe\n",
"yum update -y aws-cfn-bootstrap\n",
"/opt/aws/bin/cfn-init -v ",
" --stack ", {
"Ref": "AWS::StackName"
},
" --resource LaunchConfig ",
" --configsets install_all ",
" --region ", {
"Ref": "AWS::Region"
}, "\n",
"yum remove -y java-1.7.0\n",
"# Install Jenkins\n",
"wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo\n",
"rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key\n",
"yum install -y jenkins\n",
"# Add Jenkins user to Docker group\n",
"usermod -a -G docker jenkins\n",
"service jenkins start\n",
"chkconfig jenkins on\n",
"# Update the AWS CLI to the latest version\n",
"yum update -y aws-cli\n",
"# Wait 30 seconds to allow Jenkins to startup\n",
"echo \"Waiting 30 seconds for Jenkins to start.....\"\n",
"sleep 30\n",
"# Install the required plugins\n",
"cd /var/lib/jenkins/plugins\n",
"curl -O -L https://updates.jenkins-ci.org/latest/token-macro.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/docker-build-publish.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/multiple-scms.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/github-api.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/scm-api.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/git-client.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/github.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/git.hpi\n",
"curl -O -L https://updates.jenkins-ci.org/latest/dockerhub.hpi\n",
"chown jenkins:jenkins *.hpi\n",
"# Restarting Jenkins\n",
"service jenkins restart\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --stack ", {
"Ref": "AWS::StackName"
},
" --resource WebServerGroup ",
" --region ", {
"Ref": "AWS::Region"
}, "\n"
]]
}
},
}
},
"LbSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Jenkins LBs",
"VpcId": {
"Ref": "VpcId"
},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "0.0.0.0/0"
}
],
"Tags" : [
{
"Key" : "app_id",
"Value" : { "Ref": "AppId"}
},
{
"Key" : "env",
"Value" : { "Ref": "AppEnv"}
}
]
}
},
"ServerSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Jenkins servers",
"VpcId": {
"Ref": "VpcId"
},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "8080",
"ToPort": "8080",
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": {"Ref" : "IPWhitelist"}
}
],
"Tags" : [
{
"Key" : "app_id",
"Value" : { "Ref": "AppId"}
},
{
"Key" : "env",
"Value" : { "Ref": "AppEnv"}
}
]
}
},
"ElasticLoadBalancer": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"SecurityGroups": [
{
"Ref": "LbSecurityGroup"
},
{
"Ref": "AdminSecurityGroup"
}
],
"Subnets": {
"Ref": "Subnets"
},
"Listeners": [
{
"LoadBalancerPort": "80",
"InstancePort": "8080",
"Protocol": "HTTP"
}
],
"HealthCheck": {
"Target": "TCP:8080",
"HealthyThreshold": "3",
"UnhealthyThreshold": "5",
"Interval": "30",
"Timeout": "5"
},
"LoadBalancerName": {
"Fn::Join": [
"-",
[
{"Ref": "AppEnv"},{"Ref": "AppId"},{"Ref": "AppComponentId"}
]
]
},
"Tags" : [
{
"Key" : "app_id",
"Value" : { "Ref": "AppId"}
},
{
"Key" : "env",
"Value" : { "Ref": "AppEnv"}
}
]
}
},
"WaitHandle": {
"Type": "AWS::CloudFormation::WaitConditionHandle"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment