Skip to content

Instantly share code, notes, and snippets.

@randika
Created March 30, 2018 03:01
Show Gist options
  • Select an option

  • Save randika/c08e911cf7a8ed52e6642a9606ae3ea2 to your computer and use it in GitHub Desktop.

Select an option

Save randika/c08e911cf7a8ed52e6642a9606ae3ea2 to your computer and use it in GitHub Desktop.

Revisions

  1. randika created this gist Mar 30, 2018.
    766 changes: 766 additions & 0 deletions cloudformation_jenkins.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,766 @@
    {
    "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"
    }
    }
    }