├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── docs └── architecture.png ├── examples ├── jenkins-master-ami │ ├── README.md │ ├── install_jenkins │ └── jenkins.json └── jenkins-slave-ami │ ├── README.md │ ├── jenkins-slave-service.sh │ ├── jenkins-slave.conf │ └── jenkins.json ├── main.tf ├── modules ├── jenkins-alb-security-group │ ├── main.tf │ ├── output.tf │ └── variables.tf ├── jenkins-alb │ ├── main.tf │ ├── output.tf │ └── variables.tf ├── jenkins-master │ ├── main.tf │ ├── outputs.tf │ ├── setup.tpl │ └── variables.tf ├── jenkins-security-group-rules │ ├── master.tf │ ├── outputs.tf │ └── variables.tf └── jenkins-slave │ ├── main.tf │ ├── outputs.tf │ ├── setup.tpl │ └── variables.tf ├── outputs.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .terraform/ 3 | terraform.tfstate* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | terraform-aws-jenkins 2 | Copyright 2017 InfogroupNW, Inc. 3 | 4 | This product includes software developed at InfogroupNW (http://www.ignw.io/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jenkins AWS Module 2 | 3 | This repo contains a Module for how to deploy a [Jenkins](https://jenkins.io/) cluster on 4 | [AWS](https://aws.amazon.com/) using [Terraform](https://www.terraform.io/). Jenkins is a distributed automation server, generally associated with [Continuous Integration (CI)](https://en.wikipedia.org/wiki/Continuous_integration) and [Continuous Delivery (CD)](https://en.wikipedia.org/wiki/Continuous_delivery). A Jenkins cluster typically involves one 5 | or more [master](https://wiki.jenkins.io/display/JENKINS/Distributed+builds) instance(s) coupled with one or more [slave](https://wiki.jenkins.io/display/JENKINS/Distributed+builds) instance(s): 6 | 7 | ![Jenkins architecture](docs/architecture.png) 8 | 9 | 10 | ## How to use this Module 11 | 12 | Each Module has the following folder structure: 13 | 14 | * [root](https://github.com/ignw/terraform-aws-jenkins/tree/master): This folder shows an example of Terraform code 15 | that uses the [jenkins-master](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-master) and 16 | [jenkins-slave](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-slave) module(s) to 17 | deploy a [Jenkins](https://www.jenkins.io/) cluster in [AWS](https://aws.amazon.com/). 18 | * [modules](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules): This folder contains the reusable code for this Module, broken down into one or more modules. 19 | * [examples](https://github.com/ignw/terraform-aws-jenkins/tree/master/examples): This folder contains examples of how to use the modules. 20 | * [test](https://github.com/ignw/terraform-aws-jenkins/tree/master/test): Automated tests for the modules and examples. 21 | 22 | To deploy Jenkins servers using this Module: 23 | 24 | 1. Create a Jankins Master AMI using a Packer template installs jenkins and all of the required dependencies. 25 | Here is an [example Packer template](https://github.com/ignw/terraform-aws-jenkins/tree/master/examples/jenkins-ami#quick-start). 26 | 27 | If you are just experimenting with this Module, you may find it more convenient to use one of our official public AMIs: 28 | - [Latest Master AMIs (Amazon Linux)](https://github.com/ignw/terraform-aws-jenkins/tree/master/_docs/amazon-linux-ami-list.md). 29 | - [Latest Slave AMIs (Amazon Linux)](https://github.com/ignw/terraform-aws-jenkins/tree/master/_docs/amazon-linux-ami-list.md). 30 | 31 | **WARNING! Do NOT use these AMIs in your production setup. In production, you should build your own AMIs in your own 32 | AWS account.** 33 | 34 | 2. Deploy those AMIs to your AWS region using the Terraform [jenkins-master module](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-master) 35 | and execute the [jenkins-slave module](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-slave) Here is [an example Terraform 36 | configuration](https://github.com/ignw/terraform-aws-jenkins/tree/master/MAIN.md#quick-start) to provision a Jenkins cluster. 37 | 38 | To deploy Jenkins clients using this Module: 39 | 40 | ``` 41 | terraform init 42 | 43 | terraform plan --var "ssh_key_name=ignw_dev" \ 44 | --var "ssh_key_path=~/.ssh/ignw_dev.pem" \ 45 | --var "linux_slave_count=1" \ 46 | --var aws_ssl_certificate_arn="arn:aws:acm:us-east-1:xxxxxxxxxxx" \ 47 | --var dns_zone="example.com" \ 48 | --var app_dns_name="jenkins.example.com" \ 49 | 50 | terraform apply --var "ssh_key_name=ignw_dev" \ 51 | --var "ssh_key_path=~/.ssh/ignw_dev.pem" \ 52 | --var "linux_slave_count=1" \ 53 | --var aws_ssl_certificate_arn="arn:aws:acm:us-east-1:xxxxxxxxxxx" \ 54 | --var dns_zone="example.com" \ 55 | --var app_dns_name="jenkins.example.com" \ 56 | 57 | ``` 58 | Argument | Description 59 | --- | --- 60 | name | The name to be used on all instances as a prefix 61 | ssh_key_name | AWS SSH Key Pair name 62 | ssh_key_path | Path to AWS SSH Key Pair private key pair used for provisioning 63 | linux_slave_count | The number of Jenkins Linux Build Slaves to provision 64 | win_slave_count | The number of Jenkins Windows Build Slaves to provision 65 | instance_type_master | The instance type to be used on the master instance. Default: t2.micro 66 | instance_type_slave | The instance type to be used on the slave instance(s). Default: t2.micro 67 | setup_data | The script used to setup the Jenkins master instance and install plugins. Default: ./modules/jenkins-master/setup.tpl 68 | http_port | The port to use for HTTP traffic to Jenkins 69 | jnlp_port | The Port to use for Jenkins master to slave communication bewtween instances 70 | plugins | The list of plugins to pre-install on the master instance. Default: ["git", "xunit"] 71 | tags | A map of tags to add to all resources 72 | master_ami_id | ID of the AMI to use for master instance. Default: lookup latest IGNW master AMI 73 | linux_slave_ami_id | ID of the AMI to use for linux slave instance(s). Default: lookup latest IGNW linux slave AMI 74 | win_slave_ami_id | ID of the AMI to use for windows slave instance(s). Default: lookup latest IGNW windows slave AMI 75 | aws_ssl_certificate_arn | Amazon Resource Name for the certificate to be used on the load balancer for HTTPS 76 | dns_zone | DNS zone in AWS Route53 to use for the Application Load Balancer (ALB) 77 | app_dns_name | DNS name within the zone to dynamically point to the ALB 78 | 79 | ## What's a Module? 80 | 81 | A Module is a canonical, reusable, best-practices definition for how to run a single piece of infrastructure, such 82 | as a database or server cluster. Each Module is created using [Terraform](https://www.terraform.io/), and 83 | includes automated tests, examples, and documentation. It is maintained both by the open source community and 84 | companies that provide commercial support. 85 | 86 | Instead of figuring out the details of how to run a piece of infrastructure from scratch, you can reuse 87 | existing code that has been proven in production. And instead of maintaining all that infrastructure code yourself, 88 | you can leverage the work of the Module community to pick up infrastructure improvements through 89 | a version number bump. 90 | 91 | 92 | 93 | ## Who maintains this Module? 94 | 95 | This Module is maintained by [IGNW](http://www.ignw.io/). If you're looking for help or commercial 96 | support, send an email to [support@infogroupnw.com](mailto:support@infogroupnw.com?Subject=Jenkins%20Module). 97 | IGNW can help with: 98 | 99 | * Setup, customization, and support for this Module. 100 | * Modules for other types of infrastructure, such as VPCs, Docker clusters, databases, and continuous integration. 101 | * Modules that meet compliance requirements, such as FedRamp, HIPAA. 102 | * Consulting & Training on AWS, Azure, GCP, Terraform, and DevOps. 103 | 104 | 105 | 106 | ## Code included in this Module: 107 | 108 | * [jenkins-master](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-master): The module includes Terraform code to deploy a Jenkins master on AWS and setup [plugins](https://plugins.jenkins.io). 109 | 110 | * [jenkins-slave](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-slave): The module includes Terraform code to deploy a Jenkins [slave](https://wiki.jenkins.io/display/JENKINS/Distributed+builds) on AWS and connect it to it's [master](https://wiki.jenkins.io/display/JENKINS/Distributed+builds). 111 | 112 | * [jenkins-security-group-rules](https://github.com/ignw/terraform-aws-jenkins/tree/master/modules/jenkins-security-group-rules): Defines the security group rules used by a 113 | Jenkins cluster to control the traffic that is allowed to go in and out of the cluster. 114 | 115 | 116 | ## How is this Module versioned? 117 | 118 | This Module follows the principles of [Semantic Versioning](http://semver.org/). You can find each new release, 119 | along with the changelog, in the [Releases Page](../../releases). 120 | 121 | During initial development, the major version will be 0 (e.g., `0.x.y`), which indicates the code does not yet have a 122 | stable API. Once we hit `1.0.0`, we will make every effort to maintain a backwards compatible API and use the MAJOR, 123 | MINOR, and PATCH versions on each release to indicate any incompatibilities. 124 | 125 | 126 | 127 | ## License 128 | 129 | This code is released under the Apache 2.0 License. Please see [LICENSE](https://github.com/ignw/terraform-aws-jenkins/tree/master/LICENSE) and [NOTICE](https://github.com/ignw/terraform-aws-jenkins/tree/master/NOTICE) for more 130 | details. 131 | 132 | Copyright © 2017 InfogroupNW, Inc. -------------------------------------------------------------------------------- /docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IGNW/terraform-aws-jenkins/75b200b9f3674cc108a1894a45f78de97cfe0ed2/docs/architecture.png -------------------------------------------------------------------------------- /examples/jenkins-master-ami/README.md: -------------------------------------------------------------------------------- 1 | # Jenkins Master AMI 2 | 3 | This folder shows an example Jenkins master server build using a packer template to generate an AMI. 4 | 5 | OS: _Amazon Linux_ 6 | 7 | These AMIs will have [Jenkins](https://www.jenkins.io/) installed and configured to automatically run the Jenkins daemon service. You would need to tailor this environment 8 | to include your build tools or run all builds on slave instance(s). 9 | 10 | Installed 11 | * Java 1.8 Open JDK 12 | * Git Tools 13 | * AWS CLI Tools 14 | * Apache Maven 15 | * Jenkins 16 | 17 | ## Quick start 18 | 19 | To build the Jenkins Master AMI: 20 | 21 | 1. `git clone` this repo to your computer. 22 | 1. Install [Packer](https://www.packer.io/). 23 | 1. Configure your AWS credentials using one of the [options supported by the AWS 24 | SDK](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html). Usually, the easiest option is to 25 | set the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. 26 | 1. Update the `variables` section of the `jenkins.json` Packer template to configure the AWS region and Jenkins version you wish to use. 27 | 1. Run `packer build jenkins.json`. 28 | 29 | When the build finishes, it will output the IDs of the new AMIs. To see how to deploy one of these AMIs, check out the 30 | [jenkins example](https://github.com/ignw/terraform-aws-jenkins/tree/master/MAIN.md). -------------------------------------------------------------------------------- /examples/jenkins-master-ami/install_jenkins: -------------------------------------------------------------------------------- 1 | sudo yum -y update 2 | 3 | sudo yum install -y java-1.8.0-openjdk.x86_64 4 | 5 | sudo /usr/sbin/alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java 6 | 7 | sudo /usr/sbin/alternatives --set javac /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/javac 8 | 9 | sudo yum remove -y java-1.7* 10 | 11 | # install core reqs plus nginx for ssl termination 12 | sudo yum install -y git nginx aws-cli 13 | sudo update-alternatives --config java 14 | 15 | # install maven 16 | sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo 17 | sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo 18 | sudo yum install -y apache-maven 19 | #mvn –v 20 | 21 | # install xmlstarlet used for XML config manipulation 22 | sudo yum install -y xmlstarlet 23 | 24 | # install jenkins 25 | sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo 26 | sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key 27 | 28 | # todo: parameterize version 29 | sudo yum install -y jenkins -------------------------------------------------------------------------------- /examples/jenkins-master-ami/jenkins.json: -------------------------------------------------------------------------------- 1 | { 2 | "min_packer_version": "0.12.0", 3 | "variables": { 4 | "aws_region": "us-east-1", 5 | "jenkins_version": "2.8" 6 | }, 7 | "builders": [{ 8 | "name": "amazon-linux-ami", 9 | "ami_name": "jenkins-amazon-linux-{{isotime | clean_ami_name}}", 10 | "ami_description": "An Amazon Linux AMI that has Jenkins installed.", 11 | "instance_type": "t2.micro", 12 | "region": "{{user `aws_region`}}", 13 | "type": "amazon-ebs", 14 | "source_ami_filter": { 15 | "filters": { 16 | "virtualization-type": "hvm", 17 | "architecture": "x86_64", 18 | "name": "*amzn-ami-hvm-*", 19 | "block-device-mapping.volume-type": "gp2", 20 | "root-device-type": "ebs" 21 | }, 22 | "owners": ["amazon"], 23 | "most_recent": true 24 | }, 25 | "ssh_username": "ec2-user" 26 | }], 27 | "provisioners": [{ 28 | "type": "shell", 29 | "script": "./install_jenkins", 30 | "pause_before": "30s" 31 | }] 32 | } -------------------------------------------------------------------------------- /examples/jenkins-slave-ami/README.md: -------------------------------------------------------------------------------- 1 | # Jenkins Slave AMI 2 | 3 | This folder shows an example Jenkins master server build using a packer template to generate an AMI. 4 | 5 | OS: _Amazon Linux_ 6 | 7 | These AMIs will have [Jenkins](https://www.jenkins.io/) installed and configured to automatically run the Jenkins daemon service. You would need to tailor this environment 8 | to include your build tools or run all builds on slave instance(s). 9 | 10 | Installed 11 | * Java 1.8 Open JDK 12 | * Jenkins Slave JAR 13 | 14 | ## Quick start 15 | 16 | To build the Jenkins Slave AMI: 17 | 18 | 1. `git clone` this repo to your computer. 19 | 1. Install [Packer](https://www.packer.io/). 20 | 1. Configure your AWS credentials using one of the [options supported by the AWS 21 | SDK](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html). Usually, the easiest option is to 22 | set the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. 23 | 1. Update the `variables` section of the `jenkins.json` Packer template to configure the AWS region and Jenkins version you wish to use. 24 | 1. Run `packer build jenkins.json`. 25 | 26 | When the build finishes, it will output the IDs of the new AMIs. To see how to deploy one of these AMIs, check out the 27 | [jenkins example](https://github.com/ignw/terraform-aws-jenkins/tree/master/MAIN.md). -------------------------------------------------------------------------------- /examples/jenkins-slave-ami/jenkins-slave-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Start Jenkins slave daemon at boot time 9 | # Description: Jenkins Build Slave for running Linux builds 10 | ### END INIT INFO 11 | 12 | name=`basename $0` 13 | pid_file="/var/run/$name.pid" 14 | stdout_log="/var/log/$name.log" 15 | stderr_log="/var/log/$name.err" 16 | 17 | user="jenkins" 18 | dir="/home/$user/$name" 19 | 20 | # load Jenkins config 21 | . /home/jenkins/jenkins-slave/config 22 | 23 | cmd="java -jar slave.jar -jnlpUrl $JENKINS_URL/computer/$JENKINS_SLAVE/slave-agent.jnlp -secret $JENKINS_SECRET" 24 | 25 | get_pid() { 26 | cat "$pid_file" 27 | } 28 | 29 | is_running() { 30 | [ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1 31 | } 32 | 33 | case "$1" in 34 | start) 35 | if is_running; then 36 | echo "Already started" 37 | else 38 | echo "Starting $name" 39 | cd $dir 40 | if [ -z "$user" ]; then 41 | sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & 42 | else 43 | sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & 44 | fi 45 | echo $! > "$pid_file" 46 | if ! is_running; then 47 | echo "Unable to start, see $stdout_log and $stderr_log" 48 | exit 1 49 | fi 50 | fi 51 | ;; 52 | stop) 53 | if is_running; then 54 | echo -n "Stopping $name.." 55 | kill `get_pid` 56 | for i in 1 2 3 4 5 6 7 8 9 10 57 | # for i in `seq 10` 58 | do 59 | if ! is_running; then 60 | break 61 | fi 62 | 63 | echo -n "." 64 | sleep 1 65 | done 66 | echo 67 | 68 | if is_running; then 69 | echo "Not stopped; may still be shutting down or shutdown may have failed" 70 | exit 1 71 | else 72 | echo "Stopped" 73 | if [ -f "$pid_file" ]; then 74 | rm "$pid_file" 75 | fi 76 | fi 77 | else 78 | echo "Not running" 79 | fi 80 | ;; 81 | restart) 82 | $0 stop 83 | if is_running; then 84 | echo "Unable to stop, will not attempt to start" 85 | exit 1 86 | fi 87 | $0 start 88 | ;; 89 | status) 90 | if is_running; then 91 | echo "Running" 92 | else 93 | echo "Stopped" 94 | exit 1 95 | fi 96 | ;; 97 | *) 98 | echo "Usage: $0 {start|stop|restart|status}" 99 | exit 1 100 | ;; 101 | esac 102 | 103 | exit 0 -------------------------------------------------------------------------------- /examples/jenkins-slave-ami/jenkins-slave.conf: -------------------------------------------------------------------------------- 1 | JENKINS_URL= 2 | JENKINS_SLAVE= 3 | JENKINS_SECRET= -------------------------------------------------------------------------------- /examples/jenkins-slave-ami/jenkins.json: -------------------------------------------------------------------------------- 1 | { 2 | "min_packer_version": "0.12.0", 3 | "variables": { 4 | "aws_region": "us-east-1", 5 | "jenkins_version": "2.8" 6 | }, 7 | "builders": [{ 8 | "name": "amazon-linux-ami", 9 | "ami_name": "jenkins-slave-amazon-linux-{{isotime | clean_ami_name}}", 10 | "ami_description": "An Amazon Linux AMI that has Jenkins Slave dependencies installed.", 11 | "instance_type": "t2.micro", 12 | "region": "{{user `aws_region`}}", 13 | "type": "amazon-ebs", 14 | "source_ami_filter": { 15 | "filters": { 16 | "virtualization-type": "hvm", 17 | "architecture": "x86_64", 18 | "name": "*amzn-ami-hvm-*", 19 | "block-device-mapping.volume-type": "gp2", 20 | "root-device-type": "ebs" 21 | }, 22 | "owners": ["amazon"], 23 | "most_recent": true 24 | }, 25 | "ssh_username": "ec2-user" 26 | }], 27 | "provisioners": [{ 28 | "type": "shell", 29 | "inline" : [ 30 | "sudo yum -y update", 31 | "sudo yum install -y java-1.8.0-openjdk.x86_64", 32 | "sudo yum remove -y java-1.7*", 33 | "sudo /usr/sbin/alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java" 34 | ] 35 | }, { 36 | "type": "shell", 37 | "inline" : [ 38 | "sudo useradd --home-dir /home/jenkins --create-home --shell /bin/bash jenkins", 39 | "echo 'jenkins' | sudo passwd 'jenkins' --stdin --force", 40 | "sudo mkdir /home/jenkins/jenkins-slave", 41 | "wget https://repo.jenkins-ci.org/public/org/jenkins-ci/main/remoting/{{user `jenkins_version`}}/remoting-{{user `jenkins_version`}}.jar", 42 | "sudo mv remoting-{{user `jenkins_version`}}.jar /home/jenkins/jenkins-slave/slave.jar" 43 | ] 44 | }, { 45 | "type": "file", 46 | "source": "jenkins-slave.conf", 47 | "destination": "/tmp/jenkins-slave.conf" 48 | }, { 49 | "type": "file", 50 | "source": "jenkins-slave-service.sh", 51 | "destination": "/tmp/jenkins-slave-service.sh" 52 | }, { 53 | "type": "shell", 54 | "inline" : [ 55 | "sudo mv /tmp/jenkins-slave.conf /home/jenkins/jenkins-slave/config", 56 | "sudo chown -R jenkins:jenkins /home/jenkins", 57 | "sudo mv /tmp/jenkins-slave-service.sh /etc/init.d/jenkins-slave", 58 | "sudo chmod +x /etc/init.d/jenkins-slave", 59 | "sudo chkconfig --add jenkins-slave" 60 | ] 61 | }] 62 | } -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.aws_region}" 3 | } 4 | 5 | data "aws_ami" "jenkins" { 6 | most_recent = true 7 | 8 | # If we change the AWS Account in which test are run, update this value. 9 | owners = ["312506926764"] 10 | 11 | filter { 12 | name = "virtualization-type" 13 | values = ["hvm"] 14 | } 15 | 16 | filter { 17 | name = "is-public" 18 | values = ["false"] // flip to public when ready for release 19 | } 20 | 21 | filter { 22 | name = "name" 23 | values = ["jenkins-amazon-linux-*"] 24 | } 25 | } 26 | 27 | # Jenkins Master Instance 28 | module "jenkins-master" { 29 | source = "./modules/jenkins-master" 30 | 31 | vpc_id = "${data.aws_vpc.default.id}" 32 | 33 | name = "${var.name == "" ? "jenkins-master" : join("-", list(var.name, "jenkins-master"))}" 34 | alb_prefix = "${var.name == "" ? "jenkins" : join("-", list(var.name, "jenkins"))}" 35 | instance_type = "${var.instance_type_master}" 36 | 37 | ami_id = "${var.master_ami_id == "" ? data.aws_ami.jenkins.image_id : var.master_ami_id}" 38 | user_data = "" 39 | setup_data = "${data.template_file.setup_data_master.rendered}" 40 | 41 | http_port = "${var.http_port}" 42 | allowed_ssh_cidr_blocks = ["0.0.0.0/0"] 43 | allowed_inbound_cidr_blocks = ["0.0.0.0/0"] 44 | ssh_key_name = "${var.ssh_key_name}" 45 | ssh_key_path = "${var.ssh_key_path}" 46 | 47 | # Config used by the Application Load Balancer 48 | subnet_ids = "${data.aws_subnet_ids.default.ids}" 49 | aws_ssl_certificate_arn = "${var.aws_ssl_certificate_arn}" 50 | dns_zone = "${var.dns_zone}" 51 | app_dns_name = "${var.app_dns_name}" 52 | } 53 | 54 | data "template_file" "setup_data_master" { 55 | template = "${file("./modules/jenkins-master/setup.tpl")}" 56 | 57 | vars = { 58 | jnlp_port = "${var.jnlp_port}" 59 | plugins = "${join(" ", var.plugins)}" 60 | } 61 | } 62 | 63 | # Jenkins Linux Slave Instance(s) 64 | module "jenkins-linux-slave" { 65 | source = "./modules/jenkins-slave" 66 | 67 | count = "${var.linux_slave_count}" 68 | 69 | name = "${var.name == "" ? "jenkins-linux-slave" : join("-", list(var.name, "jenkins-linux-slave"))}" 70 | instance_type = "${var.instance_type_slave}" 71 | 72 | ami_id = "${var.linux_slave_ami_id}" 73 | jenkins_security_group_id = "${module.jenkins-master.jenkins_security_group_id}" 74 | 75 | jenkins_master_ip = "${module.jenkins-master.private_ip}" 76 | jenkins_master_port = "${var.http_port}" 77 | 78 | ssh_key_name = "${var.ssh_key_name}" 79 | ssh_key_path = "${var.ssh_key_path}" 80 | } 81 | 82 | data "aws_vpc" "default" { 83 | default = true 84 | } 85 | 86 | data "aws_subnet_ids" "default" { 87 | vpc_id = "${data.aws_vpc.default.id}" 88 | } -------------------------------------------------------------------------------- /modules/jenkins-alb-security-group/main.tf: -------------------------------------------------------------------------------- 1 | #------------------- 2 | # LB security group 3 | #------------------- 4 | resource "aws_security_group" "lb_security_group" { 5 | name_prefix = "${var.name_prefix}" 6 | description = "Security group for the load balancer" 7 | vpc_id = "${var.vpc_id}" 8 | 9 | tags { 10 | Name = "${var.name_prefix}" 11 | } 12 | } 13 | 14 | # Allow HTTPS in from specific external subnets if public access is not enabled 15 | resource "aws_security_group_rule" "allow_lb_https_inbound" { 16 | type = "ingress" 17 | from_port = 443 18 | to_port = 443 19 | protocol = "tcp" 20 | cidr_blocks = ["${var.jenkins_private_ip}/32", "${var.allowed_inbound_cidr_blocks}"] 21 | 22 | security_group_id = "${aws_security_group.lb_security_group.id}" 23 | } 24 | 25 | resource "aws_security_group_rule" "allow_lb_outbound" { 26 | type = "egress" 27 | from_port = 0 28 | to_port = 0 29 | protocol = "-1" 30 | cidr_blocks = ["0.0.0.0/0"] 31 | 32 | security_group_id = "${aws_security_group.lb_security_group.id}" 33 | } 34 | -------------------------------------------------------------------------------- /modules/jenkins-alb-security-group/output.tf: -------------------------------------------------------------------------------- 1 | output "lb_security_group_id" { 2 | value = "${aws_security_group.lb_security_group.id}" 3 | } 4 | -------------------------------------------------------------------------------- /modules/jenkins-alb-security-group/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "The ID of the VPC" 3 | default = "" 4 | } 5 | 6 | variable "name_prefix" { 7 | description = "The prefix for the load balancer instance name" 8 | } 9 | 10 | variable "allowed_inbound_cidr_blocks" { 11 | description = "Networks to allow to connect to Jenkins" 12 | type = "list" 13 | } 14 | 15 | variable "jenkins_private_ip" { 16 | description = "Private IP address of the Jenkins instance" 17 | } 18 | -------------------------------------------------------------------------------- /modules/jenkins-alb/main.tf: -------------------------------------------------------------------------------- 1 | # This module builds an Application Load balancer 2 | 3 | data "aws_instance" "jenkins" { 4 | instance_id = "${var.jenkins_instance_id}" 5 | } 6 | 7 | data "aws_subnet_ids" "default" { 8 | vpc_id = "${var.vpc_id}" 9 | } 10 | 11 | # Create the security group to control access to the load balancer 12 | module "lb-security-group" { 13 | source = "../jenkins-alb-security-group" 14 | name_prefix = "${var.name_prefix}-lb-sg" 15 | allowed_inbound_cidr_blocks = "${var.allowed_inbound_cidr_blocks}" 16 | jenkins_private_ip = "${data.aws_instance.jenkins.private_ip}" 17 | vpc_id = "${var.vpc_id}" 18 | } 19 | 20 | # Create the Application Load Balancer, attached to the given subnets 21 | resource "aws_lb" "alb" { 22 | name = "${var.name_prefix}-alb" 23 | security_groups = ["${module.lb-security-group.lb_security_group_id}"] 24 | subnets = ["${var.subnet_ids}"] 25 | } 26 | 27 | # Create a target group to send traffic to for JIRA 28 | resource "aws_lb_target_group" "alb_tg" { 29 | name = "${var.name_prefix}-alb-tg" 30 | port = "${var.http_port}" 31 | protocol = "HTTP" 32 | vpc_id = "${var.vpc_id}" 33 | } 34 | 35 | # Attach the JIRA EC2 instance to the target group 36 | resource "aws_lb_target_group_attachment" "tg_attach" { 37 | target_group_arn = "${aws_lb_target_group.alb_tg.arn}" 38 | target_id = "${var.jenkins_instance_id}" 39 | port = "${var.http_port}" 40 | } 41 | 42 | # Associate the listener resource to the load balancer, and configure SSL 43 | resource "aws_lb_listener" "lb-listener" { 44 | load_balancer_arn = "${aws_lb.alb.arn}" 45 | port = 443 46 | protocol = "HTTPS" 47 | ssl_policy = "ELBSecurityPolicy-2016-08" 48 | certificate_arn = "${var.aws_ssl_certificate_arn}" 49 | 50 | "default_action" { 51 | target_group_arn = "${aws_lb_target_group.alb_tg.arn}" 52 | type = "forward" 53 | } 54 | } 55 | 56 | # Update the DNS zone in AWS Route53 to point our domain name to this ALB 57 | data "aws_route53_zone" "app_dns_zone" { 58 | name = "${var.dns_zone}" 59 | private_zone = false 60 | } 61 | 62 | resource "aws_route53_record" "dns" { 63 | zone_id = "${data.aws_route53_zone.app_dns_zone.zone_id}" 64 | name = "${var.app_dns_name}" 65 | type = "A" 66 | 67 | alias { 68 | name = "${aws_lb.alb.dns_name}" 69 | zone_id = "${aws_lb.alb.zone_id}" 70 | evaluate_target_health = false 71 | } 72 | } -------------------------------------------------------------------------------- /modules/jenkins-alb/output.tf: -------------------------------------------------------------------------------- 1 | output "lb_dns_name" { 2 | value = "${aws_lb.alb.dns_name}" 3 | } 4 | -------------------------------------------------------------------------------- /modules/jenkins-alb/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name_prefix" { 2 | description = "Name to be used on resources as prefix" 3 | default = "" 4 | } 5 | 6 | variable "vpc_id" { 7 | description = "The ID of the VPC" 8 | default = "" 9 | } 10 | 11 | variable "allowed_inbound_cidr_blocks" { 12 | description = "Networks to allow to connect to the load balancer" 13 | type = "list" 14 | } 15 | 16 | variable "http_port" { 17 | description = "HTTP port to access the application server behind the ALB" 18 | } 19 | 20 | variable "jenkins_instance_id" { 21 | description = "The ID of the Jenkins master instance" 22 | } 23 | 24 | variable "subnet_ids" { 25 | description = "Subnets for the load balancer listener to use" 26 | type = "list" 27 | } 28 | 29 | variable "aws_ssl_certificate_arn" { 30 | description = "Amazon Resource Name for the certificate to be used on the load balancer for HTTPS" 31 | } 32 | 33 | variable "dns_zone" { 34 | description = "DNS zone in AWS Route53 to use with the ALB" 35 | } 36 | 37 | variable "app_dns_name" { 38 | description = "DNS name within the zone to dynamically point to the ALB" 39 | } 40 | -------------------------------------------------------------------------------- /modules/jenkins-master/main.tf: -------------------------------------------------------------------------------- 1 | # Launch EC2 instance for master 2 | 3 | // TODO: Add ELB for HTTP & HTTPS 4 | // TODO: Lockdown traffic and make instances private 5 | // TODO: Refactor security group code so Master has SSH, HTTP + JNLP and Slaves only have SSH, JNLP 6 | 7 | # Master ELB 8 | /* 9 | resource "aws_iam_server_certificate" "test_cert" { 10 | name_prefix = "example-cert" 11 | certificate_body = "${file("self-ca-cert.pem")}" 12 | private_key = "${file("test-key.pem")}" 13 | 14 | lifecycle { 15 | create_before_destroy = true 16 | } 17 | } 18 | 19 | resource "aws_elb" "ourapp" { 20 | name = "terraform-asg-deployment-example" 21 | availability_zones = ["us-west-2a"] 22 | cross_zone_load_balancing = true 23 | 24 | listener { 25 | instance_port = 8000 26 | instance_protocol = "http" 27 | lb_port = 443 28 | lb_protocol = "https" 29 | ssl_certificate_id = "${aws_iam_server_certificate.test_cert.arn}" 30 | } 31 | 32 | # The instances are registered automatically 33 | instances = ["${aws_instance.web.*.id}"] 34 | } 35 | 36 | */ 37 | 38 | # Master Server 39 | resource "aws_instance" "ec2_jenkins_master" { 40 | count = 1 41 | ami = "${var.ami_id}" 42 | instance_type = "${var.instance_type}" 43 | user_data = "${var.user_data}" 44 | key_name = "${var.ssh_key_name}" 45 | monitoring = true 46 | vpc_security_group_ids = ["${module.security_group_rules.jenkins_security_group_id}"] 47 | tags = "${merge(map("Name", format("%s-%d", var.name, count.index+1)), map("Terraform", "true"), map("Environment", var.environment), var.tags)}" 48 | 49 | provisioner "file" { 50 | connection = { 51 | user = "ec2-user" 52 | private_key = "${file(var.ssh_key_path)}" 53 | } 54 | content = "${var.setup_data}" 55 | destination = "/tmp/setup.sh" 56 | } 57 | 58 | provisioner "remote-exec" { 59 | connection = { 60 | user = "ec2-user" 61 | private_key = "${file(var.ssh_key_path)}" 62 | } 63 | inline = [ 64 | "chmod +x /tmp/setup.sh", 65 | "sudo /tmp/setup.sh" 66 | ] 67 | } 68 | } 69 | 70 | module "security_group_rules" { 71 | source = "../jenkins-security-group-rules" 72 | 73 | name = "${var.name}" 74 | allowed_inbound_cidr_blocks = ["${var.allowed_inbound_cidr_blocks}"] 75 | allowed_ssh_cidr_blocks = ["${var.allowed_ssh_cidr_blocks}"] 76 | 77 | http_port = "${var.http_port}" 78 | https_port = "${var.https_port}" 79 | jnlp_port = "${var.jnlp_port}" 80 | } 81 | 82 | # Add the application load balancer 83 | module "jenkins-alb" { 84 | source = "../jenkins-alb" 85 | name_prefix = "${var.alb_prefix}" 86 | vpc_id = "${var.vpc_id}" 87 | allowed_inbound_cidr_blocks = "${var.allowed_inbound_cidr_blocks}" 88 | http_port = "${var.http_port}" 89 | jenkins_instance_id = "${aws_instance.ec2_jenkins_master.id}" 90 | subnet_ids = "${var.subnet_ids}" 91 | aws_ssl_certificate_arn = "${var.aws_ssl_certificate_arn}" 92 | app_dns_name = "${var.app_dns_name}" 93 | dns_zone = "${var.dns_zone}" 94 | } 95 | -------------------------------------------------------------------------------- /modules/jenkins-master/outputs.tf: -------------------------------------------------------------------------------- 1 | output "security_group_name" { 2 | value = "${module.security_group_rules.security_group_name}" 3 | } 4 | 5 | output "jenkins_security_group_id" { 6 | value = "${module.security_group_rules.jenkins_security_group_id}" 7 | } 8 | 9 | output "private_ip" { 10 | value = "${aws_instance.ec2_jenkins_master.private_ip}" 11 | } 12 | 13 | output "public_ip" { 14 | value = "${aws_instance.ec2_jenkins_master.public_ip}" 15 | } -------------------------------------------------------------------------------- /modules/jenkins-master/setup.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -x 3 | # This script is meant to be run in the User Data of each EC2 Instance while it's booting. 4 | 5 | function waitForJenkins() { 6 | echo "Waiting jenkins to launch on 8080..." 7 | 8 | while ! nc -z localhost 8080; do 9 | sleep 0.1 # wait for 1/10 of the second before check again 10 | done 11 | 12 | echo "Jenkins launched" 13 | } 14 | 15 | function waitForPasswordFile() { 16 | echo "Waiting jenkins to generate password..." 17 | 18 | while [ ! -f /var/lib/jenkins/secrets/initialAdminPassword ]; do 19 | sleep 2 # wait for 1/10 of the second before check again 20 | done 21 | 22 | echo "Password created" 23 | } 24 | 25 | sudo service jenkins start 26 | sudo chkconfig --add jenkins 27 | 28 | waitForJenkins 29 | 30 | # UPDATE PLUGIN LIST 31 | curl -L http://updates.jenkins-ci.org/update-center.json | sed '1d;$d' | curl -X POST -H 'Accept: application/json' -d @- http://localhost:8080/updateCenter/byId/default/postBack 32 | 33 | sleep 10 34 | 35 | waitForJenkins 36 | 37 | # INSTALL CLI 38 | sudo cp /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar /var/lib/jenkins/jenkins-cli.jar 39 | 40 | waitForPasswordFile 41 | 42 | PASS=$(sudo bash -c "cat /var/lib/jenkins/secrets/initialAdminPassword") 43 | 44 | sleep 10 45 | 46 | # SET AGENT PORT 47 | xmlstarlet ed -u "//slaveAgentPort" -v "${jnlp_port}" /var/lib/jenkins/config.xml > /tmp/jenkins_config.xml 48 | sudo mv /tmp/jenkins_config.xml /var/lib/jenkins/config.xml 49 | sudo service jenkins restart 50 | 51 | waitForJenkins 52 | 53 | sleep 10 54 | 55 | # INSTALL PLUGINS 56 | sudo java -jar /var/lib/jenkins/jenkins-cli.jar -s http://localhost:8080 -auth admin:$PASS install-plugin ${plugins} 57 | 58 | # RESTART JENKINS TO ACTIVATE PLUGINS 59 | sudo java -jar /var/lib/jenkins/jenkins-cli.jar -s http://localhost:8080 -auth admin:$PASS restart -------------------------------------------------------------------------------- /modules/jenkins-master/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Name to be used for the Jenkins master instance" 3 | } 4 | 5 | variable "environment" { 6 | description = "The environement tag to add to Jenkins master instance", 7 | default = "" 8 | } 9 | 10 | variable "ami_id" { 11 | description = "The ID of the AMI to run in this Jenkins master instance" 12 | } 13 | 14 | variable "instance_type" { 15 | description = "Instance Type to use for Jenkins master" 16 | } 17 | 18 | variable "vpc_id" { 19 | description = "The ID of the VPC" 20 | default = "" 21 | } 22 | 23 | variable "subnet_ids" { 24 | description = "Subnets for the load balancer listener to use" 25 | type = "list" 26 | } 27 | 28 | variable "aws_ssl_certificate_arn" { 29 | description = "Amazon Resource Name for the certificate to be used on the load balancer for HTTPS" 30 | } 31 | 32 | variable "dns_zone" { 33 | description = "DNS zone in AWS Route53 to use with the ALB" 34 | } 35 | 36 | variable "app_dns_name" { 37 | description = "DNS name within the zone to dynamically point to the ALB" 38 | } 39 | 40 | variable "alb_prefix" { 41 | description = "Naming prefix for ALB-related resources" 42 | } 43 | 44 | variable "user_data" { 45 | description = "A User Data script to execute while the server is booting." 46 | } 47 | 48 | variable "setup_data" { 49 | description = "A User Data script to execute after server has booted to setup jenkins defaults." 50 | } 51 | 52 | variable "ssh_key_name" { 53 | description = "The name of an EC2 Key Pair that can be used to SSH to the EC2 Instances in this cluster. Set to an empty string to not associate a Key Pair." 54 | default = "" 55 | } 56 | 57 | variable "ssh_key_path" { 58 | description = "The path of an EC2 Key Pair that can be used to SSH to the EC2 Instances in this cluster. Used for provisioning." 59 | default = "" 60 | } 61 | 62 | variable "allowed_ssh_cidr_blocks" { 63 | description = "A list of CIDR-formatted IP address ranges from which the EC2 Instances will allow connections on SSH" 64 | type = "list" 65 | } 66 | 67 | variable "allowed_inbound_cidr_blocks" { 68 | description = "A list of CIDR-formatted IP address ranges from which the EC2 Instances will allow connections to Jenkins" 69 | type = "list" 70 | } 71 | 72 | variable "ssh_port" { 73 | description = "The port used for SSH connections" 74 | default = 22 75 | } 76 | 77 | variable "http_port" { 78 | description = "The port to use for HTTP traffic to Jenkins" 79 | default = 8080 80 | } 81 | 82 | variable "https_port" { 83 | description = "The port to use for HTTPS traffic to Jenkins" 84 | default = 443 85 | } 86 | 87 | variable "jnlp_port" { 88 | description = "The port to use for TCP traffic between Jenkins intances" 89 | default = 49187 90 | } 91 | 92 | variable "tags" { 93 | type = "map" 94 | description = "Supply tags you want added to all resources" 95 | default = { 96 | } 97 | } -------------------------------------------------------------------------------- /modules/jenkins-security-group-rules/master.tf: -------------------------------------------------------------------------------- 1 | # create security group to allow ssh 2 | resource "aws_security_group" "jenkins_security_group" { 3 | name_prefix = "${var.name}" 4 | description = "Security group for the ${var.name}" 5 | vpc_id = "${var.vpc_id}" 6 | } 7 | 8 | resource "aws_security_group_rule" "allow_ssh_inbound" { 9 | type = "ingress" 10 | from_port = "${var.ssh_port}" 11 | to_port = "${var.ssh_port}" 12 | protocol = "tcp" 13 | cidr_blocks = ["${var.allowed_ssh_cidr_blocks}"] 14 | 15 | security_group_id = "${aws_security_group.jenkins_security_group.id}" 16 | } 17 | 18 | resource "aws_security_group_rule" "allow_all_outbound" { 19 | type = "egress" 20 | from_port = 0 21 | to_port = 0 22 | protocol = "-1" 23 | cidr_blocks = ["0.0.0.0/0"] 24 | 25 | security_group_id = "${aws_security_group.jenkins_security_group.id}" 26 | } 27 | 28 | resource "aws_security_group_rule" "allow_http_inbound" { 29 | type = "ingress" 30 | from_port = "${var.http_port}" 31 | to_port = "${var.http_port}" 32 | protocol = "tcp" 33 | cidr_blocks = ["${var.allowed_inbound_cidr_blocks}"] 34 | 35 | security_group_id = "${aws_security_group.jenkins_security_group.id}" 36 | } 37 | 38 | resource "aws_security_group_rule" "allow_https_inbound" { 39 | type = "ingress" 40 | from_port = "${var.https_port}" 41 | to_port = "${var.https_port}" 42 | protocol = "tcp" 43 | cidr_blocks = ["${var.allowed_inbound_cidr_blocks}"] 44 | 45 | security_group_id = "${aws_security_group.jenkins_security_group.id}" 46 | } 47 | 48 | resource "aws_security_group_rule" "allow_jnlp_inbound" { 49 | type = "ingress" 50 | from_port = "${var.jnlp_port}" 51 | to_port = "${var.jnlp_port}" 52 | protocol = "tcp" 53 | cidr_blocks = ["${var.allowed_inbound_cidr_blocks}"] 54 | 55 | security_group_id = "${aws_security_group.jenkins_security_group.id}" 56 | } -------------------------------------------------------------------------------- /modules/jenkins-security-group-rules/outputs.tf: -------------------------------------------------------------------------------- 1 | # The Name of the security group to which we should add the Jenkins security group rules 2 | output "security_group_name" { 3 | value = "${aws_security_group.jenkins_security_group.name}" 4 | } 5 | 6 | # The ID of the security group to which we should add the Jenkins security group rules 7 | output "jenkins_security_group_id" { 8 | value = "${aws_security_group.jenkins_security_group.id}" 9 | } -------------------------------------------------------------------------------- /modules/jenkins-security-group-rules/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_id" { 2 | description = "The ID of the VPC" 3 | default = "" 4 | } 5 | 6 | variable "name" { 7 | description = "The Name of the security group to which we should add the Jenkins security group rules" 8 | } 9 | 10 | variable "allowed_inbound_cidr_blocks" { 11 | description = "A list of CIDR-formatted IP address ranges from which the EC2 Instances will allow connections to Jenkins" 12 | type = "list" 13 | } 14 | 15 | variable "allowed_ssh_cidr_blocks" { 16 | description = "A list of CIDR-formatted IP address ranges from which the EC2 Instances will allow connections on SSH" 17 | type = "list" 18 | } 19 | 20 | variable "ssh_port" { 21 | description = "The port used for SSH connections" 22 | default = 22 23 | } 24 | 25 | variable "http_port" { 26 | description = "The port to use for HTTP traffic to Jenkins" 27 | default = 8080 28 | } 29 | 30 | variable "https_port" { 31 | description = "The port to use for HTTPS traffic to Jenkins" 32 | default = 443 33 | } 34 | 35 | variable "jnlp_port" { 36 | description = "The port to use for TCP traffic between Jenkins intances" 37 | default = 49187 38 | } -------------------------------------------------------------------------------- /modules/jenkins-slave/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | jenkins_master_url = "http://${var.jenkins_master_ip}:${var.jenkins_master_port}" 4 | } 5 | 6 | data "aws_ami" "jenkins_linux_slave" { 7 | most_recent = true 8 | 9 | # If we change the AWS Account in which test are run, update this value. 10 | owners = ["312506926764"] 11 | 12 | filter { 13 | name = "virtualization-type" 14 | values = ["hvm"] 15 | } 16 | 17 | filter { 18 | name = "is-public" 19 | values = ["false"] // flip to public when ready for release 20 | } 21 | 22 | filter { 23 | name = "name" 24 | values = ["jenkins-slave-amazon-linux-*"] 25 | } 26 | } 27 | 28 | # Jenkins Slaves 29 | resource "aws_instance" "ec2_jenkins_slave" { 30 | count = "${var.count}" 31 | ami = "${var.ami_id == "" ? data.aws_ami.jenkins_linux_slave.image_id : var.ami_id}" 32 | instance_type = "${var.instance_type}" 33 | key_name = "${var.ssh_key_name}" 34 | monitoring = true 35 | vpc_security_group_ids = ["${var.jenkins_security_group_id}"] 36 | tags = "${merge(map("Name", format("%s-%d", var.name, count.index+1)), map("Terraform", "true"), map("Environment", var.environment), var.tags)}" 37 | 38 | provisioner "file" { 39 | connection = { 40 | user = "ec2-user" 41 | private_key = "${file(var.ssh_key_path)}" 42 | } 43 | content = "${file(var.ssh_key_path)}" 44 | destination = "/tmp/key.pem" 45 | } 46 | 47 | # Download dependencies from Master 48 | provisioner "remote-exec" { 49 | connection = { 50 | user = "ec2-user" 51 | private_key = "${file(var.ssh_key_path)}" 52 | } 53 | 54 | inline = [ 55 | "chmod 0600 /tmp/key.pem", 56 | "ssh -oStrictHostKeyChecking=no -i /tmp/key.pem ec2-user@${var.jenkins_master_ip} 'sudo cat /var/lib/jenkins/secrets/initialAdminPassword' > /tmp/secret", 57 | "wget -P /tmp ${local.jenkins_master_url}/jnlpJars/jenkins-cli.jar", 58 | "wget -P /tmp ${local.jenkins_master_url}/jnlpJars/slave.jar", 59 | "sudo mv /tmp/slave.jar /home/jenkins/jenkins-slave/" 60 | ] 61 | } 62 | 63 | provisioner "file" { 64 | connection = { 65 | user = "ec2-user" 66 | private_key = "${file(var.ssh_key_path)}" 67 | } 68 | content = "${data.template_file.bootstrap.rendered}" 69 | destination = "/tmp/bootstrap.sh" 70 | } 71 | 72 | # Register & Launch slave 73 | provisioner "remote-exec" { 74 | connection = { 75 | user = "ec2-user" 76 | private_key = "${file(var.ssh_key_path)}" 77 | } 78 | 79 | inline = [ 80 | "sudo chmod +x /tmp/bootstrap.sh", 81 | "/tmp/bootstrap.sh ${self.tags["Name"]}" 82 | ] 83 | } 84 | 85 | # Cleanup node registration 86 | # Currenlty unable to clean-up Jenkins node meta due to TF issue: https://github.com/hashicorp/terraform/issues/13549 87 | /* 88 | provisioner "remote-exec" { 89 | when = "destroy" 90 | 91 | connection = { 92 | host = "${self.private_ip}" 93 | user = "ec2-user" 94 | private_key = "${file(var.ssh_key_path)}" 95 | } 96 | 97 | inline = [ 98 | "sudo service jenkins-slave stop", 99 | "sudo java -jar /tmp/jenkins-cli.jar -auth admin:$( 8 | $1 9 | 10 | /home/jenkins 11 | 2 12 | NORMAL 13 | 14 | 15 | $1 16 | 22 17 | admin 18 | 19 | 20 | 21 | admin 22 | 23 | EOF 24 | 25 | 26 | export TOKEN=$(curl --user "admin:$PASS" -s ${jenkins_master_url}/crumbIssuer/api/json | python -c 'import sys,json;j=json.load(sys.stdin);print j["crumbRequestField"] + "=" + j["crumb"]') 27 | 28 | cat > /tmp/secret.groovy </dev/null <