Skip to content

Instantly share code, notes, and snippets.

@imod
Created January 22, 2020 08:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imod/fb702d545dbe77292e8f4796c7804059 to your computer and use it in GitHub Desktop.
Save imod/fb702d545dbe77292e8f4796c7804059 to your computer and use it in GitHub Desktop.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"HostedZoneName": {
"Description": "The route53 HostedZoneName. For example, 'mydomain.org.' Don't forget the period at the end. (must exist!)",
"Default": "mydomain.net.",
"Type": "String"
},
"Subdomain": {
"Description": "The subdomain of the dns entry. For example, build -> jenkins.mydomain.org, 'build' is the subdomain.",
"Type": "String"
},
"NetworkStackName": {
"Description": "Name of an active CloudFormation stack that contains the networking resources, such as the subnet and security group, that will be used in this stack.",
"Type": "String",
"MinLength": 1,
"MaxLength": 255,
"AllowedPattern": "^[a-zA-Z][-a-zA-Z0-9]*$"
},
"InstanceTypeParameter": {
"Type": "String",
"Default": "t2.medium",
"AllowedValues": [
"t2.micro",
"m1.small",
"m1.large",
"t2.large",
"t2.medium"
],
"Description": "Type of EC2 instance you want to run your master in."
},
"KeyNameParameter": {
"Type": "AWS::EC2::KeyPair::KeyName",
"Description": "The KeyName that will be used to access this EC2 instance."
},
"PrivateKey": {
"NoEcho": "true",
"Type": "String",
"Description": "The private key for the keypair specified above (required to access agents from master)."
},
"SSHLocation": {
"Description": "The IP address range that can be used to SSH to the EC2 instance for your Jenkins instance.",
"Type": "String",
"MinLength": "9",
"MaxLength": "18",
"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"
},
"HttpIngressCidr": {
"Description": "The IP address range that is allowed to access jenkins master port 80 (e.g. from proxy)",
"Type": "String",
"MinLength": "9",
"MaxLength": "18",
"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"
}
},
"Resources": {
"BuildDNSRecord": {
"Type": "AWS::Route53::RecordSet",
"Description": "DNS name for jenkins server, e.g. 'jenkins.mydomain.org'",
"Properties": {
"HostedZoneName": {
"Ref": "HostedZoneName"
},
"Name": {
"Fn::Join": [
"",
[
{
"Ref": "Subdomain"
},
".",
{
"Ref": "HostedZoneName"
}
]
]
},
"Type": "A",
"TTL": "300",
"ResourceRecords": [
{
"Fn::GetAtt": ["EC2JksInstance", "PublicIp"]
}
]
}
},
"EC2JksInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"SubnetId": {
"Fn::ImportValue": {
"Fn::Sub": "${NetworkStackName}-SubnetID"
}
},
"ImageId": "ami-cfe4b2b0",
"InstanceType": { "Ref": "InstanceTypeParameter" },
"IamInstanceProfile": { "Ref": "MasterInstanceProfile" },
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": "30"
}
}
],
"KeyName": { "Ref": "KeyNameParameter" },
"SecurityGroupIds": [{ "Ref": "JksMasterSecurityGroup" }],
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Sub": "Jenkins Master for ${AWS::StackName}" }
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -v\n",
"# Install Docker to be able to start the Jenkins container\n",
"exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1",
"\n",
"## do the magic to install jenkins docker container && letsencrypt\n",
"#...\n",
"echo ...done\n"
]
]
}
}
}
},
"JksMasterSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Fn::ImportValue": {
"Fn::Sub": "${NetworkStackName}-VPCID"
}
},
"GroupDescription": "Enable HTTPS access via port 443 and JNLP port and SSH (TODO: restrict agent port)",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"Description": "SSH access",
"FromPort": "22",
"ToPort": "22",
"CidrIp": { "Ref": "SSHLocation" }
},
{
"IpProtocol": "tcp",
"Description": "only used by letsencrypt to verify domain",
"FromPort": "80",
"ToPort": "80",
"CidrIp": { "Ref": "HttpIngressCidr" }
},
{
"IpProtocol": "tcp",
"Description": "HTTPS access, mapped to nginx container (letsencrypt)",
"FromPort": "443",
"ToPort": "443",
"CidrIp": { "Ref": "HttpIngressCidr" }
},
{
"IpProtocol": "tcp",
"Description": "JNLP access for jenkins nodes",
"FromPort": "50000",
"ToPort": "50000",
"CidrIp": "0.0.0.0/0"
}
]
}
},
"JksAgentSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": {
"Fn::ImportValue": {
"Fn::Sub": "${NetworkStackName}-VPCID"
}
},
"GroupDescription": "Enable HTTP access via port 8080 and JNLP port (TODO: restrict agent port)",
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "0.0.0.0/0"
}
]
}
},
"MasterInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Roles": [{ "Ref": "JksMasterRole" }]
}
},
"JksMasterRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": ["ec2.amazonaws.com"]
},
"Action": ["sts:AssumeRole"]
}
]
},
"Policies": [
{
"PolicyName": "EC2CloudPluginPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ControlEC2JenkinsAgents",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:TerminateInstances",
"ec2:RequestSpotInstances",
"ec2:DeleteTags",
"ec2:CreateTags",
"ec2:DescribeRegions",
"ec2:RunInstances",
"ec2:DescribeSpotInstanceRequests",
"ec2:StopInstances",
"ec2:DescribeSecurityGroups",
"ec2:GetConsoleOutput",
"ec2:DescribeImages",
"ec2:CancelSpotInstanceRequests",
"ec2:StartInstances",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeSubnets",
"ec2:DescribeKeyPairs",
"iam:ListInstanceProfilesForRole",
"iam:PassRole",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability"
],
"Resource": "*"
}
]
}
},
{
"PolicyName": "JKSSecretsManagerPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "secretsmanager:ListSecrets",
"Resource": "*"
}
]
}
}
]
}
}
}
}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"VPCIPRange": {
"Description": "The private IP address range for allocating IPs within the VPC.",
"Type": "String",
"MinLength": "9",
"MaxLength": "18",
"Default": "10.0.0.0/16",
"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."
}
},
"Resources": {
"JksCIVPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"EnableDnsSupport": "true",
"EnableDnsHostnames": "true",
"CidrBlock": { "Ref": "VPCIPRange" },
"InstanceTenancy": "default",
"Tags": [
{ "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
{ "Key": "Name", "Value": "VPC for CI Infrastructure" }
]
}
},
"JksCIInternetGateway": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {
"Tags": [
{ "Key": "Name", "Value": { "Fn::Sub": "CI ${AWS::StackName}" } }
]
}
},
"JksCIGatewayAttachment": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": { "Ref": "JksCIVPC" },
"InternetGatewayId": { "Ref": "JksCIInternetGateway" }
}
},
"JksCISubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"AvailabilityZone": "us-east-1a",
"VpcId": { "Ref": "JksCIVPC" },
"CidrBlock": { "Ref": "VPCIPRange" },
"MapPublicIpOnLaunch": "true",
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Sub": "Subnet for CI ${AWS::StackName}" }
}
]
}
},
"JksCIRouteTable": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": { "Ref": "JksCIVPC" },
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Sub": "RouteTable for CI ${AWS::StackName}" }
}
]
}
},
"JksCIInternetRoute": {
"Type": "AWS::EC2::Route",
"DependsOn": "JksCIGatewayAttachment",
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": { "Ref": "JksCIInternetGateway" },
"RouteTableId": { "Ref": "JksCIRouteTable" }
}
},
"JksCISubnetRouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"RouteTableId": { "Ref": "JksCIRouteTable" },
"SubnetId": { "Ref": "JksCISubnet" }
}
},
"JksCISecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"VpcId": { "Ref": "JksCIVPC" },
"GroupDescription": "allow all traffic",
"SecurityGroupIngress": [
{
"IpProtocol": "-1",
"FromPort": "-1",
"ToPort": "-1",
"CidrIp": "0.0.0.0/0"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "-1",
"CidrIp": "0.0.0.0/0"
}
],
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Sub": "Global SecurityGroup for ${AWS::StackName}"
}
}
]
}
}
},
"Outputs": {
"VPCId": {
"Description": "VPC ID",
"Value": {
"Ref": "JksCIVPC"
},
"Export": {
"Name": {
"Fn::Sub": "${AWS::StackName}-VPCID"
}
}
},
"VPCPublicSubnet": {
"Description": "The subnet ID to use for public web servers",
"Value": {
"Ref": "JksCISubnet"
},
"Export": {
"Name": {
"Fn::Sub": "${AWS::StackName}-SubnetID"
}
}
},
"VPCWebServerSecurityGroup": {
"Description": "The security group ID to use for public web servers",
"Value": {
"Fn::GetAtt": ["JksCISecurityGroup", "GroupId"]
},
"Export": {
"Name": {
"Fn::Sub": "${AWS::StackName}-SecurityGroupID"
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment