├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── appspec.yml ├── buildspec.yml ├── cloudformation ├── go-applications-pipeline-codecommit.template ├── go-applications-pipeline-github.template ├── go-applications-pipeline-infrastructure.template ├── infrastructure.yml └── pipeline.yml ├── codedeploy ├── AfterInstall.sh ├── ApplicationStart.sh ├── ApplicationStop.sh ├── BeforeInstall.sh └── ValidateService.sh ├── config ├── production.conf └── staging.conf ├── images ├── architecture-overview.graffle │ ├── data.plist │ ├── image12.pdf │ ├── image14.pdf │ ├── image6.pdf │ ├── image8.pdf │ └── image9.pdf ├── architecture-overview.png ├── cicd-overview.png ├── cloudformation-launch-stack.png ├── codebuild-log.png ├── fork.png ├── pipeline-screenshot.png ├── pipeline-stage-approvals.png ├── pipeline-stage-build.png ├── pipeline-stage-production.png ├── pipeline-stage-source.png ├── pipeline-stage-staging.png ├── stack-outputs.png └── stack-settings.png ├── main.go └── main_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ecs-refarch-cloudformation 2 | Copyright 2011-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deployment Pipeline for Go Applications on AWS 2 | 3 | ![pipeline-screenshot](images/pipeline-screenshot.png) 4 | 5 | This repository provides an easy-to-deploy pipeline for the development, testing, building and deployment of applications written in Go. Although this example is tailored to Go, it can be easily modified to deploy applications written in other languages too. 6 | 7 | Services Used: 8 | 9 | * [AWS CodePipeline](https://aws.amazon.com/codepipeline/) for pipeline creation. 10 | * [AWS CodeBuild](https://aws.amazon.com/codebuild/) for testing and building your Go application(s). 11 | * [AWS CloudFormation](https://aws.amazon.com/cloudformation/) for deploying infrastructure (Infrastructure-as-Code). 12 | * [AWS CodeDeploy](https://aws.amazon.com/codedeploy/) for zero downtime deployments of your application(s). 13 | 14 | This pipeline allows you easily apply [continuous delivery](https://aws.amazon.com/devops/continuous-delivery/) or [continuous deployment](https://aws.amazon.com/devops/continuous-delivery/) principles to your development lifecycle. 15 | 16 | ![cicd-overview](images/cicd-overview.png) 17 | 18 | ## Table of Contents 19 | 20 | * [What's in the box?](#what's-in-the-box?) 21 | * [Source Control](#source-control) 22 | * [Build / Test](#build--test) 23 | * [Infrastructure-as-Code](#infrastructure-as-code) 24 | * [Zero Downtime Application Deployment](#zero--downtime-application-deployment-staging-production) 25 | * [Per Environment Configuration](#per-environment-configuration) 26 | * [Optional manual gate for Production](#optional-manual-gate-before-production) 27 | * [How do I deploy this?](#how-do-i-deploy-this) 28 | * [Fork the repository](#1-fork-this-github-repository-to-your-github-account) 29 | * [Deploy the pipeline to your AWS account](#2-deploy-the-pipeline-to-your-aws-account) 30 | * [Develop, deploy, iterate!](#3-develop-deploy-iterate) 31 | * [How does this work?](#how-does-this-work) 32 | * [Stage: Source](#stage-source) 33 | * [Stage: Test & Build](#stage-test--build) 34 | * [Stage: Deploy to Staging](#stage-deploy-to-staging) 35 | * [Stage: Manual Approvals (optional)](#stage-manual-approvals) 36 | * [Stage: Deploy to Production](#stage-deploy-to-production) 37 | * [Contributing](#contributing) 38 | * [License](#license) 39 | 40 | ## What's in the box? 41 | 42 | ### Source control 43 | 44 | A pipeline will be configured in [AWS CodePipeline](https://aws.amazon.com/codepipeline). It will automatically monitor a [GitHub](https://github.com) repository for modifications to applications or infrastructure and push them through the delivery pipeline. 45 | 46 | ### Build/test 47 | 48 | [AWS CodeBuild](https://aws.amazon.com/codebuild) will be used to: 49 | 50 | - Run any tests included in the project (using `go test`). 51 | - Check for code lint errors (using `golint`). 52 | - Build your Go application. 53 | - Archive all build artifacts to S3. 54 | 55 | To read or modify the [AWS CodeBuild](https://aws.amazon.com/codebuild) configuration, see [buildspec.yml](buildspec.yml). 56 | 57 | ### Infrastructure as code 58 | 59 | [AWS CloudFormation](https://aws.amazon.com/cloudformation) will be used to deploy a staging and production environment suitable for hosting Go applications on AWS. This is incorporated into the same pipeline as the application development. Infrastructure for the staging and production environments will be automatically deployed as required. 60 | 61 | By default, this will configure: 62 | 63 | - A [VPC](https://aws.amazon.com/vpc), with public and private subnets across multiple [Availability Zones (AZs)](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html). 64 | - An [Application load balancer](https://aws.amazon.com/elasticloadbalancing/applicationloadbalancer/) in the public subnets. 65 | - An [Auto Scaling group](https://aws.amazon.com/autoscaling/) of EC2 instances in private subnets. 66 | - [NAT gateways](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html) to allow outbound internet access from the private subnets. 67 | 68 | The following diagram shows an overview of the infrastructure deployed for each environment (staging and production): 69 | 70 | ![architecture-overview](images/architecture-overview.png) 71 | 72 | You can read or modify the infrastructure deployed in [cloudformation/infrastructure.yml](cloudformation/infrastructure.yml). 73 | 74 | ### Zero-downtime application deployment (staging/production) 75 | 76 | A sample Go application is included (see [main.go](main.go)) that acts as a simple webserver. 77 | 78 | When changes are made to the Go application, [AWS CodePipeline](https://aws.amazon.com/codepipeline) will automatically take the changes through the deployment pipeline. 79 | 80 | [AWS CodeBuild](https://aws.amazon.com/codebuild) will run all tests, build the application, and then archive successful builds to [Amazon S3](https://aws.amazon.com/s3). 81 | 82 | [AWS CodeDeploy](https://aws.amazon.com/codedeploy) will deploy the new application version to the [Auto Scaling group](https://aws.amazon.com/autoscaling) of [Amazon EC2](https://aws.amazon.com/ec2/) instances. By default, [AWS CodeDeploy](https://aws.amazon.com/codedeploy) will use the `CodeDeployDefault.OneAtATime` deployment strategy. However you can modify the [per-environment configuration](#per-environment-configuration) to use any of the strategies listed [here](http://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html). 83 | 84 | ### Per-environment configuration 85 | 86 | In the [config/](config/) directory you will find a configuration file for each environment that you can use to override key settings, such as the [AWS CodeDeploy](https://aws.amazon.com/codedeploy) deployment strategy, instance types, and Auto Scaling group sizes. 87 | 88 | Here is an example: 89 | 90 | ```json 91 | { 92 | "Parameters": { 93 | "InstanceType": "t2.micro", 94 | "InstanceCount": "2", 95 | "DeploymentStrategy": "CodeDeployDefault.OneAtATime", 96 | "VpcCIDR": "10.193.0.0/16", 97 | "PublicSubnet1CIDR": "10.193.10.0/24", 98 | "PublicSubnet2CIDR": "10.193.11.0/24", 99 | "PrivateSubnet1CIDR": "10.193.20.0/24", 100 | "PrivateSubnet2CIDR": "10.193.21.0/24" 101 | } 102 | } 103 | ``` 104 | 105 | See: 106 | 107 | * [config/staging.conf](config/staging.conf) 108 | * [config/production.conf](config/production.conf) 109 | 110 | Any changes made and committed back to your repository will be run through the pipeline and applied to your infrastructure/application automatically. 111 | 112 | 113 | ### Optional manual gate before production 114 | 115 | This pipeline includes an (optional) manual approval stage between the staging and production environments. This can be useful if you are not yet at the stage where you can move to fully automated continuous deployment. 116 | 117 | 118 | ## How do I deploy this? 119 | 120 | #### 1. Fork this GitHub repository to your GitHub account 121 | 122 | This new repository will be used for developing your application, infrastructure, and pipeline (through [AWS CloudFormation](https://aws.amazon.com/cloudformation)). Click the following button to initiate the fork: 123 | 124 | [![fork-repository](images/fork.png)](https://github.com/awslabs/golang-deployment-pipeline/fork) 125 | 126 | #### 2. Deploy the pipeline to your AWS account 127 | 128 | The pipeline is available as a [AWS CloudFormation](https://aws.amazon.com/cloudformation) template, and included in this repository ([see cloudformation/pipeline.yml](cloudformation/pipeline.yml)). Click the following button to deploy it to your AWS account in the `us-east-1` region: 129 | 130 | [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=\-pipeline&templateURL=https://s3.amazonaws.com/golang-deployment-pipeline/cloudformation/pipeline.yml) 131 | 132 | You will need to provide some parameters to point [AWS CodePipeline](https://aws.amazon.com/codepipeline) to the repository you forked in your account. For example: 133 | 134 | ![stack-settings](images/stack-settings.png) 135 | 136 | After the [AWS CloudFormation](https://aws.amazon.com/cloudformation) stack has been created, you will find your new pipeline in the [AWS CodePipeline](https://aws.amazon.com/codepipeline) console. 137 | 138 | #### 3. Develop, deploy, iterate! 139 | 140 | Now that your pipeline is setup, you can start the fun part: developing your application! Make some changes to the example Go application in [main.go](main.go), and then commit the changes to your [GitHub](https://github.com) repository. 141 | 142 | In the [AWS CodePipeline](https://aws.amazon.com/codepipeline) console, you should see the changes you've made flow through the pipeline. 143 | 144 | To find the URL of your staging and production environments, go to [AWS CloudFormation](https://aws.amazon.com/cloudformation) console, find the staging or production stack and look at the stack output. You will see a link to your load-balanced URL. For convenience, you will probably want to create a more convenient CNAME in your DNS to point to this URL. (Or just bookmark it!) It will remain constant across application deployments. 145 | 146 | ## How does this work? 147 | 148 | ### Stage: Source 149 | 150 | ![pipeline-stage-source](images/pipeline-stage-source.png) 151 | 152 | A hook is used to notify [AWS CodePipeline](https://aws.amazon.com/codepipeline) of any updates to your GitHub repository. This will trigger the `Source` stage of your pipeline automatically. 153 | 154 | ### Stage: Test and Build 155 | 156 | ![pipeline-stage-source](images/pipeline-stage-build.png) 157 | 158 | [AWS CodeBuild](https://aws.amazon.com/codebuild) will then run your application tests, check code linting and build your application. After a successful build, it will archive the build artifact(s) to the [Amazon S3](https://aws.amazon.com/s3) bucket provided when you deployed your pipeline. 159 | 160 | If there are any failures in your test or build process, they will be displayed in the [AWS CodePipeline](https://aws.amazon.com/codepipeline) console. Click on the **Detail** link in the pipeline stage to see the full build log. This will help you identify the reason for the failure. 161 | 162 | ![codebuild-log](images/codebuild-log.png) 163 | 164 | ### Stage: Deploy to staging 165 | 166 | ![pipeline-stage-source](images/pipeline-stage-staging.png) 167 | 168 | This stage uses [AWS CodePipeline's ability to deploy with AWS CloudFormation](https://aws.amazon.com/blogs/aws/codepipeline-update-build-continuous-delivery-workflows-for-cloudformation-stacks/). 169 | 170 | It will deploy the infrastructure for the staging environment (as defined in [cloudformation/infrastructure.yml](cloudformation/infrastructure.yml)). 171 | 172 | The first time the pipeline stage runs, it will create the environment from scratch. On future runs, it will apply any updates through a [CloudFormation stack update](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html). 173 | 174 | It will also use [AWS CodeDeploy](https://aws.amazon.com/codedeploy) to deploy and validate the application. You can see the [AWS CodeDeploy](https://aws.amazon.com/codedeploy) configuration in [appspec.yml](appspec.yml) 175 | 176 | ### Stage: Manual Approvals 177 | 178 | ![pipeline-stage-source](images/pipeline-stage-approvals.png) 179 | 180 | This pipeline includes an (optional) manual approval stage between the staging and production environments. This can be useful if you are not yet at the stage where you can move to fully automated continuous deployment. 181 | 182 | If you want to remove this from your pipeline, just edit the pipeline definition in [cloudformation/pipeline.yml](cloudformation/pipeline.yml) and update your pipeline [AWS CloudFormation](https://aws.amazon.com/cloudformation) stack with the new template. 183 | 184 | The approval can be configured to trigger an [Amazon Simple Notification Service (SNS)](https://aws.amazon.com/sns) notification. This allows you to notify approvers of pending actions through Email, SMS or a custom [AWS Lambda](https://aws.amazon.com/lambda) function. 185 | 186 | ### Stage: Deploy to production 187 | 188 | ![pipeline-stage-source](images/pipeline-stage-production.png) 189 | 190 | This stage deploys a whole new environment (everything in the architecture diagram) for your production environment. This ensures an identical configuration between environments, reducing false positives in your testing. 191 | 192 | The first time the pipeline stage runs, it will create the environment from scratch. On future runs of the pipeline, it will apply any updates via a [CloudFormation stack update](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html). 193 | 194 | ## Want to contribute? 195 | 196 | You can [create a GitHub issue](https://github.com/awslabs/golang-deployment-pipeline/issues/new) for any feature requests, bugs, or documentation improvements. 197 | 198 | Where possible, please [submit a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) for the change. 199 | -------------------------------------------------------------------------------- /appspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.0 2 | os: linux 3 | files: 4 | - source: /app 5 | destination: /opt 6 | hooks: 7 | BeforeInstall: 8 | - location: codedeploy/BeforeInstall.sh 9 | AfterInstall: 10 | - location: codedeploy/AfterInstall.sh 11 | ApplicationStop: 12 | - location: codedeploy/ApplicationStop.sh 13 | ApplicationStart: 14 | - location: codedeploy/ApplicationStart.sh 15 | ValidateService: 16 | - location: codedeploy/ValidateService.sh 17 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | 3 | phases: 4 | 5 | install: 6 | commands: 7 | - go get -u github.com/golang/lint/golint 8 | 9 | pre_build: 10 | commands: 11 | 12 | # Ensure code passes all lint tests 13 | - golint -set_exit_status 14 | 15 | # Run all tests included with our application 16 | - go test 17 | 18 | build: 19 | commands: 20 | 21 | # Build our application 22 | - go build -o app 23 | 24 | artifacts: 25 | files: 26 | - app 27 | - appspec.yml 28 | - buildspec.yml 29 | - config/* 30 | - codedeploy/* 31 | - cloudformation/* 32 | -------------------------------------------------------------------------------- /cloudformation/go-applications-pipeline-codecommit.template: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | (SO0024) - Deployment Pipeline for Go Applications on AWS - AWS CloudFormation Template for 4 | AWS Solutions Builder Deployment Pipeline for Go Applications on AWS - **WARNING** This template 5 | creates AWS resources. You will be billed for the AWS resources used if you create 6 | a stack from this template. 7 | This CloudFormation template will deploy a full CI/CD pipeline for Go 8 | development. It includes building with AWS CodeBuild, and infrastructure 9 | deployment via AWS CloudFormation. It will create an AWS CodePipeline for 10 | orchastrating builds, testing and deployments to beta and production 11 | environments. 12 | 13 | Parameters: 14 | 15 | ApplicationName: 16 | Description: This will be used to name the pipeline and build resources 17 | Type: String 18 | AllowedPattern: '[A-Za-z0-9-]+' 19 | 20 | ArtifactS3Bucket: 21 | Description: An existing S3 bucket within this AWS account 22 | Type: String 23 | 24 | CodeCommitRepository: 25 | Description: Enter the repository name that should be monitored for changes 26 | Type: String 27 | 28 | CodeCommitBranch: 29 | Description: Enter the CodeCommit branch to monitored 30 | Type: String 31 | Default: master 32 | 33 | Mappings: 34 | Send: 35 | AnonymousUsage: 36 | Data: Yes 37 | 38 | Metadata: 39 | 40 | AWS::CloudFormation::Interface: 41 | ParameterGroups: 42 | - 43 | Label: 44 | default: Application Configuration 45 | Parameters: 46 | - ApplicationName 47 | - ArtifactS3Bucket 48 | - 49 | Label: 50 | default: CodeCommit Configuration 51 | Parameters: 52 | - CodeCommitRepository 53 | - CodeCommitBranch 54 | 55 | ParameterLabels: 56 | ApplicationName: 57 | default: Application Name 58 | ArtifactS3Bucket: 59 | default: CodePipeline S3 Bucket 60 | CodeCommitRepository: 61 | default: Repository Name 62 | CodeCommitBranch: 63 | default: Repository Branch 64 | 65 | Resources: 66 | 67 | CodePipeline: 68 | Type: AWS::CodePipeline::Pipeline 69 | Properties: 70 | Name: !Ref ApplicationName 71 | RoleArn: !Sub ${CodePipelineRole.Arn} 72 | ArtifactStore: 73 | Type: S3 74 | Location: !Ref ArtifactS3Bucket 75 | Stages: 76 | - 77 | Name: Source 78 | Actions: 79 | - 80 | Name: CodeCommit 81 | ActionTypeId: 82 | Category: Source 83 | Owner: AWS 84 | Version: 1 85 | Provider: CodeCommit 86 | OutputArtifacts: 87 | - Name: Source 88 | Configuration: 89 | RepositoryName: !Ref CodeCommitRepository 90 | BranchName: !Ref CodeCommitBranch 91 | - 92 | Name: Build 93 | Actions: 94 | - 95 | Name: CodeBuild 96 | InputArtifacts: 97 | - Name: Source 98 | ActionTypeId: 99 | Category: Build 100 | Owner: AWS 101 | Version: 1 102 | Provider: CodeBuild 103 | OutputArtifacts: 104 | - Name: Built 105 | Configuration: 106 | ProjectName: !Ref CodeBuild 107 | - 108 | Name: Staging 109 | Actions: 110 | - 111 | Name: DeployInfrastructure 112 | RunOrder: 1 113 | InputArtifacts: 114 | - Name: Built 115 | ActionTypeId: 116 | Category: Deploy 117 | Owner: AWS 118 | Version: 1 119 | Provider: CloudFormation 120 | Configuration: 121 | ActionMode: REPLACE_ON_FAILURE 122 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 123 | Capabilities: CAPABILITY_NAMED_IAM 124 | StackName: !Sub ${ApplicationName}-staging 125 | TemplatePath: Built::cloudformation/go-applications-pipeline-infrastructure.template 126 | TemplateConfiguration: Built::config/staging.conf 127 | ParameterOverrides: !Sub | 128 | { 129 | "ApplicationName": "${ApplicationName}", 130 | "EnvironmentName": "staging", 131 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 132 | } 133 | - 134 | Name: DeployApplication 135 | RunOrder: 2 136 | InputArtifacts: 137 | - Name: Built 138 | ActionTypeId: 139 | Category: Deploy 140 | Owner: AWS 141 | Version: 1 142 | Provider: CodeDeploy 143 | Configuration: 144 | ApplicationName: !Ref ApplicationName 145 | DeploymentGroupName: staging 146 | - 147 | Name: Approvals 148 | Actions: 149 | - 150 | Name: ProductionGate 151 | ActionTypeId: 152 | Category: Approval 153 | Owner: AWS 154 | Version: 1 155 | Provider: Manual 156 | 157 | - 158 | Name: Production 159 | Actions: 160 | - 161 | Name: DeployInfrastructure 162 | RunOrder: 1 163 | InputArtifacts: 164 | - Name: Built 165 | ActionTypeId: 166 | Category: Deploy 167 | Owner: AWS 168 | Version: 1 169 | Provider: CloudFormation 170 | Configuration: 171 | ActionMode: CREATE_UPDATE 172 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 173 | Capabilities: CAPABILITY_NAMED_IAM 174 | StackName: !Sub ${ApplicationName}-production 175 | TemplatePath: Built::cloudformation/go-applications-pipeline-infrastructure.template 176 | TemplateConfiguration: Built::config/production.conf 177 | ParameterOverrides: !Sub | 178 | { 179 | "ApplicationName": "${ApplicationName}", 180 | "EnvironmentName": "production", 181 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 182 | } 183 | - 184 | Name: DeployApplication 185 | RunOrder: 2 186 | InputArtifacts: 187 | - Name: Built 188 | ActionTypeId: 189 | Category: Deploy 190 | Owner: AWS 191 | Version: 1 192 | Provider: CodeDeploy 193 | Configuration: 194 | ApplicationName: !Ref ApplicationName 195 | DeploymentGroupName: production 196 | 197 | 198 | CodeBuild: 199 | Type: AWS::CodeBuild::Project 200 | Properties: 201 | Name: !Ref ApplicationName 202 | Description: !Sub Build project for ${ApplicationName} 203 | ServiceRole: !Ref CodeBuildRole 204 | Source: 205 | Type: CODEPIPELINE 206 | Environment: 207 | ComputeType: BUILD_GENERAL1_SMALL 208 | Image: aws/codebuild/golang:1.7.3 209 | Type: LINUX_CONTAINER 210 | EnvironmentVariables: 211 | - 212 | Name: ARTIFACT_S3_BUCKET 213 | Value: !Sub ${ArtifactS3Bucket} 214 | Artifacts: 215 | Name: !Ref ApplicationName 216 | Type: CODEPIPELINE 217 | 218 | CodePipelineRole: 219 | Type: AWS::IAM::Role 220 | Properties: 221 | Path: / 222 | RoleName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 223 | AssumeRolePolicyDocument: | 224 | { 225 | "Statement": [{ 226 | "Action": "sts:AssumeRole", 227 | "Effect": "Allow", 228 | "Principal": { 229 | "Service": "codepipeline.amazonaws.com" 230 | } 231 | }] 232 | } 233 | Policies: 234 | - 235 | PolicyName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 236 | PolicyDocument: !Sub | 237 | { 238 | "Statement": [ 239 | { 240 | "Action": [ "s3:GetBucketVersioning" ], 241 | "Resource": [ "arn:aws:s3:::${ArtifactS3Bucket}" ], 242 | "Effect": "Allow" 243 | }, 244 | { 245 | "Action": [ 246 | "s3:PutObject", 247 | "s3:GetObject", 248 | "S3:GetObjectVersion" 249 | ], 250 | "Resource": [ 251 | "arn:aws:s3:::${ArtifactS3Bucket}/*" 252 | ], 253 | "Effect": "Allow" 254 | }, 255 | { 256 | "Action": [ 257 | "codecommit:BatchGetRepositories", 258 | "codecommit:UploadArchive", 259 | "codecommit:Get*", 260 | "codecommit:List*", 261 | "codecommit:GitPull" 262 | ], 263 | "Resource": "*", 264 | "Effect": "Allow" 265 | }, 266 | { 267 | "Action": [ 268 | "codedeploy:CreateDeployment", 269 | "codedeploy:GetApplicationRevision", 270 | "codedeploy:GetDeployment", 271 | "codedeploy:GetDeploymentConfig", 272 | "codedeploy:RegisterApplicationRevision" 273 | ], 274 | "Resource": "*", 275 | "Effect": "Allow" 276 | }, 277 | { 278 | "Action": [ 279 | "cloudformation:CreateStack", 280 | "cloudformation:DeleteStack", 281 | "cloudformation:DescribeStacks", 282 | "cloudformation:UpdateStack", 283 | "cloudformation:CreateChangeSet", 284 | "cloudformation:DeleteChangeSet", 285 | "cloudformation:DescribeChangeSet", 286 | "cloudformation:ExecuteChangeSet", 287 | "cloudformation:SetStackPolicy", 288 | "cloudformation:ValidateTemplate", 289 | "iam:PassRole" 290 | ], 291 | "Resource": [ 292 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-CloudFormation-${AWS::Region}", 293 | "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${ApplicationName}-staging/*", 294 | "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${ApplicationName}-production/*" 295 | ], 296 | "Effect": "Allow" 297 | }, 298 | { 299 | "Action": [ 300 | "codebuild:BatchGetBuilds", 301 | "codebuild:StartBuild" 302 | ], 303 | "Resource": "*", 304 | "Effect": "Allow" 305 | } 306 | ] 307 | } 308 | 309 | CodePipelineCloudFormationRole: 310 | Type: AWS::IAM::Role 311 | Properties: 312 | Path: / 313 | RoleName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 314 | AssumeRolePolicyDocument: | 315 | { 316 | "Statement": [{ 317 | "Action": "sts:AssumeRole", 318 | "Effect": "Allow", 319 | "Principal": { 320 | "Service": "cloudformation.amazonaws.com" 321 | } 322 | }] 323 | } 324 | Policies: 325 | - 326 | PolicyName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 327 | PolicyDocument: !Sub | 328 | { 329 | "Statement": [{ 330 | "Effect": "Allow", 331 | "Action": [ 332 | "ec2:AssociateRouteTable", 333 | "ec2:AttachInternetGateway", 334 | "ec2:AttachNetworkInterface", 335 | "ec2:CreateInternetGateway", 336 | "ec2:CreateNatGateway", 337 | "ec2:CreateNetworkInterface", 338 | "ec2:CreateRoute", 339 | "ec2:CreateRouteTable", 340 | "ec2:CreateSubnet", 341 | "ec2:CreateVolume", 342 | "ec2:CreateVpc", 343 | "ec2:createTags", 344 | "ec2:CreateSecurityGroup", 345 | "ec2:DeleteInternetGateway", 346 | "ec2:DeleteNatGateway", 347 | "ec2:DeleteNetworkInterface", 348 | "ec2:DeleteRoute", 349 | "ec2:DeleteRouteTable", 350 | "ec2:DeleteSecurityGroup", 351 | "ec2:DeleteSubnet", 352 | "ec2:DeleteVolume", 353 | "ec2:DeleteVpc", 354 | "ec2:DeleteTags", 355 | "ec2:DescribeInternetGateways", 356 | "ec2:DescribeVpcs", 357 | "ec2:DescribeRouteTables", 358 | "ec2:DetachInternetGateway", 359 | "ec2:ModifyVpcAttribute", 360 | "ec2:DescribeAvailabilityZones", 361 | "ec2:DescribeAccountAttributes", 362 | "ec2:DescribeSubnets", 363 | "ec2:DescribeSecurityGroups", 364 | "ec2:AuthorizeSecurityGroupIngress", 365 | "ec2:ModifySubnetAttribute", 366 | "ec2:allocateAddress", 367 | "ec2:DisassociateRouteTable", 368 | "ec2:describeAddresses", 369 | "ec2:DescribeNatGateways", 370 | "ec2:releaseAddress", 371 | "ec2:DescribeInstances", 372 | "ec2:RevokeSecurityGroupIngress", 373 | "elasticloadbalancing:CreateLoadBalancer", 374 | "elasticloadbalancing:CreateLoadBalancerListeners", 375 | "elasticloadbalancing:CreateRule", 376 | "elasticloadbalancing:CreateTargetGroup", 377 | "elasticloadbalancing:DeleteLoadBalancer", 378 | "elasticloadbalancing:DeleteLoadBalancerListeners", 379 | "elasticloadbalancing:DeleteRule", 380 | "elasticloadbalancing:DeleteTargetGroup", 381 | "elasticloadbalancing:SetSubnets", 382 | "elasticloadbalancing:DescribeTargetGroups", 383 | "elasticloadbalancingv2:DescribeTargetGroups", 384 | "elasticloadbalancing:DescribeLoadBalancers", 385 | "elasticloadbalancingv2:DescribeLoadBalancers", 386 | "elasticloadbalancing:CreateListener", 387 | "elasticloadbalancing:DescribeListeners", 388 | "elasticloadbalancing:DeleteListener", 389 | "autoscaling:CreateAutoScalingGroup", 390 | "autoscaling:CreateLaunchConfiguration", 391 | "autoscaling:DeleteAutoScalingGroup", 392 | "autoscaling:DeleteLaunchConfiguration", 393 | "autoscaling:DescribeAutoScalingGroup", 394 | "autoscaling:DescribeAutoScalingGroups", 395 | "autoscaling:DescribeScalingActivities", 396 | "autoscaling:DescribeLaunchConfigurations", 397 | "autoscaling:UpdateAutoScalingGroup", 398 | "autoscaling:DescribeAutoScalingInstances", 399 | "codedeploy:GetDeploymentGroup", 400 | "codedeploy:CreateDeploymentGroup", 401 | "codedeploy:DeleteDeploymentGroup" 402 | ], 403 | "Resource": "*" 404 | }, 405 | { 406 | "Effect": "Allow", 407 | "Action": [ 408 | "iam:GetRole", 409 | "iam:CreateRole", 410 | "iam:PutRolePolicy", 411 | "iam:DeleteRolePolicy", 412 | "iam:DeleteRole", 413 | "iam:CreateInstanceProfile", 414 | "iam:AddRoleToInstanceProfile", 415 | "iam:RemoveRoleFromInstanceProfile", 416 | "iam:PassRole", 417 | "iam:DeleteInstanceProfile" 418 | ], 419 | "Resource": [ 420 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-staging-${AWS::Region}", 421 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-production-${AWS::Region}", 422 | "arn:aws:iam::${AWS::AccountId}:instance-profile/gotest-staging-InstanceProfile-*", 423 | "arn:aws:iam::${AWS::AccountId}:instance-profile/gotest-production-InstanceProfile-*", 424 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-CodeDeploy-${AWS::Region}" 425 | ] 426 | } 427 | ] 428 | } 429 | 430 | CodeDeployApplication: 431 | Type: AWS::CodeDeploy::Application 432 | Properties: 433 | ApplicationName: !Sub ${ApplicationName} 434 | 435 | CodeDeployServiceRole: 436 | Type: AWS::IAM::Role 437 | Properties: 438 | Path: / 439 | RoleName: !Sub ${ApplicationName}-CodeDeploy-${AWS::Region} 440 | AssumeRolePolicyDocument: | 441 | { 442 | "Statement": [{ 443 | "Action": "sts:AssumeRole", 444 | "Effect": "Allow", 445 | "Principal": { 446 | "Service": "codedeploy.amazonaws.com" 447 | } 448 | }] 449 | } 450 | Policies: 451 | - PolicyName: !Sub ${AWS::StackName}-${AWS::Region} 452 | PolicyDocument: !Sub | 453 | { 454 | "Version": "2012-10-17", 455 | "Statement": [ 456 | { 457 | "Effect": "Allow", 458 | "Action": [ 459 | "autoscaling:CompleteLifecycleAction", 460 | "autoscaling:DeleteLifecycleHook", 461 | "autoscaling:DescribeAutoScalingGroups", 462 | "autoscaling:DescribeLifecycleHooks", 463 | "autoscaling:PutLifecycleHook", 464 | "autoscaling:RecordLifecycleActionHeartbeat", 465 | "ec2:DescribeInstances", 466 | "ec2:DescribeInstanceStatus", 467 | "tag:GetTags", 468 | "tag:GetResources", 469 | "sns:Publish", 470 | "cloudwatch:DescribeAlarms" 471 | ], 472 | "Resource": "*" 473 | } 474 | ] 475 | } 476 | 477 | CodeBuildRole: 478 | Type: AWS::IAM::Role 479 | Properties: 480 | Path: / 481 | RoleName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 482 | AssumeRolePolicyDocument: | 483 | { 484 | "Statement": [{ 485 | "Action": "sts:AssumeRole", 486 | "Effect": "Allow", 487 | "Principal": { 488 | "Service": "codebuild.amazonaws.com" 489 | } 490 | }] 491 | } 492 | Policies: 493 | - 494 | PolicyName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 495 | PolicyDocument: !Sub | 496 | { 497 | "Statement": [ 498 | { 499 | "Effect": "Allow", 500 | "Resource": [ "*" ], 501 | "Action": [ 502 | "logs:CreateLogGroup", 503 | "logs:CreateLogStream", 504 | "logs:PutLogEvents" 505 | ] 506 | }, 507 | { 508 | "Effect": "Allow", 509 | "Resource": [ 510 | "arn:aws:s3:::${ArtifactS3Bucket}/*" 511 | ], 512 | "Action": [ 513 | "s3:GetObject", 514 | "s3:GetObjectVersion", 515 | "s3:PutObject" 516 | ] 517 | } 518 | ] 519 | } 520 | 521 | SolutionHelperRole: 522 | Type: AWS::IAM::Role 523 | Properties: 524 | AssumeRolePolicyDocument: 525 | Version: '2012-10-17' 526 | Statement: 527 | - Effect: Allow 528 | Principal: 529 | Service: lambda.amazonaws.com 530 | Action: sts:AssumeRole 531 | Path: / 532 | Policies: 533 | - PolicyName: Solution_Helper_Permissions 534 | PolicyDocument: 535 | Version: '2012-10-17' 536 | Statement: 537 | - Effect: Allow 538 | Action: 539 | - logs:CreateLogGroup 540 | - logs:CreateLogStream 541 | - logs:PutLogEvents 542 | Resource: !Join ['', ['arn:aws:logs:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', 543 | ':log-group:/aws/lambda/*']] 544 | SolutionHelper: 545 | Type: AWS::Lambda::Function 546 | Properties: 547 | Handler: solution-helper.lambda_handler 548 | Role: !GetAtt [SolutionHelperRole, Arn] 549 | Description: This function creates a CloudFormation custom lambda resource that 550 | creates custom lambda functions by finding and replacing specific values from 551 | existing lambda function code. 552 | Code: 553 | S3Bucket: !Join ['', [REPLACE_BUCKET_NAME-, !Ref 'AWS::Region']] 554 | S3Key: library/solution-helper/stable/solution-helper-test.zip 555 | Runtime: python2.7 556 | Timeout: '300' 557 | CreateUniqueID: 558 | Type: Custom::LoadLambda 559 | Properties: 560 | ServiceToken: !GetAtt [SolutionHelper, Arn] 561 | Region: !Ref 'AWS::Region' 562 | CreateUniqueID: 'true' 563 | SendingAnonymousData: 564 | Type: Custom::LoadLambda 565 | Properties: 566 | ServiceToken: !GetAtt [SolutionHelper, Arn] 567 | SendAnonymousData: !Join ['', ['{ ''Solution'' : ''', SO0024, ''', ', '''UUID'' 568 | : ''', !GetAtt [CreateUniqueID, UUID], ''', ', '''Data'': {', '''CodeRepo'':',' ''CodeCommit'',', 569 | '''SendAnonymousData'': ''', !FindInMap [Send, AnonymousUsage, Data], '''', 570 | '}', '}']] 571 | 572 | Outputs: 573 | 574 | CodePipelineURL: 575 | Description: The URL for the created pipeline 576 | Value: !Sub https://${AWS::Region}.console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${ApplicationName} 577 | 578 | CodeDeployApplication: 579 | Description: The CodeDeploy application used across all environments 580 | Value: !Ref CodeDeployApplication 581 | Export: 582 | Name: !Sub CodeDeployApplication-${ApplicationName} 583 | 584 | CodeDeployServiceRoleArn: 585 | Description: The CodeDeploy service role used across all environments 586 | Value: !GetAtt CodeDeployServiceRole.Arn 587 | Export: 588 | Name: !Sub CodeDeployServiceRoleArn-${ApplicationName} 589 | 590 | 591 | -------------------------------------------------------------------------------- /cloudformation/go-applications-pipeline-github.template: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | (SO0024) - Deployment Pipeline for Go Applications on AWS - AWS CloudFormation Template for 4 | AWS Solutions Builder Deployment Pipeline for Go Applications on AWS - **WARNING** This template 5 | creates AWS resources. You will be billed for the AWS resources used if you create 6 | a stack from this template. 7 | This CloudFormation template will deploy a full CI/CD pipeline for Go 8 | development. It includes building with AWS CodeBuild, and infrastructure 9 | deployment via AWS CloudFormation. It will create an AWS CodePipeline for 10 | orchastrating builds, testing and deployments to beta and production 11 | environments. 12 | 13 | Parameters: 14 | 15 | ApplicationName: 16 | Description: This will be used to name the pipeline and build resources 17 | Type: String 18 | AllowedPattern: '[A-Za-z0-9-]+' 19 | 20 | ArtifactS3Bucket: 21 | Description: An existing S3 bucket within this AWS account 22 | Type: String 23 | 24 | GitHubOAuthToken: 25 | Description: Create a token with 'repo' and 'admin:repo_hook' permissions here https://github.com/settings/tokens 26 | Type: String 27 | 28 | GitHubUser: 29 | Description: Enter GitHub username of the repository owner 30 | Type: String 31 | 32 | GitHubRepository: 33 | Description: Enter the repository name that should be monitored for changes 34 | Type: String 35 | 36 | GitHubBranch: 37 | Description: Enter the GitHub branch to monitored 38 | Type: String 39 | Default: master 40 | 41 | Mappings: 42 | Send: 43 | AnonymousUsage: 44 | Data: Yes 45 | 46 | 47 | Metadata: 48 | 49 | AWS::CloudFormation::Interface: 50 | ParameterGroups: 51 | - 52 | Label: 53 | default: Application Configuration 54 | Parameters: 55 | - ApplicationName 56 | - ArtifactS3Bucket 57 | - 58 | Label: 59 | default: GitHub Configuration 60 | Parameters: 61 | - GitHubOAuthToken 62 | - GitHubUser 63 | - GitHubRepository 64 | - GitHubBranch 65 | 66 | ParameterLabels: 67 | ApplicationName: 68 | default: Application Name 69 | ArtifactS3Bucket: 70 | default: CodePipeline S3 Bucket 71 | GitHubRepository: 72 | default: Repository Name 73 | GitHubUser: 74 | default: Repository Owner 75 | GitHubBranch: 76 | default: Repository Branch 77 | GitHubOAuthToken: 78 | default: OAuth2 Token 79 | 80 | Resources: 81 | 82 | CodePipeline: 83 | Type: AWS::CodePipeline::Pipeline 84 | Properties: 85 | Name: !Ref ApplicationName 86 | RoleArn: !Sub ${CodePipelineRole.Arn} 87 | ArtifactStore: 88 | Type: S3 89 | Location: !Ref ArtifactS3Bucket 90 | Stages: 91 | - 92 | Name: Source 93 | Actions: 94 | - 95 | Name: GitHub 96 | ActionTypeId: 97 | Category: Source 98 | Owner: ThirdParty 99 | Version: 1 100 | Provider: GitHub 101 | OutputArtifacts: 102 | - Name: Source 103 | Configuration: 104 | Owner: !Ref GitHubUser 105 | Repo: !Ref GitHubRepository 106 | Branch: !Ref GitHubBranch 107 | OAuthToken: !Ref GitHubOAuthToken 108 | - 109 | Name: Build 110 | Actions: 111 | - 112 | Name: CodeBuild 113 | InputArtifacts: 114 | - Name: Source 115 | ActionTypeId: 116 | Category: Build 117 | Owner: AWS 118 | Version: 1 119 | Provider: CodeBuild 120 | OutputArtifacts: 121 | - Name: Built 122 | Configuration: 123 | ProjectName: !Ref CodeBuild 124 | - 125 | Name: Staging 126 | Actions: 127 | - 128 | Name: DeployInfrastructure 129 | RunOrder: 1 130 | InputArtifacts: 131 | - Name: Built 132 | ActionTypeId: 133 | Category: Deploy 134 | Owner: AWS 135 | Version: 1 136 | Provider: CloudFormation 137 | Configuration: 138 | ActionMode: REPLACE_ON_FAILURE 139 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 140 | Capabilities: CAPABILITY_NAMED_IAM 141 | StackName: !Sub ${ApplicationName}-staging 142 | TemplatePath: Built::cloudformation/go-applications-pipeline-infrastructure.template 143 | TemplateConfiguration: Built::config/staging.conf 144 | ParameterOverrides: !Sub | 145 | { 146 | "ApplicationName": "${ApplicationName}", 147 | "EnvironmentName": "staging", 148 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 149 | } 150 | - 151 | Name: DeployApplication 152 | RunOrder: 2 153 | InputArtifacts: 154 | - Name: Built 155 | ActionTypeId: 156 | Category: Deploy 157 | Owner: AWS 158 | Version: 1 159 | Provider: CodeDeploy 160 | Configuration: 161 | ApplicationName: !Ref ApplicationName 162 | DeploymentGroupName: staging 163 | - 164 | Name: Approvals 165 | Actions: 166 | - 167 | Name: ProductionGate 168 | ActionTypeId: 169 | Category: Approval 170 | Owner: AWS 171 | Version: 1 172 | Provider: Manual 173 | 174 | - 175 | Name: Production 176 | Actions: 177 | - 178 | Name: DeployInfrastructure 179 | RunOrder: 1 180 | InputArtifacts: 181 | - Name: Built 182 | ActionTypeId: 183 | Category: Deploy 184 | Owner: AWS 185 | Version: 1 186 | Provider: CloudFormation 187 | Configuration: 188 | ActionMode: CREATE_UPDATE 189 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 190 | Capabilities: CAPABILITY_NAMED_IAM 191 | StackName: !Sub ${ApplicationName}-production 192 | TemplatePath: Built::cloudformation/go-applications-pipeline-infrastructure.template 193 | TemplateConfiguration: Built::config/production.conf 194 | ParameterOverrides: !Sub | 195 | { 196 | "ApplicationName": "${ApplicationName}", 197 | "EnvironmentName": "production", 198 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 199 | } 200 | - 201 | Name: DeployApplication 202 | RunOrder: 2 203 | InputArtifacts: 204 | - Name: Built 205 | ActionTypeId: 206 | Category: Deploy 207 | Owner: AWS 208 | Version: 1 209 | Provider: CodeDeploy 210 | Configuration: 211 | ApplicationName: !Ref ApplicationName 212 | DeploymentGroupName: production 213 | 214 | 215 | CodeBuild: 216 | Type: AWS::CodeBuild::Project 217 | Properties: 218 | Name: !Ref ApplicationName 219 | Description: !Sub Build project for ${ApplicationName} 220 | ServiceRole: !Ref CodeBuildRole 221 | Source: 222 | Type: CODEPIPELINE 223 | Environment: 224 | ComputeType: BUILD_GENERAL1_SMALL 225 | Image: aws/codebuild/golang:1.7.3 226 | Type: LINUX_CONTAINER 227 | EnvironmentVariables: 228 | - 229 | Name: ARTIFACT_S3_BUCKET 230 | Value: !Sub ${ArtifactS3Bucket} 231 | Artifacts: 232 | Name: !Ref ApplicationName 233 | Type: CODEPIPELINE 234 | 235 | CodePipelineRole: 236 | Type: AWS::IAM::Role 237 | Properties: 238 | Path: / 239 | RoleName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 240 | AssumeRolePolicyDocument: | 241 | { 242 | "Statement": [{ 243 | "Action": "sts:AssumeRole", 244 | "Effect": "Allow", 245 | "Principal": { 246 | "Service": "codepipeline.amazonaws.com" 247 | } 248 | }] 249 | } 250 | Policies: 251 | - 252 | PolicyName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 253 | PolicyDocument: !Sub | 254 | { 255 | "Statement": [ 256 | { 257 | "Action": [ "s3:GetBucketVersioning" ], 258 | "Resource": [ "arn:aws:s3:::${ArtifactS3Bucket}" ], 259 | "Effect": "Allow" 260 | }, 261 | { 262 | "Action": [ 263 | "s3:PutObject", 264 | "s3:GetObject", 265 | "S3:GetObjectVersion" 266 | ], 267 | "Resource": [ 268 | "arn:aws:s3:::${ArtifactS3Bucket}/*" 269 | ], 270 | "Effect": "Allow" 271 | }, 272 | { 273 | "Action": [ 274 | "codedeploy:CreateDeployment", 275 | "codedeploy:GetApplicationRevision", 276 | "codedeploy:GetDeployment", 277 | "codedeploy:GetDeploymentConfig", 278 | "codedeploy:RegisterApplicationRevision" 279 | ], 280 | "Resource": "*", 281 | "Effect": "Allow" 282 | }, 283 | { 284 | "Action": [ 285 | "cloudformation:CreateStack", 286 | "cloudformation:DeleteStack", 287 | "cloudformation:DescribeStacks", 288 | "cloudformation:UpdateStack", 289 | "cloudformation:CreateChangeSet", 290 | "cloudformation:DeleteChangeSet", 291 | "cloudformation:DescribeChangeSet", 292 | "cloudformation:ExecuteChangeSet", 293 | "cloudformation:SetStackPolicy", 294 | "cloudformation:ValidateTemplate", 295 | "iam:PassRole" 296 | ], 297 | "Resource": [ 298 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-CloudFormation-${AWS::Region}", 299 | "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${ApplicationName}-staging/*", 300 | "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${ApplicationName}-production/*" 301 | ], 302 | "Effect": "Allow" 303 | }, 304 | { 305 | "Action": [ 306 | "codebuild:BatchGetBuilds", 307 | "codebuild:StartBuild" 308 | ], 309 | "Resource": "*", 310 | "Effect": "Allow" 311 | } 312 | ] 313 | } 314 | 315 | CodePipelineCloudFormationRole: 316 | Type: AWS::IAM::Role 317 | Properties: 318 | Path: / 319 | RoleName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 320 | AssumeRolePolicyDocument: | 321 | { 322 | "Statement": [{ 323 | "Action": "sts:AssumeRole", 324 | "Effect": "Allow", 325 | "Principal": { 326 | "Service": "cloudformation.amazonaws.com" 327 | } 328 | }] 329 | } 330 | Policies: 331 | - 332 | PolicyName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 333 | PolicyDocument: !Sub | 334 | { 335 | "Statement": [{ 336 | "Effect": "Allow", 337 | "Action": [ 338 | "ec2:AssociateRouteTable", 339 | "ec2:AttachInternetGateway", 340 | "ec2:AttachNetworkInterface", 341 | "ec2:CreateInternetGateway", 342 | "ec2:CreateNatGateway", 343 | "ec2:CreateNetworkInterface", 344 | "ec2:CreateRoute", 345 | "ec2:CreateRouteTable", 346 | "ec2:CreateSubnet", 347 | "ec2:CreateVolume", 348 | "ec2:CreateVpc", 349 | "ec2:createTags", 350 | "ec2:CreateSecurityGroup", 351 | "ec2:DeleteInternetGateway", 352 | "ec2:DeleteNatGateway", 353 | "ec2:DeleteNetworkInterface", 354 | "ec2:DeleteRoute", 355 | "ec2:DeleteRouteTable", 356 | "ec2:DeleteSecurityGroup", 357 | "ec2:DeleteSubnet", 358 | "ec2:DeleteVolume", 359 | "ec2:DeleteVpc", 360 | "ec2:DeleteTags", 361 | "ec2:DescribeInternetGateways", 362 | "ec2:DescribeVpcs", 363 | "ec2:DescribeRouteTables", 364 | "ec2:DetachInternetGateway", 365 | "ec2:ModifyVpcAttribute", 366 | "ec2:DescribeAvailabilityZones", 367 | "ec2:DescribeAccountAttributes", 368 | "ec2:DescribeSubnets", 369 | "ec2:DescribeSecurityGroups", 370 | "ec2:AuthorizeSecurityGroupIngress", 371 | "ec2:ModifySubnetAttribute", 372 | "ec2:allocateAddress", 373 | "ec2:DisassociateRouteTable", 374 | "ec2:describeAddresses", 375 | "ec2:DescribeNatGateways", 376 | "ec2:releaseAddress", 377 | "ec2:DescribeInstances", 378 | "ec2:RevokeSecurityGroupIngress", 379 | "elasticloadbalancing:CreateLoadBalancer", 380 | "elasticloadbalancing:CreateLoadBalancerListeners", 381 | "elasticloadbalancing:CreateRule", 382 | "elasticloadbalancing:CreateTargetGroup", 383 | "elasticloadbalancing:DeleteLoadBalancer", 384 | "elasticloadbalancing:DeleteLoadBalancerListeners", 385 | "elasticloadbalancing:DeleteRule", 386 | "elasticloadbalancing:DeleteTargetGroup", 387 | "elasticloadbalancing:SetSubnets", 388 | "elasticloadbalancing:DescribeTargetGroups", 389 | "elasticloadbalancingv2:DescribeTargetGroups", 390 | "elasticloadbalancing:DescribeLoadBalancers", 391 | "elasticloadbalancingv2:DescribeLoadBalancers", 392 | "elasticloadbalancing:CreateListener", 393 | "elasticloadbalancing:DescribeListeners", 394 | "elasticloadbalancing:DeleteListener", 395 | "autoscaling:CreateAutoScalingGroup", 396 | "autoscaling:CreateLaunchConfiguration", 397 | "autoscaling:DeleteAutoScalingGroup", 398 | "autoscaling:DeleteLaunchConfiguration", 399 | "autoscaling:DescribeAutoScalingGroup", 400 | "autoscaling:DescribeAutoScalingGroups", 401 | "autoscaling:DescribeScalingActivities", 402 | "autoscaling:DescribeLaunchConfigurations", 403 | "autoscaling:UpdateAutoScalingGroup", 404 | "autoscaling:DescribeAutoScalingInstances", 405 | "codedeploy:GetDeploymentGroup", 406 | "codedeploy:CreateDeploymentGroup", 407 | "codedeploy:DeleteDeploymentGroup" 408 | ], 409 | "Resource": "*" 410 | }, 411 | { 412 | "Effect": "Allow", 413 | "Action": [ 414 | "iam:GetRole", 415 | "iam:CreateRole", 416 | "iam:PutRolePolicy", 417 | "iam:DeleteRolePolicy", 418 | "iam:DeleteRole", 419 | "iam:CreateInstanceProfile", 420 | "iam:AddRoleToInstanceProfile", 421 | "iam:RemoveRoleFromInstanceProfile", 422 | "iam:PassRole", 423 | "iam:DeleteInstanceProfile" 424 | ], 425 | "Resource": [ 426 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-staging-${AWS::Region}", 427 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-production-${AWS::Region}", 428 | "arn:aws:iam::${AWS::AccountId}:instance-profile/gotest-staging-InstanceProfile-*", 429 | "arn:aws:iam::${AWS::AccountId}:instance-profile/gotest-production-InstanceProfile-*", 430 | "arn:aws:iam::${AWS::AccountId}:role/${ApplicationName}-CodeDeploy-${AWS::Region}" 431 | ] 432 | } 433 | ] 434 | } 435 | 436 | CodeDeployApplication: 437 | Type: AWS::CodeDeploy::Application 438 | Properties: 439 | ApplicationName: !Sub ${ApplicationName} 440 | 441 | CodeDeployServiceRole: 442 | Type: AWS::IAM::Role 443 | Properties: 444 | Path: / 445 | RoleName: !Sub ${ApplicationName}-CodeDeploy-${AWS::Region} 446 | AssumeRolePolicyDocument: | 447 | { 448 | "Statement": [{ 449 | "Action": "sts:AssumeRole", 450 | "Effect": "Allow", 451 | "Principal": { 452 | "Service": "codedeploy.amazonaws.com" 453 | } 454 | }] 455 | } 456 | Policies: 457 | - PolicyName: !Sub ${AWS::StackName}-${AWS::Region} 458 | PolicyDocument: !Sub | 459 | { 460 | "Version": "2012-10-17", 461 | "Statement": [ 462 | { 463 | "Effect": "Allow", 464 | "Action": [ 465 | "autoscaling:CompleteLifecycleAction", 466 | "autoscaling:DeleteLifecycleHook", 467 | "autoscaling:DescribeAutoScalingGroups", 468 | "autoscaling:DescribeLifecycleHooks", 469 | "autoscaling:PutLifecycleHook", 470 | "autoscaling:RecordLifecycleActionHeartbeat", 471 | "ec2:DescribeInstances", 472 | "ec2:DescribeInstanceStatus", 473 | "tag:GetTags", 474 | "tag:GetResources", 475 | "sns:Publish", 476 | "cloudwatch:DescribeAlarms" 477 | ], 478 | "Resource": "*" 479 | } 480 | ] 481 | } 482 | 483 | CodeBuildRole: 484 | Type: AWS::IAM::Role 485 | Properties: 486 | Path: / 487 | RoleName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 488 | AssumeRolePolicyDocument: | 489 | { 490 | "Statement": [{ 491 | "Action": "sts:AssumeRole", 492 | "Effect": "Allow", 493 | "Principal": { 494 | "Service": "codebuild.amazonaws.com" 495 | } 496 | }] 497 | } 498 | Policies: 499 | - 500 | PolicyName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 501 | PolicyDocument: !Sub | 502 | { 503 | "Statement": [ 504 | { 505 | "Effect": "Allow", 506 | "Resource": [ "*" ], 507 | "Action": [ 508 | "logs:CreateLogGroup", 509 | "logs:CreateLogStream", 510 | "logs:PutLogEvents" 511 | ] 512 | }, 513 | { 514 | "Effect": "Allow", 515 | "Resource": [ 516 | "arn:aws:s3:::${ArtifactS3Bucket}/*" 517 | ], 518 | "Action": [ 519 | "s3:GetObject", 520 | "s3:GetObjectVersion", 521 | "s3:PutObject" 522 | ] 523 | } 524 | ] 525 | } 526 | 527 | SolutionHelperRole: 528 | Type: AWS::IAM::Role 529 | Properties: 530 | AssumeRolePolicyDocument: 531 | Version: '2012-10-17' 532 | Statement: 533 | - Effect: Allow 534 | Principal: 535 | Service: lambda.amazonaws.com 536 | Action: sts:AssumeRole 537 | Path: / 538 | Policies: 539 | - PolicyName: Solution_Helper_Permissions 540 | PolicyDocument: 541 | Version: '2012-10-17' 542 | Statement: 543 | - Effect: Allow 544 | Action: 545 | - logs:CreateLogGroup 546 | - logs:CreateLogStream 547 | - logs:PutLogEvents 548 | Resource: !Join ['', ['arn:aws:logs:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', 549 | ':log-group:/aws/lambda/*']] 550 | SolutionHelper: 551 | Type: AWS::Lambda::Function 552 | Properties: 553 | Handler: solution-helper.lambda_handler 554 | Role: !GetAtt [SolutionHelperRole, Arn] 555 | Description: This function creates a CloudFormation custom lambda resource that 556 | creates custom lambda functions by finding and replacing specific values from 557 | existing lambda function code. 558 | Code: 559 | S3Bucket: !Join ['', [REPLACE_BUCKET_NAME-, !Ref 'AWS::Region']] 560 | S3Key: library/solution-helper/stable/solution-helper-test.zip 561 | Runtime: 'python2.7' 562 | Timeout: '300' 563 | CreateUniqueID: 564 | Type: Custom::LoadLambda 565 | Properties: 566 | ServiceToken: !GetAtt [SolutionHelper, Arn] 567 | Region: !Ref 'AWS::Region' 568 | CreateUniqueID: 'true' 569 | SendingAnonymousData: 570 | Type: Custom::LoadLambda 571 | Properties: 572 | ServiceToken: !GetAtt [SolutionHelper, Arn] 573 | SendAnonymousData: !Join ['', ['{ ''Solution'' : ''', SO0024, ''', ', '''UUID'' 574 | : ''', !GetAtt [CreateUniqueID, UUID], ''', ', '''Data'': {', '''CodeRepo'':',' ''GitHub'',', 575 | '''SendAnonymousData'': ''', !FindInMap [Send, AnonymousUsage, Data], '''', 576 | '}', '}']] 577 | 578 | Outputs: 579 | 580 | CodePipelineURL: 581 | Description: The URL for the created pipeline 582 | Value: !Sub https://${AWS::Region}.console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${ApplicationName} 583 | 584 | CodeDeployApplication: 585 | Description: The CodeDeploy application used across all environments 586 | Value: !Ref CodeDeployApplication 587 | Export: 588 | Name: !Sub CodeDeployApplication-${ApplicationName} 589 | 590 | CodeDeployServiceRoleArn: 591 | Description: The CodeDeploy service role used across all environments 592 | Value: !GetAtt CodeDeployServiceRole.Arn 593 | Export: 594 | Name: !Sub CodeDeployServiceRoleArn-${ApplicationName} 595 | 596 | 597 | -------------------------------------------------------------------------------- /cloudformation/go-applications-pipeline-infrastructure.template: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | (SO0024i) - Infrastructure - Deployment Pipeline for Go Applications on AWS - AWS CloudFormation Template for 4 | AWS Solutions Builder Deployment Pipeline for Go Applications on AWS - **WARNING** This template 5 | creates AWS resources. You will be billed for the AWS resources used if you create 6 | a stack from this template. 7 | Example Infrastructure for a Go Application. 8 | This template will deploy a highly available Auto Scaling Group of EC2 instances with the CodeDeploy 9 | agent installed ready for application deployments. It will also deploy a VPC with public and private 10 | subnets. An Application Load Balancer is placed in the public subnets, which forwards traffic to the 11 | application servers in the private subnets. NAT Gateways are deployed in each AZ to facilitate outbound 12 | traffic from the application servers to the internet. 13 | 14 | Parameters: 15 | 16 | # The following parameters are set dynamically by AWS CodePipeline 17 | 18 | ApplicationName: 19 | Type: String 20 | 21 | EnvironmentName: 22 | Type: String 23 | 24 | ArtifactS3Bucket: 25 | Type: String 26 | 27 | # All parameters below can be overridden on a per environment basis in config/* 28 | 29 | InstanceType: 30 | Description: The instance type to deploy to 31 | Type: String 32 | Default: t2.micro 33 | 34 | InstanceCount: 35 | Description: How many instances should the AutoScaling Group contain 36 | Type: Number 37 | Default: 2 38 | 39 | DeploymentStrategy: 40 | Description: The CodeDeploy deployment strategy to user 41 | Type: String 42 | Default: CodeDeployDefault.OneAtATime 43 | 44 | VpcCIDR: 45 | Description: Please enter the IP range (CIDR notation) for this VPC 46 | Type: String 47 | Default: 10.192.0.0/16 48 | 49 | PublicSubnet1CIDR: 50 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 51 | Type: String 52 | Default: 10.192.10.0/24 53 | 54 | PublicSubnet2CIDR: 55 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 56 | Type: String 57 | Default: 10.192.11.0/24 58 | 59 | PrivateSubnet1CIDR: 60 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 61 | Type: String 62 | Default: 10.192.20.0/24 63 | 64 | PrivateSubnet2CIDR: 65 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 66 | Type: String 67 | Default: 10.192.21.0/24 68 | 69 | Mappings: 70 | 71 | # Amazon Linux AMI 2016.09.1, released on 2016-12-22 72 | # https://aws.amazon.com/amazon-linux-ami/ 73 | 74 | AWSRegionToAMI: 75 | us-east-1: 76 | AMI: ami-9be6f38c 77 | us-east-2: 78 | AMI: ami-38cd975d 79 | us-west-1: 80 | AMI: ami-1e299d7e 81 | us-west-2: 82 | AMI: ami-b73d6cd7 83 | ca-central-1: 84 | AMI: ami-eb20928f 85 | eu-west-1: 86 | AMI: ami-c51e3eb6 87 | eu-west-2: 88 | AMI: ami-bfe0eadb 89 | eu-central-1: 90 | AMI: ami-211ada4e 91 | ap-southeast-1: 92 | AMI: ami-4dd6782e 93 | ap-northeast-2: 94 | AMI: ami-94bb6dfa 95 | ap-northeast-1: 96 | AMI: ami-9f0c67f8 97 | ap-southeast-2: 98 | AMI: ami-28cff44b 99 | ap-south-1: 100 | AMI: ami-9fc7b0f0 101 | sa-east-1: 102 | AMI: ami-bb40d8d7 103 | 104 | # CodeDeploy installer bucket list sourced from: 105 | # http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-set-up-new-instance.html 106 | 107 | CodeDeployInstallerBuckets: 108 | us-east-1: 109 | Bucket: aws-codedeploy-us-east-1 110 | us-east-2: 111 | Bucket: aws-codedeploy-us-east-2 112 | us-west-1: 113 | Bucket: aws-codedeploy-us-west-1 114 | us-west-2: 115 | Bucket: aws-codedeploy-us-west-2 116 | ca-central-1: 117 | Bucket: aws-codedeploy-ca-central-1 118 | eu-west-1: 119 | Bucket: aws-codedeploy-eu-west-1 120 | eu-west-2: 121 | Bucket: aws-codedeploy-eu-west-2 122 | eu-central-1: 123 | Bucket: aws-codedeploy-eu-central-1 124 | ap-northeast-1: 125 | Bucket: aws-codedeploy-ap-northeast-1 126 | ap-northeast-2: 127 | Bucket: aws-codedeploy-ap-northeast-2 128 | ap-southeast-1: 129 | Bucket: aws-codedeploy-ap-southeast-1 130 | ap-southeast-2: 131 | Bucket: aws-codedeploy-ap-southeast-2 132 | ap-south-1: 133 | Bucket: aws-codedeploy-ap-south-1 134 | sa-east-1: 135 | Bucket: aws-codedeploy-sa-east-1 136 | 137 | 138 | Resources: 139 | 140 | ########################################### 141 | # AutoScaling Group 142 | ########################################### 143 | 144 | AutoScalingGroup: 145 | Type: AWS::AutoScaling::AutoScalingGroup 146 | Properties: 147 | VPCZoneIdentifier: 148 | - !Ref PrivateSubnet1 149 | - !Ref PrivateSubnet2 150 | LaunchConfigurationName: !Ref LaunchConfiguration 151 | TargetGroupARNs: 152 | - !Ref TargetGroup 153 | MinSize: !Ref InstanceCount 154 | MaxSize: !Ref InstanceCount 155 | DesiredCapacity: !Ref InstanceCount 156 | Tags: 157 | - Key: Name 158 | Value: !Sub ${ApplicationName}-${EnvironmentName} 159 | PropagateAtLaunch: true 160 | CreationPolicy: 161 | ResourceSignal: 162 | Timeout: PT15M 163 | UpdatePolicy: 164 | AutoScalingRollingUpdate: 165 | MinInstancesInService: 1 166 | MaxBatchSize: 1 167 | PauseTime: PT15M 168 | WaitOnResourceSignals: true 169 | 170 | LaunchConfiguration: 171 | Type: AWS::AutoScaling::LaunchConfiguration 172 | Properties: 173 | ImageId: !FindInMap [AWSRegionToAMI, !Ref "AWS::Region", AMI] 174 | InstanceType: !Ref InstanceType 175 | IamInstanceProfile: !Ref InstanceProfile 176 | SecurityGroups: 177 | - !Ref ApplicationSecurityGroup 178 | UserData: ! 179 | "Fn::Base64": !Sub 180 | - |+ 181 | #!/bin/bash 182 | 183 | # Set up a CloudFormation wait signal, so we can notify when the CodeDeploy agent is running 184 | yum install -y aws-cfn-bootstrap 185 | /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource LaunchConfiguration 186 | 187 | # Install CodeDeploy agent 188 | yum -y update 189 | yum install -y ruby 190 | cd /home/ec2-user 191 | curl -O https://${InstallerBucket}.s3.amazonaws.com/latest/install 192 | chmod +x ./install 193 | ./install auto 194 | 195 | # Send a success/failure signal to CloudFormation based on the exit status of the last command 196 | /opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup 197 | - InstallerBucket: 198 | Fn::FindInMap: [ CodeDeployInstallerBuckets, !Ref "AWS::Region", Bucket] 199 | 200 | ########################################### 201 | # Load Balancer 202 | ########################################### 203 | 204 | LoadBalancer: 205 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 206 | Properties: 207 | Name: !Sub ${ApplicationName}-${EnvironmentName} 208 | Subnets: 209 | - !Ref PublicSubnet1 210 | - !Ref PublicSubnet2 211 | SecurityGroups: 212 | - !Ref LoadBalancerSecurityGroup 213 | Tags: 214 | - Key: Name 215 | Value: !Sub ${ApplicationName}-${EnvironmentName} 216 | 217 | LoadBalancerListener: 218 | Type: AWS::ElasticLoadBalancingV2::Listener 219 | Properties: 220 | LoadBalancerArn: !Ref LoadBalancer 221 | Port: 80 222 | Protocol: HTTP 223 | DefaultActions: 224 | - Type: forward 225 | TargetGroupArn: !Ref TargetGroup 226 | 227 | TargetGroup: 228 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 229 | Properties: 230 | Name: !Sub ${ApplicationName}-${EnvironmentName} 231 | VpcId: !Ref VPC 232 | Port: 8000 233 | Protocol: HTTP 234 | 235 | ########################################### 236 | # CodeDeploy 237 | ########################################### 238 | 239 | CodeDeployDeploymentGroup: 240 | Type: AWS::CodeDeploy::DeploymentGroup 241 | Properties: 242 | DeploymentGroupName: !Ref EnvironmentName 243 | DeploymentConfigName: !Ref DeploymentStrategy 244 | ServiceRoleArn: 245 | Fn::ImportValue: 246 | !Sub CodeDeployServiceRoleArn-${ApplicationName} 247 | ApplicationName: 248 | Fn::ImportValue: 249 | !Sub CodeDeployApplication-${ApplicationName} 250 | AutoScalingGroups: 251 | - !Ref AutoScalingGroup 252 | 253 | ########################################### 254 | # Security Groups 255 | ########################################### 256 | 257 | ApplicationSecurityGroup: 258 | Type: AWS::EC2::SecurityGroup 259 | Properties: 260 | VpcId: !Ref VPC 261 | GroupDescription: Controls who can access the application servers 262 | SecurityGroupIngress: 263 | # Only allow in bound from the load balancer 264 | - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 265 | IpProtocol: -1 266 | Tags: 267 | - Key: Name 268 | Value: !Sub ${ApplicationName}-${EnvironmentName}-ApplicationServers 269 | 270 | LoadBalancerSecurityGroup: 271 | Type: AWS::EC2::SecurityGroup 272 | Properties: 273 | VpcId: !Ref VPC 274 | GroupDescription: Controls who can access the load balancer 275 | SecurityGroupIngress: 276 | # Allow access from anywhere 277 | - CidrIp: 0.0.0.0/0 278 | IpProtocol: -1 279 | Tags: 280 | - Key: Name 281 | Value: !Sub ${ApplicationName}-${EnvironmentName}-LoadBalancers 282 | 283 | ########################################### 284 | # IAM 285 | ########################################### 286 | 287 | InstanceRole: 288 | Type: AWS::IAM::Role 289 | Properties: 290 | Path: / 291 | RoleName: !Sub ${ApplicationName}-${EnvironmentName}-${AWS::Region} 292 | AssumeRolePolicyDocument: | 293 | { 294 | "Statement": [{ 295 | "Action": "sts:AssumeRole", 296 | "Effect": "Allow", 297 | "Principal": { 298 | "Service": "ec2.amazonaws.com" 299 | } 300 | }] 301 | } 302 | Policies: 303 | - PolicyName: !Sub ${ApplicationName}-${EnvironmentName}-${AWS::Region} 304 | PolicyDocument: !Sub | 305 | { 306 | "Statement": [{ 307 | "Effect": "Allow", 308 | "Action": [ 309 | "s3:Get*" 310 | ], 311 | "Resource": [ "arn:aws:s3:::${ArtifactS3Bucket}/${ApplicationName}/*" ] 312 | }] 313 | } 314 | 315 | InstanceProfile: 316 | Type: AWS::IAM::InstanceProfile 317 | Properties: 318 | Path: / 319 | Roles: 320 | - !Ref InstanceRole 321 | 322 | ########################################### 323 | # VPC, Subnets, IGW, Routes, NAT etc 324 | ########################################### 325 | 326 | VPC: 327 | Type: AWS::EC2::VPC 328 | Properties: 329 | CidrBlock: !Ref VpcCIDR 330 | Tags: 331 | - Key: Name 332 | Value: !Sub ${ApplicationName}-${EnvironmentName} 333 | 334 | InternetGateway: 335 | Type: AWS::EC2::InternetGateway 336 | Properties: 337 | Tags: 338 | - Key: Name 339 | Value: !Sub $!Sub ${ApplicationName}-${EnvironmentName} 340 | 341 | InternetGatewayAttachment: 342 | Type: AWS::EC2::VPCGatewayAttachment 343 | Properties: 344 | InternetGatewayId: !Ref InternetGateway 345 | VpcId: !Ref VPC 346 | 347 | PublicSubnet1: 348 | Type: AWS::EC2::Subnet 349 | Properties: 350 | VpcId: !Ref VPC 351 | AvailabilityZone: !Select [ 0, !GetAZs ] 352 | CidrBlock: !Ref PublicSubnet1CIDR 353 | MapPublicIpOnLaunch: true 354 | Tags: 355 | - Key: Name 356 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Subnet (AZ1) 357 | 358 | PublicSubnet2: 359 | Type: AWS::EC2::Subnet 360 | Properties: 361 | VpcId: !Ref VPC 362 | AvailabilityZone: !Select [ 1, !GetAZs ] 363 | CidrBlock: !Ref PublicSubnet2CIDR 364 | MapPublicIpOnLaunch: true 365 | Tags: 366 | - Key: Name 367 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Subnet (AZ2) 368 | 369 | PrivateSubnet1: 370 | Type: AWS::EC2::Subnet 371 | Properties: 372 | VpcId: !Ref VPC 373 | AvailabilityZone: !Select [ 0, !GetAZs ] 374 | CidrBlock: !Ref PrivateSubnet1CIDR 375 | MapPublicIpOnLaunch: false 376 | Tags: 377 | - Key: Name 378 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Subnet (AZ1) 379 | 380 | PrivateSubnet2: 381 | Type: AWS::EC2::Subnet 382 | Properties: 383 | VpcId: !Ref VPC 384 | AvailabilityZone: !Select [ 1, !GetAZs ] 385 | CidrBlock: !Ref PrivateSubnet2CIDR 386 | MapPublicIpOnLaunch: false 387 | Tags: 388 | - Key: Name 389 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Subnet (AZ2) 390 | 391 | NatGateway1EIP: 392 | Type: AWS::EC2::EIP 393 | DependsOn: InternetGatewayAttachment 394 | Properties: 395 | Domain: vpc 396 | 397 | NatGateway2EIP: 398 | Type: AWS::EC2::EIP 399 | DependsOn: InternetGatewayAttachment 400 | Properties: 401 | Domain: vpc 402 | 403 | NatGateway1: 404 | Type: AWS::EC2::NatGateway 405 | Properties: 406 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 407 | SubnetId: !Ref PublicSubnet1 408 | 409 | NatGateway2: 410 | Type: AWS::EC2::NatGateway 411 | Properties: 412 | AllocationId: !GetAtt NatGateway2EIP.AllocationId 413 | SubnetId: !Ref PublicSubnet2 414 | 415 | PublicRouteTable: 416 | Type: AWS::EC2::RouteTable 417 | Properties: 418 | VpcId: !Ref VPC 419 | Tags: 420 | - Key: Name 421 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Routes 422 | 423 | DefaultPublicRoute: 424 | Type: AWS::EC2::Route 425 | Properties: 426 | RouteTableId: !Ref PublicRouteTable 427 | DestinationCidrBlock: 0.0.0.0/0 428 | GatewayId: !Ref InternetGateway 429 | 430 | PublicSubnet1RouteTableAssociation: 431 | Type: AWS::EC2::SubnetRouteTableAssociation 432 | Properties: 433 | RouteTableId: !Ref PublicRouteTable 434 | SubnetId: !Ref PublicSubnet1 435 | 436 | PublicSubnet2RouteTableAssociation: 437 | Type: AWS::EC2::SubnetRouteTableAssociation 438 | Properties: 439 | RouteTableId: !Ref PublicRouteTable 440 | SubnetId: !Ref PublicSubnet2 441 | 442 | PrivateRouteTable1: 443 | Type: AWS::EC2::RouteTable 444 | Properties: 445 | VpcId: !Ref VPC 446 | Tags: 447 | - Key: Name 448 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Routes (AZ1) 449 | 450 | DefaultPrivateRoute1: 451 | Type: AWS::EC2::Route 452 | Properties: 453 | RouteTableId: !Ref PrivateRouteTable1 454 | DestinationCidrBlock: 0.0.0.0/0 455 | NatGatewayId: !Ref NatGateway1 456 | 457 | PrivateSubnet1RouteTableAssociation: 458 | Type: AWS::EC2::SubnetRouteTableAssociation 459 | Properties: 460 | RouteTableId: !Ref PrivateRouteTable1 461 | SubnetId: !Ref PrivateSubnet1 462 | 463 | PrivateRouteTable2: 464 | Type: AWS::EC2::RouteTable 465 | Properties: 466 | VpcId: !Ref VPC 467 | Tags: 468 | - Key: Name 469 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Routes (AZ2) 470 | 471 | DefaultPrivateRoute2: 472 | Type: AWS::EC2::Route 473 | Properties: 474 | RouteTableId: !Ref PrivateRouteTable2 475 | DestinationCidrBlock: 0.0.0.0/0 476 | NatGatewayId: !Ref NatGateway2 477 | 478 | PrivateSubnet2RouteTableAssociation: 479 | Type: AWS::EC2::SubnetRouteTableAssociation 480 | Properties: 481 | RouteTableId: !Ref PrivateRouteTable2 482 | SubnetId: !Ref PrivateSubnet2 483 | 484 | Outputs: 485 | 486 | WebsiteURL: 487 | Description: Webserver URL 488 | Value: !Sub http://${LoadBalancer.DNSName}/version 489 | -------------------------------------------------------------------------------- /cloudformation/infrastructure.yml: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | Example Infrastructure for a Go Application. 4 | This template will deploy a highly available Auto Scaling Group of EC2 instances with the CodeDeploy 5 | agent installed ready for application deployments. It will also deploy a VPC with public and private 6 | subnets. An Application Load Balancer is placed in the public subnets, which forwards traffic to the 7 | application servers in the private subnets. NAT Gateways are deployed in each AZ to facilitate outbound 8 | traffic from the application servers to the internet. 9 | 10 | Parameters: 11 | 12 | # The following parameters are set dynamically by AWS CodePipeline 13 | 14 | ApplicationName: 15 | Type: String 16 | 17 | EnvironmentName: 18 | Type: String 19 | 20 | ArtifactS3Bucket: 21 | Type: String 22 | 23 | # All parameters below can be overridden on a per environment basis in config/* 24 | 25 | InstanceType: 26 | Description: The instance type to deploy to 27 | Type: String 28 | Default: t2.micro 29 | 30 | InstanceCount: 31 | Description: How many instances should the AutoScaling Group contain 32 | Type: Number 33 | Default: 2 34 | 35 | DeploymentStrategy: 36 | Description: The CodeDeploy deployment strategy to user 37 | Type: String 38 | Default: CodeDeployDefault.OneAtATime 39 | 40 | VpcCIDR: 41 | Description: Please enter the IP range (CIDR notation) for this VPC 42 | Type: String 43 | Default: 10.192.0.0/16 44 | 45 | PublicSubnet1CIDR: 46 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 47 | Type: String 48 | Default: 10.192.10.0/24 49 | 50 | PublicSubnet2CIDR: 51 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 52 | Type: String 53 | Default: 10.192.11.0/24 54 | 55 | PrivateSubnet1CIDR: 56 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 57 | Type: String 58 | Default: 10.192.20.0/24 59 | 60 | PrivateSubnet2CIDR: 61 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 62 | Type: String 63 | Default: 10.192.21.0/24 64 | 65 | Mappings: 66 | 67 | # Amazon Linux AMI 2016.09.1, released on 2016-12-22 68 | # https://aws.amazon.com/amazon-linux-ami/ 69 | 70 | AWSRegionToAMI: 71 | us-east-1: 72 | AMI: ami-9be6f38c 73 | us-east-2: 74 | AMI: ami-38cd975d 75 | us-west-1: 76 | AMI: ami-1e299d7e 77 | us-west-2: 78 | AMI: ami-b73d6cd7 79 | ca-central-1: 80 | AMI: ami-eb20928f 81 | eu-west-1: 82 | AMI: ami-c51e3eb6 83 | eu-west-2: 84 | AMI: ami-bfe0eadb 85 | eu-central-1: 86 | AMI: ami-211ada4e 87 | ap-southeast-1: 88 | AMI: ami-4dd6782e 89 | ap-northeast-2: 90 | AMI: ami-94bb6dfa 91 | ap-northeast-1: 92 | AMI: ami-9f0c67f8 93 | ap-southeast-2: 94 | AMI: ami-28cff44b 95 | ap-south-1: 96 | AMI: ami-9fc7b0f0 97 | sa-east-1: 98 | AMI: ami-bb40d8d7 99 | 100 | # CodeDeploy installer bucket list sourced from: 101 | # http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-set-up-new-instance.html 102 | 103 | CodeDeployInstallerBuckets: 104 | us-east-1: 105 | Bucket: aws-codedeploy-us-east-1 106 | us-east-2: 107 | Bucket: aws-codedeploy-us-east-2 108 | us-west-1: 109 | Bucket: aws-codedeploy-us-west-1 110 | us-west-2: 111 | Bucket: aws-codedeploy-us-west-2 112 | ca-central-1: 113 | Bucket: aws-codedeploy-ca-central-1 114 | eu-west-1: 115 | Bucket: aws-codedeploy-eu-west-1 116 | eu-west-2: 117 | Bucket: aws-codedeploy-eu-west-2 118 | eu-central-1: 119 | Bucket: aws-codedeploy-eu-central-1 120 | ap-northeast-1: 121 | Bucket: aws-codedeploy-ap-northeast-1 122 | ap-northeast-2: 123 | Bucket: aws-codedeploy-ap-northeast-2 124 | ap-southeast-1: 125 | Bucket: aws-codedeploy-ap-southeast-1 126 | ap-southeast-2: 127 | Bucket: aws-codedeploy-ap-southeast-2 128 | ap-south-1: 129 | Bucket: aws-codedeploy-ap-south-1 130 | sa-east-1: 131 | Bucket: aws-codedeploy-sa-east-1 132 | 133 | 134 | Resources: 135 | 136 | ########################################### 137 | # AutoScaling Group 138 | ########################################### 139 | 140 | AutoScalingGroup: 141 | Type: AWS::AutoScaling::AutoScalingGroup 142 | Properties: 143 | VPCZoneIdentifier: 144 | - !Ref PrivateSubnet1 145 | - !Ref PrivateSubnet2 146 | LaunchConfigurationName: !Ref LaunchConfiguration 147 | TargetGroupARNs: 148 | - !Ref TargetGroup 149 | MinSize: !Ref InstanceCount 150 | MaxSize: !Ref InstanceCount 151 | DesiredCapacity: !Ref InstanceCount 152 | Tags: 153 | - Key: Name 154 | Value: !Sub ${ApplicationName}-${EnvironmentName} 155 | PropagateAtLaunch: true 156 | CreationPolicy: 157 | ResourceSignal: 158 | Timeout: PT15M 159 | UpdatePolicy: 160 | AutoScalingRollingUpdate: 161 | MinInstancesInService: 1 162 | MaxBatchSize: 1 163 | PauseTime: PT15M 164 | WaitOnResourceSignals: true 165 | 166 | LaunchConfiguration: 167 | Type: AWS::AutoScaling::LaunchConfiguration 168 | Properties: 169 | ImageId: !FindInMap [AWSRegionToAMI, !Ref "AWS::Region", AMI] 170 | InstanceType: !Ref InstanceType 171 | IamInstanceProfile: !Ref InstanceProfile 172 | SecurityGroups: 173 | - !Ref ApplicationSecurityGroup 174 | UserData: ! 175 | "Fn::Base64": !Sub 176 | - |+ 177 | #!/bin/bash 178 | 179 | # Set up a CloudFormation wait signal, so we can notify when the CodeDeploy agent is running 180 | yum install -y aws-cfn-bootstrap 181 | /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource LaunchConfiguration 182 | 183 | # Install CodeDeploy agent 184 | yum -y update 185 | yum install -y ruby 186 | cd /home/ec2-user 187 | curl -O https://${InstallerBucket}.s3.amazonaws.com/latest/install 188 | chmod +x ./install 189 | ./install auto 190 | 191 | # Send a success/failure signal to CloudFormation based on the exit status of the last command 192 | /opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup 193 | - InstallerBucket: 194 | Fn::FindInMap: [ CodeDeployInstallerBuckets, !Ref "AWS::Region", Bucket] 195 | 196 | ########################################### 197 | # Load Balancer 198 | ########################################### 199 | 200 | LoadBalancer: 201 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 202 | Properties: 203 | Name: !Sub ${ApplicationName}-${EnvironmentName} 204 | Subnets: 205 | - !Ref PublicSubnet1 206 | - !Ref PublicSubnet2 207 | SecurityGroups: 208 | - !Ref LoadBalancerSecurityGroup 209 | Tags: 210 | - Key: Name 211 | Value: !Sub ${ApplicationName}-${EnvironmentName} 212 | 213 | LoadBalancerListener: 214 | Type: AWS::ElasticLoadBalancingV2::Listener 215 | Properties: 216 | LoadBalancerArn: !Ref LoadBalancer 217 | Port: 80 218 | Protocol: HTTP 219 | DefaultActions: 220 | - Type: forward 221 | TargetGroupArn: !Ref TargetGroup 222 | 223 | TargetGroup: 224 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 225 | Properties: 226 | Name: !Sub ${ApplicationName}-${EnvironmentName} 227 | VpcId: !Ref VPC 228 | Port: 8000 229 | Protocol: HTTP 230 | 231 | ########################################### 232 | # CodeDeploy 233 | ########################################### 234 | 235 | CodeDeployDeploymentGroup: 236 | Type: AWS::CodeDeploy::DeploymentGroup 237 | Properties: 238 | DeploymentGroupName: !Ref EnvironmentName 239 | DeploymentConfigName: !Ref DeploymentStrategy 240 | ServiceRoleArn: 241 | Fn::ImportValue: 242 | !Sub CodeDeployServiceRoleArn-${ApplicationName} 243 | ApplicationName: 244 | Fn::ImportValue: 245 | !Sub CodeDeployApplication-${ApplicationName} 246 | AutoScalingGroups: 247 | - !Ref AutoScalingGroup 248 | 249 | ########################################### 250 | # Security Groups 251 | ########################################### 252 | 253 | ApplicationSecurityGroup: 254 | Type: AWS::EC2::SecurityGroup 255 | Properties: 256 | VpcId: !Ref VPC 257 | GroupDescription: Controls who can access the application servers 258 | SecurityGroupIngress: 259 | # Only allow in bound from the load balancer 260 | - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 261 | IpProtocol: -1 262 | Tags: 263 | - Key: Name 264 | Value: !Sub ${ApplicationName}-${EnvironmentName}-ApplicationServers 265 | 266 | LoadBalancerSecurityGroup: 267 | Type: AWS::EC2::SecurityGroup 268 | Properties: 269 | VpcId: !Ref VPC 270 | GroupDescription: Controls who can access the load balancer 271 | SecurityGroupIngress: 272 | # Allow access from anywhere 273 | - CidrIp: 0.0.0.0/0 274 | IpProtocol: -1 275 | Tags: 276 | - Key: Name 277 | Value: !Sub ${ApplicationName}-${EnvironmentName}-LoadBalancers 278 | 279 | ########################################### 280 | # IAM 281 | ########################################### 282 | 283 | InstanceRole: 284 | Type: AWS::IAM::Role 285 | Properties: 286 | Path: / 287 | RoleName: !Sub ${ApplicationName}-${EnvironmentName}-${AWS::Region} 288 | AssumeRolePolicyDocument: | 289 | { 290 | "Statement": [{ 291 | "Action": "sts:AssumeRole", 292 | "Effect": "Allow", 293 | "Principal": { 294 | "Service": "ec2.amazonaws.com" 295 | } 296 | }] 297 | } 298 | Policies: 299 | - PolicyName: !Sub ${ApplicationName}-${EnvironmentName}-${AWS::Region} 300 | PolicyDocument: !Sub | 301 | { 302 | "Statement": [{ 303 | "Effect": "Allow", 304 | "Action": [ 305 | "s3:Get*" 306 | ], 307 | "Resource": [ "arn:aws:s3:::${ArtifactS3Bucket}/${ApplicationName}/*" ] 308 | }] 309 | } 310 | 311 | InstanceProfile: 312 | Type: AWS::IAM::InstanceProfile 313 | Properties: 314 | Path: / 315 | Roles: 316 | - !Ref InstanceRole 317 | 318 | ########################################### 319 | # VPC, Subnets, IGW, Routes, NAT etc 320 | ########################################### 321 | 322 | VPC: 323 | Type: AWS::EC2::VPC 324 | Properties: 325 | CidrBlock: !Ref VpcCIDR 326 | Tags: 327 | - Key: Name 328 | Value: !Sub ${ApplicationName}-${EnvironmentName} 329 | 330 | InternetGateway: 331 | Type: AWS::EC2::InternetGateway 332 | Properties: 333 | Tags: 334 | - Key: Name 335 | Value: !Sub $!Sub ${ApplicationName}-${EnvironmentName} 336 | 337 | InternetGatewayAttachment: 338 | Type: AWS::EC2::VPCGatewayAttachment 339 | Properties: 340 | InternetGatewayId: !Ref InternetGateway 341 | VpcId: !Ref VPC 342 | 343 | PublicSubnet1: 344 | Type: AWS::EC2::Subnet 345 | Properties: 346 | VpcId: !Ref VPC 347 | AvailabilityZone: !Select [ 0, !GetAZs ] 348 | CidrBlock: !Ref PublicSubnet1CIDR 349 | MapPublicIpOnLaunch: true 350 | Tags: 351 | - Key: Name 352 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Subnet (AZ1) 353 | 354 | PublicSubnet2: 355 | Type: AWS::EC2::Subnet 356 | Properties: 357 | VpcId: !Ref VPC 358 | AvailabilityZone: !Select [ 1, !GetAZs ] 359 | CidrBlock: !Ref PublicSubnet2CIDR 360 | MapPublicIpOnLaunch: true 361 | Tags: 362 | - Key: Name 363 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Subnet (AZ2) 364 | 365 | PrivateSubnet1: 366 | Type: AWS::EC2::Subnet 367 | Properties: 368 | VpcId: !Ref VPC 369 | AvailabilityZone: !Select [ 0, !GetAZs ] 370 | CidrBlock: !Ref PrivateSubnet1CIDR 371 | MapPublicIpOnLaunch: false 372 | Tags: 373 | - Key: Name 374 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Subnet (AZ1) 375 | 376 | PrivateSubnet2: 377 | Type: AWS::EC2::Subnet 378 | Properties: 379 | VpcId: !Ref VPC 380 | AvailabilityZone: !Select [ 1, !GetAZs ] 381 | CidrBlock: !Ref PrivateSubnet2CIDR 382 | MapPublicIpOnLaunch: false 383 | Tags: 384 | - Key: Name 385 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Subnet (AZ2) 386 | 387 | NatGateway1EIP: 388 | Type: AWS::EC2::EIP 389 | DependsOn: InternetGatewayAttachment 390 | Properties: 391 | Domain: vpc 392 | 393 | NatGateway2EIP: 394 | Type: AWS::EC2::EIP 395 | DependsOn: InternetGatewayAttachment 396 | Properties: 397 | Domain: vpc 398 | 399 | NatGateway1: 400 | Type: AWS::EC2::NatGateway 401 | Properties: 402 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 403 | SubnetId: !Ref PublicSubnet1 404 | 405 | NatGateway2: 406 | Type: AWS::EC2::NatGateway 407 | Properties: 408 | AllocationId: !GetAtt NatGateway2EIP.AllocationId 409 | SubnetId: !Ref PublicSubnet2 410 | 411 | PublicRouteTable: 412 | Type: AWS::EC2::RouteTable 413 | Properties: 414 | VpcId: !Ref VPC 415 | Tags: 416 | - Key: Name 417 | Value: !Sub ${ApplicationName} ${EnvironmentName} Public Routes 418 | 419 | DefaultPublicRoute: 420 | Type: AWS::EC2::Route 421 | Properties: 422 | RouteTableId: !Ref PublicRouteTable 423 | DestinationCidrBlock: 0.0.0.0/0 424 | GatewayId: !Ref InternetGateway 425 | 426 | PublicSubnet1RouteTableAssociation: 427 | Type: AWS::EC2::SubnetRouteTableAssociation 428 | Properties: 429 | RouteTableId: !Ref PublicRouteTable 430 | SubnetId: !Ref PublicSubnet1 431 | 432 | PublicSubnet2RouteTableAssociation: 433 | Type: AWS::EC2::SubnetRouteTableAssociation 434 | Properties: 435 | RouteTableId: !Ref PublicRouteTable 436 | SubnetId: !Ref PublicSubnet2 437 | 438 | PrivateRouteTable1: 439 | Type: AWS::EC2::RouteTable 440 | Properties: 441 | VpcId: !Ref VPC 442 | Tags: 443 | - Key: Name 444 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Routes (AZ1) 445 | 446 | DefaultPrivateRoute1: 447 | Type: AWS::EC2::Route 448 | Properties: 449 | RouteTableId: !Ref PrivateRouteTable1 450 | DestinationCidrBlock: 0.0.0.0/0 451 | NatGatewayId: !Ref NatGateway1 452 | 453 | PrivateSubnet1RouteTableAssociation: 454 | Type: AWS::EC2::SubnetRouteTableAssociation 455 | Properties: 456 | RouteTableId: !Ref PrivateRouteTable1 457 | SubnetId: !Ref PrivateSubnet1 458 | 459 | PrivateRouteTable2: 460 | Type: AWS::EC2::RouteTable 461 | Properties: 462 | VpcId: !Ref VPC 463 | Tags: 464 | - Key: Name 465 | Value: !Sub ${ApplicationName} ${EnvironmentName} Private Routes (AZ2) 466 | 467 | DefaultPrivateRoute2: 468 | Type: AWS::EC2::Route 469 | Properties: 470 | RouteTableId: !Ref PrivateRouteTable2 471 | DestinationCidrBlock: 0.0.0.0/0 472 | NatGatewayId: !Ref NatGateway2 473 | 474 | PrivateSubnet2RouteTableAssociation: 475 | Type: AWS::EC2::SubnetRouteTableAssociation 476 | Properties: 477 | RouteTableId: !Ref PrivateRouteTable2 478 | SubnetId: !Ref PrivateSubnet2 479 | 480 | Outputs: 481 | 482 | WebsiteURL: 483 | Description: Webserver URL 484 | Value: !Sub http://${LoadBalancer.DNSName} 485 | -------------------------------------------------------------------------------- /cloudformation/pipeline.yml: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | This CloudFormation template will deploy a full CI/CD pipeline for Go 4 | development. It includes building with AWS CodeBuild, and infrastructure 5 | deployment via AWS CloudFormation. It will create an AWS CodePipeline for 6 | orchastrating builds, testing and deployments to beta and production 7 | environments. 8 | 9 | Parameters: 10 | 11 | ApplicationName: 12 | Description: This will be used to name the pipeline and build resources 13 | Type: String 14 | AllowedPattern: '[A-Za-z0-9-]+' 15 | 16 | ArtifactS3Bucket: 17 | Description: An existing S3 bucket within this AWS account 18 | Type: String 19 | 20 | GitHubOAuthToken: 21 | Description: Create a token with 'repo' and 'admin:repo_hook' permissions here https://github.com/settings/tokens 22 | Type: String 23 | 24 | GitHubUser: 25 | Description: Enter GitHub username of the repository owner 26 | Type: String 27 | 28 | GitHubRepository: 29 | Description: Enter the repository name that should be monitored for changes 30 | Type: String 31 | 32 | GitHubBranch: 33 | Description: Enter the GitHub branch to monitored 34 | Type: String 35 | Default: master 36 | 37 | Metadata: 38 | 39 | AWS::CloudFormation::Interface: 40 | ParameterGroups: 41 | - 42 | Label: 43 | default: Application Configuration 44 | Parameters: 45 | - ApplicationName 46 | - ArtifactS3Bucket 47 | - 48 | Label: 49 | default: GitHub Configuration 50 | Parameters: 51 | - GitHubOAuthToken 52 | - GitHubUser 53 | - GitHubRepository 54 | - GitHubBranch 55 | 56 | ParameterLabels: 57 | ApplicationName: 58 | default: Application Name 59 | ArtifactS3Bucket: 60 | default: CodePipeline S3 Bucket 61 | GitHubRepository: 62 | default: Repository Name 63 | GitHubUser: 64 | default: Repository Owner 65 | GitHubBranch: 66 | default: Repository Branch 67 | GitHubOAuthToken: 68 | default: OAuth2 Token 69 | 70 | Resources: 71 | 72 | CodePipeline: 73 | Type: AWS::CodePipeline::Pipeline 74 | Properties: 75 | Name: !Ref ApplicationName 76 | RoleArn: !Sub ${CodePipelineRole.Arn} 77 | ArtifactStore: 78 | Type: S3 79 | Location: !Ref ArtifactS3Bucket 80 | Stages: 81 | - 82 | Name: Source 83 | Actions: 84 | - 85 | Name: GitHub 86 | ActionTypeId: 87 | Category: Source 88 | Owner: ThirdParty 89 | Version: 1 90 | Provider: GitHub 91 | OutputArtifacts: 92 | - Name: Source 93 | Configuration: 94 | Owner: !Ref GitHubUser 95 | Repo: !Ref GitHubRepository 96 | Branch: !Ref GitHubBranch 97 | OAuthToken: !Ref GitHubOAuthToken 98 | - 99 | Name: Build 100 | Actions: 101 | - 102 | Name: CodeBuild 103 | InputArtifacts: 104 | - Name: Source 105 | ActionTypeId: 106 | Category: Build 107 | Owner: AWS 108 | Version: 1 109 | Provider: CodeBuild 110 | OutputArtifacts: 111 | - Name: Built 112 | Configuration: 113 | ProjectName: !Ref CodeBuild 114 | - 115 | Name: Staging 116 | Actions: 117 | - 118 | Name: DeployInfrastructure 119 | RunOrder: 1 120 | InputArtifacts: 121 | - Name: Built 122 | ActionTypeId: 123 | Category: Deploy 124 | Owner: AWS 125 | Version: 1 126 | Provider: CloudFormation 127 | Configuration: 128 | ActionMode: REPLACE_ON_FAILURE 129 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 130 | Capabilities: CAPABILITY_NAMED_IAM 131 | StackName: !Sub ${ApplicationName}-staging 132 | TemplatePath: Built::cloudformation/infrastructure.yml 133 | TemplateConfiguration: Built::config/staging.conf 134 | ParameterOverrides: !Sub | 135 | { 136 | "ApplicationName": "${ApplicationName}", 137 | "EnvironmentName": "staging", 138 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 139 | } 140 | - 141 | Name: DeployApplication 142 | RunOrder: 2 143 | InputArtifacts: 144 | - Name: Built 145 | ActionTypeId: 146 | Category: Deploy 147 | Owner: AWS 148 | Version: 1 149 | Provider: CodeDeploy 150 | Configuration: 151 | ApplicationName: !Ref ApplicationName 152 | DeploymentGroupName: staging 153 | - 154 | Name: Approvals 155 | Actions: 156 | - 157 | Name: ProductionGate 158 | ActionTypeId: 159 | Category: Approval 160 | Owner: AWS 161 | Version: 1 162 | Provider: Manual 163 | 164 | - 165 | Name: Production 166 | Actions: 167 | - 168 | Name: DeployInfrastructure 169 | RunOrder: 1 170 | InputArtifacts: 171 | - Name: Built 172 | ActionTypeId: 173 | Category: Deploy 174 | Owner: AWS 175 | Version: 1 176 | Provider: CloudFormation 177 | Configuration: 178 | ActionMode: CREATE_UPDATE 179 | RoleArn: !Sub ${CodePipelineCloudFormationRole.Arn} 180 | Capabilities: CAPABILITY_NAMED_IAM 181 | StackName: !Sub ${ApplicationName}-production 182 | TemplatePath: Built::cloudformation/infrastructure.yml 183 | TemplateConfiguration: Built::config/production.conf 184 | ParameterOverrides: !Sub | 185 | { 186 | "ApplicationName": "${ApplicationName}", 187 | "EnvironmentName": "production", 188 | "ArtifactS3Bucket": "${ArtifactS3Bucket}" 189 | } 190 | - 191 | Name: DeployApplication 192 | RunOrder: 2 193 | InputArtifacts: 194 | - Name: Built 195 | ActionTypeId: 196 | Category: Deploy 197 | Owner: AWS 198 | Version: 1 199 | Provider: CodeDeploy 200 | Configuration: 201 | ApplicationName: !Ref ApplicationName 202 | DeploymentGroupName: production 203 | 204 | 205 | CodeBuild: 206 | Type: AWS::CodeBuild::Project 207 | Properties: 208 | Name: !Ref ApplicationName 209 | Description: !Sub Build project for ${ApplicationName} 210 | ServiceRole: !Ref CodeBuildRole 211 | Source: 212 | Type: CODEPIPELINE 213 | Environment: 214 | ComputeType: BUILD_GENERAL1_SMALL 215 | Image: aws/codebuild/golang:1.7.3 216 | Type: LINUX_CONTAINER 217 | EnvironmentVariables: 218 | - 219 | Name: ARTIFACT_S3_BUCKET 220 | Value: !Sub ${ArtifactS3Bucket} 221 | Artifacts: 222 | Name: !Ref ApplicationName 223 | Type: CODEPIPELINE 224 | 225 | CodePipelineRole: 226 | Type: AWS::IAM::Role 227 | Properties: 228 | Path: / 229 | RoleName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 230 | AssumeRolePolicyDocument: | 231 | { 232 | "Statement": [{ 233 | "Action": "sts:AssumeRole", 234 | "Effect": "Allow", 235 | "Principal": { 236 | "Service": "codepipeline.amazonaws.com" 237 | } 238 | }] 239 | } 240 | Policies: 241 | - 242 | PolicyName: !Sub ${ApplicationName}-CodePipeline-${AWS::Region} 243 | PolicyDocument: !Sub | 244 | { 245 | "Statement": [ 246 | { 247 | "Action": [ "s3:GetBucketVersioning" ], 248 | "Resource": [ "arn:aws:s3:::${ArtifactS3Bucket}" ], 249 | "Effect": "Allow" 250 | }, 251 | { 252 | "Action": [ 253 | "s3:PutObject", 254 | "s3:GetObject", 255 | "S3:GetObjectVersion" 256 | ], 257 | "Resource": [ 258 | "arn:aws:s3:::${ArtifactS3Bucket}/${ApplicationName}/*" 259 | ], 260 | "Effect": "Allow" 261 | }, 262 | { 263 | "Action": [ 264 | "codedeploy:CreateDeployment", 265 | "codedeploy:GetApplicationRevision", 266 | "codedeploy:GetDeployment", 267 | "codedeploy:GetDeploymentConfig", 268 | "codedeploy:RegisterApplicationRevision" 269 | ], 270 | "Resource": "*", 271 | "Effect": "Allow" 272 | }, 273 | { 274 | "Action": [ 275 | "cloudformation:CreateStack", 276 | "cloudformation:DeleteStack", 277 | "cloudformation:DescribeStacks", 278 | "cloudformation:UpdateStack", 279 | "cloudformation:CreateChangeSet", 280 | "cloudformation:DeleteChangeSet", 281 | "cloudformation:DescribeChangeSet", 282 | "cloudformation:ExecuteChangeSet", 283 | "cloudformation:SetStackPolicy", 284 | "cloudformation:ValidateTemplate", 285 | "iam:PassRole" 286 | ], 287 | "Resource": "*", 288 | "Effect": "Allow" 289 | }, 290 | { 291 | "Action": [ 292 | "codebuild:BatchGetBuilds", 293 | "codebuild:StartBuild" 294 | ], 295 | "Resource": "*", 296 | "Effect": "Allow" 297 | } 298 | ] 299 | } 300 | 301 | CodePipelineCloudFormationRole: 302 | Type: AWS::IAM::Role 303 | Properties: 304 | Path: / 305 | RoleName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 306 | AssumeRolePolicyDocument: | 307 | { 308 | "Statement": [{ 309 | "Action": "sts:AssumeRole", 310 | "Effect": "Allow", 311 | "Principal": { 312 | "Service": "cloudformation.amazonaws.com" 313 | } 314 | }] 315 | } 316 | Policies: 317 | - 318 | PolicyName: !Sub ${ApplicationName}-CloudFormation-${AWS::Region} 319 | PolicyDocument: !Sub | 320 | { 321 | "Statement": [{ 322 | "Effect": "Allow", 323 | "Action": [ "*" ], 324 | "Resource": "*" 325 | }] 326 | } 327 | 328 | CodeDeployApplication: 329 | Type: AWS::CodeDeploy::Application 330 | Properties: 331 | ApplicationName: !Sub ${ApplicationName} 332 | 333 | CodeDeployServiceRole: 334 | Type: AWS::IAM::Role 335 | Properties: 336 | Path: / 337 | RoleName: !Sub ${ApplicationName}-CodeDeploy-${AWS::Region} 338 | AssumeRolePolicyDocument: | 339 | { 340 | "Statement": [{ 341 | "Action": "sts:AssumeRole", 342 | "Effect": "Allow", 343 | "Principal": { 344 | "Service": "codedeploy.amazonaws.com" 345 | } 346 | }] 347 | } 348 | Policies: 349 | - PolicyName: !Sub ${AWS::StackName}-${AWS::Region} 350 | PolicyDocument: !Sub | 351 | { 352 | "Version": "2012-10-17", 353 | "Statement": [ 354 | { 355 | "Effect": "Allow", 356 | "Action": [ 357 | "autoscaling:CompleteLifecycleAction", 358 | "autoscaling:DeleteLifecycleHook", 359 | "autoscaling:DescribeAutoScalingGroups", 360 | "autoscaling:DescribeLifecycleHooks", 361 | "autoscaling:PutLifecycleHook", 362 | "autoscaling:RecordLifecycleActionHeartbeat", 363 | "ec2:DescribeInstances", 364 | "ec2:DescribeInstanceStatus", 365 | "tag:GetTags", 366 | "tag:GetResources", 367 | "sns:Publish", 368 | "cloudwatch:DescribeAlarms" 369 | ], 370 | "Resource": "*" 371 | } 372 | ] 373 | } 374 | 375 | CodeBuildRole: 376 | Type: AWS::IAM::Role 377 | Properties: 378 | Path: / 379 | RoleName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 380 | AssumeRolePolicyDocument: | 381 | { 382 | "Statement": [{ 383 | "Action": "sts:AssumeRole", 384 | "Effect": "Allow", 385 | "Principal": { 386 | "Service": "codebuild.amazonaws.com" 387 | } 388 | }] 389 | } 390 | Policies: 391 | - 392 | PolicyName: !Sub ${ApplicationName}-CodeBuild-${AWS::Region} 393 | PolicyDocument: !Sub | 394 | { 395 | "Statement": [ 396 | { 397 | "Effect": "Allow", 398 | "Resource": [ "*" ], 399 | "Action": [ 400 | "logs:CreateLogGroup", 401 | "logs:CreateLogStream", 402 | "logs:PutLogEvents" 403 | ] 404 | }, 405 | { 406 | "Effect": "Allow", 407 | "Resource": [ 408 | "arn:aws:s3:::${ArtifactS3Bucket}/${ApplicationName}/*" 409 | ], 410 | "Action": [ 411 | "s3:GetObject", 412 | "s3:GetObjectVersion", 413 | "s3:PutObject" 414 | ] 415 | } 416 | ] 417 | } 418 | 419 | Outputs: 420 | 421 | CodePipelineURL: 422 | Description: The URL for the created pipeline 423 | Value: !Sub https://${AWS::Region}.console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${ApplicationName} 424 | 425 | CodeDeployApplication: 426 | Description: The CodeDeploy application used across all environments 427 | Value: !Ref CodeDeployApplication 428 | Export: 429 | Name: !Sub CodeDeployApplication-${ApplicationName} 430 | 431 | CodeDeployServiceRoleArn: 432 | Description: The CodeDeploy service role used across all environments 433 | Value: !GetAtt CodeDeployServiceRole.Arn 434 | Export: 435 | Name: !Sub CodeDeployServiceRoleArn-${ApplicationName} 436 | 437 | 438 | -------------------------------------------------------------------------------- /codedeploy/AfterInstall.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/codedeploy/AfterInstall.sh -------------------------------------------------------------------------------- /codedeploy/ApplicationStart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /opt/app > /dev/null 2> /dev/null < /dev/null & 3 | -------------------------------------------------------------------------------- /codedeploy/ApplicationStop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | killall app 3 | exit 0 4 | -------------------------------------------------------------------------------- /codedeploy/BeforeInstall.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/codedeploy/BeforeInstall.sh -------------------------------------------------------------------------------- /codedeploy/ValidateService.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | curl http://localhost:8000 3 | -------------------------------------------------------------------------------- /config/production.conf: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "InstanceType": "t2.micro", 4 | "InstanceCount": "2", 5 | "DeploymentStrategy": "CodeDeployDefault.OneAtATime", 6 | "VpcCIDR": "10.192.0.0/16", 7 | "PublicSubnet1CIDR": "10.192.10.0/24", 8 | "PublicSubnet2CIDR": "10.192.11.0/24", 9 | "PrivateSubnet1CIDR": "10.192.20.0/24", 10 | "PrivateSubnet2CIDR": "10.192.21.0/24" 11 | } 12 | } -------------------------------------------------------------------------------- /config/staging.conf: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "InstanceType": "t2.micro", 4 | "InstanceCount": "2", 5 | "DeploymentStrategy": "CodeDeployDefault.OneAtATime", 6 | "VpcCIDR": "10.193.0.0/16", 7 | "PublicSubnet1CIDR": "10.193.10.0/24", 8 | "PublicSubnet2CIDR": "10.193.11.0/24", 9 | "PrivateSubnet1CIDR": "10.193.20.0/24", 10 | "PrivateSubnet2CIDR": "10.193.21.0/24" 11 | } 12 | } -------------------------------------------------------------------------------- /images/architecture-overview.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/data.plist -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/image12.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/image14.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/image6.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/image8.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.graffle/image9.pdf -------------------------------------------------------------------------------- /images/architecture-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/architecture-overview.png -------------------------------------------------------------------------------- /images/cicd-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/cicd-overview.png -------------------------------------------------------------------------------- /images/cloudformation-launch-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/cloudformation-launch-stack.png -------------------------------------------------------------------------------- /images/codebuild-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/codebuild-log.png -------------------------------------------------------------------------------- /images/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/fork.png -------------------------------------------------------------------------------- /images/pipeline-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-screenshot.png -------------------------------------------------------------------------------- /images/pipeline-stage-approvals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-stage-approvals.png -------------------------------------------------------------------------------- /images/pipeline-stage-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-stage-build.png -------------------------------------------------------------------------------- /images/pipeline-stage-production.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-stage-production.png -------------------------------------------------------------------------------- /images/pipeline-stage-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-stage-source.png -------------------------------------------------------------------------------- /images/pipeline-stage-staging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/pipeline-stage-staging.png -------------------------------------------------------------------------------- /images/stack-outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/stack-outputs.png -------------------------------------------------------------------------------- /images/stack-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/golang-deployment-pipeline/8ae06b0ecb1c000b6de5cce4b6b94650bd02e5d7/images/stack-settings.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | const version string = "2.0.1" 10 | 11 | // VersionHandler handles incoming requests to /version 12 | // and just returns a simple version number 13 | func versionHandler(w http.ResponseWriter, r *http.Request) { 14 | io.WriteString(w, version) 15 | } 16 | 17 | func main() { 18 | log.Printf("Listening on port 8000...") 19 | http.HandleFunc("/version", versionHandler) 20 | http.ListenAndServe(":8000", nil) 21 | } 22 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | func TestVersionHandler(t *testing.T) { 10 | 11 | // Create a request to pass to our handler 12 | req, err := http.NewRequest("GET", "/", nil) 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | 17 | // Create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response 18 | rr := httptest.NewRecorder() 19 | handler := http.HandlerFunc(versionHandler) 20 | 21 | // Our handlers satisfy http.Handler, so we can call their ServeHTTP method 22 | // directly and pass in our Request and ResponseRecorder. 23 | handler.ServeHTTP(rr, req) 24 | 25 | // Check the status code is what we expect. 26 | if status := rr.Code; status != http.StatusOK { 27 | t.Errorf("handler returned wrong status code: got %v want %v", 28 | status, http.StatusOK) 29 | } 30 | 31 | // Check the response body is what we expect. 32 | expected := version 33 | if rr.Body.String() != expected { 34 | t.Errorf("handler returned unexpected body: got %v want %v", 35 | rr.Body.String(), expected) 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------