├── .github ├── scripts │ └── build.sh └── workflows │ └── deploy.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appspec.yml ├── aws-coodedeplooy-github-action-deploymentV3.png ├── aws └── scripts │ ├── after-install.sh │ ├── application-start.sh │ ├── application-stop.sh │ ├── before-install.sh │ └── validate-service.sh ├── cloudformation └── template.yaml └── spring-boot-hello-world-example ├── .classpath ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.apt.core.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── pom.xml └── src └── main └── java └── com └── helloworld ├── SpringBootHelloWorldExampleApplication.java └── controller └── HelloWorldController.java /.github/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | # Maven is used to build and create a war file. 5 | mvn -Dmaven.test.skip=true clean install 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | env: 7 | applicationfolder: spring-boot-hello-world-example 8 | AWS_REGION: ##region## 9 | S3BUCKET: ##s3-bucket## 10 | 11 | 12 | jobs: 13 | build: 14 | name: Build and Package 15 | runs-on: ubuntu-latest 16 | permissions: 17 | id-token: write 18 | contents: read 19 | steps: 20 | - uses: actions/checkout@v2 21 | name: Checkout Repository 22 | 23 | - uses: aws-actions/configure-aws-credentials@v1 24 | with: 25 | role-to-assume: ${{ secrets.IAMROLE_GITHUB }} 26 | role-session-name: GitHub-Action-Role 27 | aws-region: ${{ env.AWS_REGION }} 28 | 29 | - name: Set up JDK 1.8 30 | uses: actions/setup-java@v1 31 | with: 32 | java-version: 1.8 33 | 34 | - name: chmod 35 | run: chmod -R +x ./.github 36 | 37 | - name: Build and Package Maven 38 | id: package 39 | working-directory: ${{ env.applicationfolder }} 40 | run: $GITHUB_WORKSPACE/.github/scripts/build.sh 41 | 42 | - name: Upload Artifact to s3 43 | working-directory: ${{ env.applicationfolder }}/target 44 | run: aws s3 cp *.war s3://${{ env.S3BUCKET }}/ 45 | 46 | deploy: 47 | needs: build 48 | runs-on: ubuntu-latest 49 | environment: Dev 50 | permissions: 51 | id-token: write 52 | contents: read 53 | steps: 54 | - uses: actions/checkout@v2 55 | - uses: aws-actions/configure-aws-credentials@v1 56 | with: 57 | role-to-assume: ${{ secrets.IAMROLE_GITHUB }} 58 | role-session-name: GitHub-Action-Role 59 | aws-region: ${{ env.AWS_REGION }} 60 | - run: | 61 | echo "Deploying branch ${{ env.GITHUB_REF }} to ${{ github.event.inputs.environment }}" 62 | commit_hash=`git rev-parse HEAD` 63 | aws deploy create-deployment --application-name CodeDeployAppNameWithASG --deployment-group-name CodeDeployGroupName --github-location repository=$GITHUB_REPOSITORY,commitId=$commit_hash --ignore-application-stop-failures 64 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Integrating with GitHub Actions – CICD pipeline to Deploy a Web App to Amazon EC2 2 | 3 | Many Organizations adopt [DevOps Practices](https://aws.amazon.com/devops/what-is-devops/) to innovate faster by automating and streamlining the software development and infrastructure management processes. Beyond cultural adoption, DevOps also suggests following certain best practices and Continuous Integration and Continuous Delivery (CI/CD) is among the important ones to start with. CI/CD practice reduces the time it takes to release new software updates by automating deployment activities. Many tools are available to implement this practice. Although AWS has a set of native tools to help achieve your CI/CD goals, it also offers flexibility and extensibility for integrating with numerous third party tools. 4 | 5 | In this post, you will use [GitHub Actions](https://help.github.com/en/actions) to create a CI/CD workflow and [AWS CodeDeploy](https://aws.amazon.com/codedeploy/) to deploy a sample Java SpringBoot application to Amazon Elastic Compute Cloud ([Amazon EC2](https://docs.aws.amazon.com/ec2/index.html?nc2=h_ql_doc_ec2#amazon-ec2)) instances in an Autoscaling group. 6 | 7 | 8 | GitHub Actions is a feature on GitHub’s popular development platform that helps you automate your software development workflows in the same place that you store code and collaborate on pull requests and issues. You can write individual tasks called actions, and then combine them to create a custom workflow. Workflows are custom automated processes that you can set up in your repository to build, test, package, release, or deploy any code project on GitHub. 9 | 10 | AWS CodeDeploy is a deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless AWS Lambda functions, or Amazon Elastic Container Service (Amazon ECS) services. 11 | 12 | 13 | ## Solution Overview 14 | 15 | The solution utilizes following services: 16 | 17 | 1. [GitHub Actions](https://docs.github.com/en/actions) : Workflow Orchestration tool that will host the Pipeline. 18 | 2. [AWS CodeDeploy](https://aws.amazon.com/codedeploy/) : AWS service to manage deployment on Amazon EC2 Autoscaling Group. 19 | 3. [AWS Auto Scaling](https://aws.amazon.com/ec2/autoscaling/) : AWS Service to help maintain application availability and elasticity by automatically adding or removing EC2 instances. 20 | 4. [Amazon EC2](https://docs.aws.amazon.com/ec2/index.html?nc2=h_ql_doc_ec2#amazon-ec2) : Destination Compute server for the application deployment. 21 | 5. [AWS CloudFormation](https://aws.amazon.com/cloudformation/) : AWS infrastructure as code (IaC) service used to spin up the initial infrastructure on AWS side. 22 | 6. [IAM OIDC identity provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) : Federated authentication service to establish trust between GitHub and AWS to allow GitHub Actions to deploy on AWS without maintaining AWS Secrets and credentials. 23 | 7. [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) : Amazon S3 to store the deployment artifacts. 24 | 25 | The following diagram illustrates the architecture for the solution: 26 | ![Alt Text](aws-coodedeplooy-github-action-deploymentV3.png?raw=true "Title") 27 | 28 | ## Prerequisites 29 | Before you begin, you need to complete the following prerequisites: 30 | 31 | * An AWS account with permissions to create the necessary resources. 32 | * A [Git Client](https://git-scm.com/downloads) to clone the provided source code. 33 | * A [GitHub account](https://github.com/) with permissions to configure GitHub repositories, create workflows, and configure GitHub secrets. 34 | 35 | ## Walkthrough 36 | The following steps provide a high-level overview of the walkthrough: 37 | 38 | 1. Clone the project from the AWS code samples repository. 39 | 2. Deploy the AWS CloudFormation template to create the required services. 40 | 3. Update the source code. 41 | 4. Setup GitHub secrets. 42 | 5. Integrate CodeDeploy with GitHub 43 | 6. Trigger the GitHub Action to build and deploy the code. 44 | 7. Verify the deployment. 45 | 46 | ## Download the source code 47 | 48 | Clone this repository aws-codedeploy-github-actions-deployment 49 | 50 | git clone https://github.com/aws-samples/aws-codedeploy-github-actions-deployment.git 51 | 52 | Create an empty repository in your personal GitHub account. 53 | 54 | git clone https://github.com//.git 55 | 56 | Copy the code. We need contents from the hidden .github folder for the GitHub actions to work. 57 | 58 | cp -r aws-codedeploy-github-actions-deployment/. 59 | 60 | e.g. GitActionsDeploytoAWS 61 | 62 | ## Deploying the CloudFormation template 63 | To deploy the CloudFormation template, complete the following steps: 64 | 65 | 1. Open AWS CloudFormation console. Enter your account ID, user name and Password. 66 | 2. Check your region, this solution uses us-east-1. 67 | 3. If this is new AWS CloudFormation account, click Create New Stack. Otherwise, select Create Stack. 68 | 4. Select Template is Ready 69 | 5. Click Upload a template file 70 | 6. Click Choose File. Navigate to template.yml file in your cloned repository at “aws-codedeploy-github-actions-deployment/cloudformation/template.yaml” 71 | 7. Select the template.yml file and select next. 72 | 8. In Specify Stack Details, add or modify values as needed. 73 | - Stack name = CodeDeployStack. 74 | - VPC and Subnets = (these are pre-populated for you) you can change these values if you prefer to use your own Subnets) 75 | - GitHubThumbprintList = 6938fd4d98bab03faadb97b34396831e3780aea1 76 | - GitHubRepoName – Name of your GitHub personal repository which you created. 77 | 9. On the Options page, click Next. 78 | 10. Select the acknowledgement box to allow the creation of IAM resources, and then select Create. 79 | It will take CloudFormation about 5 minutes to create all the resources. This stack would create below resources. 80 | - Two EC2 Linux instances with Tomcat server and CodeDeploy agent installed 81 | - Autoscaling group with Internet Application load balancer 82 | - CodeDeploy application name and deployment group 83 | - S3 bucket to store build artifacts 84 | - Identity and Access Management (IAM) OIDC identity provider 85 | - Instance profile for Amazon EC2 86 | - Service role for CodeDeploy 87 | - Security groups for ALB and Amazon EC2 88 | 89 | ## GitHub configuration and Testing 90 | 91 | Please follow the [blog post](https://aws.amazon.com/blogs/devops/integrating-with-github-actions-ci-cd-pipeline-to-deploy-a-web-app-to-amazon-ec2/) to setup GitHub actions and test the CICD flow. 92 | 93 | ## Clean up 94 | 95 | To avoid incurring future changes, you should clean up the resources that you created. 96 | 97 | 1. Empty the Amazon S3 bucket: 98 | 2. Delete the CloudFormation stack (CodeDeployStack) from the AWS console. 99 | 3. Delete the GitHub Secret (‘IAMROLE_GITHUB’) 100 | 1. Go to the repository settings on GitHub Page. 101 | 2. Select Secrets under Actions. 102 | 3. Select IAMROLE_GITHUB, and delete it. 103 | 104 | 105 | ## Security 106 | 107 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 108 | 109 | ## License 110 | 111 | This library is licensed under the MIT-0 License. See the LICENSE file. 112 | -------------------------------------------------------------------------------- /appspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.0 2 | os: linux 3 | files: 4 | - source: /aws 5 | destination: /usr/local/codedeployresources 6 | hooks: 7 | ApplicationStop: 8 | - location: aws/scripts/application-stop.sh 9 | timeout: 300 10 | runas: root 11 | BeforeInstall: 12 | - location: aws/scripts/before-install.sh 13 | timeout: 300 14 | runas: root 15 | AfterInstall: 16 | - location: aws/scripts/after-install.sh 17 | timeout: 300 18 | runas: root 19 | ApplicationStart: 20 | - location: aws/scripts/application-start.sh 21 | timeout: 300 22 | runas: root 23 | ValidateService: 24 | - location: aws/scripts/validate-service.sh 25 | timeout: 300 26 | runas: root 27 | -------------------------------------------------------------------------------- /aws-coodedeplooy-github-action-deploymentV3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-codedeploy-github-actions-deployment/bcb33a707c5177ae7eada04260250e0eb6dd679b/aws-coodedeplooy-github-action-deploymentV3.png -------------------------------------------------------------------------------- /aws/scripts/after-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | 5 | # Copy war file from S3 bucket to tomcat webapp folder 6 | aws s3 cp s3://##s3-bucket##/SpringBootHelloWorldExampleApplication.war /usr/local/tomcat9/webapps/SpringBootHelloWorldExampleApplication.war 7 | 8 | 9 | # Ensure the ownership permissions are correct. 10 | chown -R tomcat:tomcat /usr/local/tomcat9/webapps -------------------------------------------------------------------------------- /aws/scripts/application-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | # Start Tomcat, the application server. 5 | service tomcat start -------------------------------------------------------------------------------- /aws/scripts/application-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # System control will return either "active" or "inactive". 5 | tomcat_running=$(systemctl is-active tomcat) 6 | if [ "$tomcat_running" == "active" ]; then 7 | service tomcat stop 8 | fi -------------------------------------------------------------------------------- /aws/scripts/before-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | # Delete the old directory as needed. 5 | if [ -d /usr/local/codedeployresources ]; then 6 | rm -rf /usr/local/codedeployresources/ 7 | fi 8 | 9 | mkdir -vp /usr/local/codedeployresources 10 | -------------------------------------------------------------------------------- /aws/scripts/validate-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | NUMBER_OF_ATTEMPTS=10 5 | SLEEP_TIME=3 6 | 7 | # Ensure Tomcat is running by making an HTTPS GET request to the default page. 8 | # Don't try and verify the certificate; use the --insecure flag. 9 | for i in `seq 1 $NUMBER_OF_ATTEMPTS`; 10 | do 11 | HTTP_CODE=`curl --insecure --write-out '%{http_code}' -o /dev/null -m 10 -q -s http://localhost:8080` 12 | if [ "$HTTP_CODE" == "200" ]; then 13 | echo "app server is running." 14 | exit 0 15 | fi 16 | echo "Attempt to curl endpoint returned HTTP Code $HTTP_CODE. Backing off and retrying." 17 | sleep $SLEEP_TIME 18 | done 19 | echo "Server did not come up after expected time. Failing." 20 | exit 1 -------------------------------------------------------------------------------- /cloudformation/template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: 'Cloudformation for provisioning services required to setup the CI/CD using GitHub actions and CodeDeploy. 4 | **WARNING** This template creates EC2,VPC and related resources. You will be billed for the AWS resources used if you create a stack from this template' 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: "VPC Configurations" 10 | Parameters: 11 | - VpcCIDR 12 | - PublicSubnet1CIDR 13 | - PublicSubnet2CIDR 14 | - PrivateSubnet1CIDR 15 | - PrivateSubnet2CIDR 16 | - Label: 17 | default: "Autoscaling configurations" 18 | Parameters: 19 | - ImageId 20 | - InstanceType 21 | - AutoScalingGroupMinSize 22 | - AutoScalingGroupMaxSize 23 | - AutoScalingGroupDesiredCapacity 24 | - Label: 25 | default: "Github configurations" 26 | Parameters: 27 | - GithubRepoName 28 | - ThumbprintList 29 | Parameters: 30 | VpcCIDR: 31 | Description: Please enter the IP range (CIDR notation) for this VPC 32 | Type: String 33 | Default: 10.192.0.0/16 34 | 35 | PublicSubnet1CIDR: 36 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 37 | Type: String 38 | Default: 10.192.10.0/24 39 | 40 | PublicSubnet2CIDR: 41 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 42 | Type: String 43 | Default: 10.192.11.0/24 44 | 45 | PrivateSubnet1CIDR: 46 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 47 | Type: String 48 | Default: 10.192.20.0/24 49 | 50 | PrivateSubnet2CIDR: 51 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 52 | Type: String 53 | Default: 10.192.21.0/24 54 | 55 | ImageId: 56 | Type: 'AWS::SSM::Parameter::Value' 57 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 58 | Description: The Amazon EC2 Linux instance Amazon Machine Image (AMI), which designates the configuration of the new instance. 59 | InstanceType: 60 | Type: String 61 | Default: t2.medium 62 | Description: The type of Amazon EC2 Linux instances that will be launched for this project. 63 | AutoScalingGroupMinSize: 64 | Type: Number 65 | Default: 2 66 | Description: Enter the Min Size for the ASG 67 | AutoScalingGroupMaxSize: 68 | Type: Number 69 | Default: 2 70 | Description: Enter the Max Size for the ASG 71 | AutoScalingGroupDesiredCapacity: 72 | Type: Number 73 | Default: 2 74 | Description: Enter the Max Size for the ASG 75 | ThumbprintList: 76 | Type: String 77 | Default: 6938fd4d98bab03faadb97b34396831e3780aea1 78 | Description: A thumbprint of an Open ID Connector is a SHA1 hash of the public certificate of the host 79 | GithubRepoName: 80 | Type: String 81 | Description: GitHub repository name Ex-TestUser/TestCodeDeploy 82 | 83 | Resources: 84 | VPC: 85 | Type: AWS::EC2::VPC 86 | Properties: 87 | CidrBlock: !Ref VpcCIDR 88 | EnableDnsSupport: true 89 | EnableDnsHostnames: true 90 | 91 | InternetGateway: 92 | Type: AWS::EC2::InternetGateway 93 | 94 | InternetGatewayAttachment: 95 | Type: AWS::EC2::VPCGatewayAttachment 96 | Properties: 97 | InternetGatewayId: !Ref InternetGateway 98 | VpcId: !Ref VPC 99 | 100 | PublicSubnet1: 101 | Type: AWS::EC2::Subnet 102 | Properties: 103 | VpcId: !Ref VPC 104 | AvailabilityZone: !Select [ 0, !GetAZs '' ] 105 | CidrBlock: !Ref PublicSubnet1CIDR 106 | MapPublicIpOnLaunch: true 107 | Tags: 108 | - Key: Name 109 | Value: Public Subnet (AZ1) 110 | 111 | PublicSubnet2: 112 | Type: AWS::EC2::Subnet 113 | Properties: 114 | VpcId: !Ref VPC 115 | AvailabilityZone: !Select [ 1, !GetAZs '' ] 116 | CidrBlock: !Ref PublicSubnet2CIDR 117 | MapPublicIpOnLaunch: true 118 | Tags: 119 | - Key: Name 120 | Value: Public Subnet (AZ2) 121 | 122 | PrivateSubnet1: 123 | Type: AWS::EC2::Subnet 124 | Properties: 125 | VpcId: !Ref VPC 126 | AvailabilityZone: !Select [ 0, !GetAZs '' ] 127 | CidrBlock: !Ref PrivateSubnet1CIDR 128 | MapPublicIpOnLaunch: false 129 | Tags: 130 | - Key: Name 131 | Value: Private Subnet (AZ1) 132 | 133 | PrivateSubnet2: 134 | Type: AWS::EC2::Subnet 135 | Properties: 136 | VpcId: !Ref VPC 137 | AvailabilityZone: !Select [ 1, !GetAZs '' ] 138 | CidrBlock: !Ref PrivateSubnet2CIDR 139 | MapPublicIpOnLaunch: false 140 | Tags: 141 | - Key: Name 142 | Value: Private Subnet (AZ2) 143 | 144 | NatGateway1EIP: 145 | Type: AWS::EC2::EIP 146 | DependsOn: InternetGatewayAttachment 147 | Properties: 148 | Domain: vpc 149 | 150 | NatGateway1: 151 | Type: AWS::EC2::NatGateway 152 | Properties: 153 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 154 | SubnetId: !Ref PublicSubnet1 155 | 156 | PublicRouteTable: 157 | Type: AWS::EC2::RouteTable 158 | Properties: 159 | VpcId: !Ref VPC 160 | 161 | DefaultPublicRoute: 162 | Type: AWS::EC2::Route 163 | DependsOn: InternetGatewayAttachment 164 | Properties: 165 | RouteTableId: !Ref PublicRouteTable 166 | DestinationCidrBlock: 0.0.0.0/0 167 | GatewayId: !Ref InternetGateway 168 | 169 | PublicSubnet1RouteTableAssociation: 170 | Type: AWS::EC2::SubnetRouteTableAssociation 171 | Properties: 172 | RouteTableId: !Ref PublicRouteTable 173 | SubnetId: !Ref PublicSubnet1 174 | 175 | PublicSubnet2RouteTableAssociation: 176 | Type: AWS::EC2::SubnetRouteTableAssociation 177 | Properties: 178 | RouteTableId: !Ref PublicRouteTable 179 | SubnetId: !Ref PublicSubnet2 180 | 181 | 182 | PrivateRouteTable1: 183 | Type: AWS::EC2::RouteTable 184 | Properties: 185 | VpcId: !Ref VPC 186 | 187 | DefaultPrivateRoute1: 188 | Type: AWS::EC2::Route 189 | Properties: 190 | RouteTableId: !Ref PrivateRouteTable1 191 | DestinationCidrBlock: 0.0.0.0/0 192 | NatGatewayId: !Ref NatGateway1 193 | 194 | PrivateSubnet1RouteTableAssociation: 195 | Type: AWS::EC2::SubnetRouteTableAssociation 196 | Properties: 197 | RouteTableId: !Ref PrivateRouteTable1 198 | SubnetId: !Ref PrivateSubnet1 199 | 200 | PrivateRouteTable2: 201 | Type: AWS::EC2::RouteTable 202 | Properties: 203 | VpcId: !Ref VPC 204 | 205 | DefaultPrivateRoute2: 206 | Type: AWS::EC2::Route 207 | Properties: 208 | RouteTableId: !Ref PrivateRouteTable2 209 | DestinationCidrBlock: 0.0.0.0/0 210 | NatGatewayId: !Ref NatGateway1 211 | 212 | PrivateSubnet2RouteTableAssociation: 213 | Type: AWS::EC2::SubnetRouteTableAssociation 214 | Properties: 215 | RouteTableId: !Ref PrivateRouteTable2 216 | SubnetId: !Ref PrivateSubnet2 217 | 218 | WebappRole: 219 | Type: AWS::IAM::Role 220 | Properties: 221 | Path: "/" 222 | RoleName: WebappRole 223 | AssumeRolePolicyDocument: 224 | Version: "2012-10-17" 225 | Statement: 226 | - 227 | Effect: "Allow" 228 | Principal: 229 | Service: 230 | - "ec2.amazonaws.com" 231 | - "codedeploy.amazonaws.com" 232 | Action: 233 | - "sts:AssumeRole" 234 | ManagedPolicyArns: 235 | - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore' 236 | Policies: 237 | - 238 | PolicyName: "allow-webapp-deployment-bucket-policy" 239 | PolicyDocument: 240 | Version: "2012-10-17" 241 | Statement: 242 | - 243 | Effect: "Allow" 244 | Action: 245 | - "s3:getObject" 246 | Resource: !Sub arn:${AWS::Partition}:s3:::${WebappDeploymentBucket}/* 247 | IDCProvider: 248 | Type: AWS::IAM::OIDCProvider 249 | Properties: 250 | Url: "https://token.actions.githubusercontent.com" 251 | ClientIdList: 252 | - "sts.amazonaws.com" 253 | ThumbprintList: 254 | - !Ref ThumbprintList 255 | GitHubIAMRole: 256 | Type: AWS::IAM::Role 257 | Properties: 258 | Path: "/" 259 | RoleName: CodeDeployRoleforGitHub 260 | AssumeRolePolicyDocument: 261 | Statement: 262 | - Effect: Allow 263 | Action: sts:AssumeRoleWithWebIdentity 264 | Principal: 265 | Federated: !Ref IDCProvider 266 | Condition: 267 | StringLike: 268 | token.actions.githubusercontent.com:sub: !Sub repo:${GithubRepoName}:* 269 | MaxSessionDuration: 3600 270 | Description: "Github Actions role" 271 | Policies: 272 | 273 | - PolicyName: 'CodeDeployRoleforGitHub-policy' 274 | PolicyDocument: 275 | Version: '2012-10-17' 276 | Statement: 277 | - Effect: Allow 278 | Action: 279 | - 'codedeploy:Get*' 280 | - 'codedeploy:Batch*' 281 | - 'codedeploy:CreateDeployment' 282 | - 'codedeploy:RegisterApplicationRevision' 283 | - 'codedeploy:List*' 284 | Resource: 285 | - !Sub 'arn:${AWS::Partition}:codedeploy:*:${AWS::AccountId}:*' 286 | - Effect: Allow 287 | Action: 288 | - 's3:putObject' 289 | Resource: !Sub arn:${AWS::Partition}:s3:::${WebappDeploymentBucket}/* 290 | 291 | WebappApplication: 292 | Type: AWS::CodeDeploy::Application 293 | Properties: 294 | ApplicationName: CodeDeployAppNameWithASG 295 | WebappDeploymentGroup: 296 | Type: AWS::CodeDeploy::DeploymentGroup 297 | Properties: 298 | ApplicationName: !Ref WebappApplication 299 | ServiceRoleArn: !GetAtt CodeDeployRole.Arn 300 | DeploymentConfigName: CodeDeployDefault.OneAtATime 301 | DeploymentGroupName: CodeDeployGroupName 302 | AutoRollbackConfiguration: 303 | Enabled: true 304 | Events: 305 | - DEPLOYMENT_FAILURE 306 | - DEPLOYMENT_STOP_ON_REQUEST 307 | 308 | AutoScalingGroups: 309 | - Ref: AutoScalingGroup 310 | ALBSecurityGroup: 311 | Type: AWS::EC2::SecurityGroup 312 | Properties: 313 | GroupDescription: allow access to ALB from internet 314 | VpcId: !Ref VPC 315 | SecurityGroupIngress: 316 | - IpProtocol: tcp 317 | FromPort: 8080 318 | ToPort: 8080 319 | CidrIp: 0.0.0.0/0 320 | SecurityGroupEgress: 321 | - IpProtocol: '-1' 322 | CidrIp: 0.0.0.0/0 323 | WebappSecurityGroup: 324 | Type: AWS::EC2::SecurityGroup 325 | Properties: 326 | GroupDescription: allow access to Webapp from ALB 327 | VpcId: !Ref VPC 328 | SecurityGroupIngress: 329 | - IpProtocol: tcp 330 | FromPort: 8080 331 | ToPort: 8080 332 | SourceSecurityGroupId: 333 | Ref: ALBSecurityGroup 334 | SecurityGroupEgress: 335 | - IpProtocol: '-1' 336 | CidrIp: 0.0.0.0/0 337 | WebappDeploymentBucket: 338 | Type: AWS::S3::Bucket 339 | Properties: 340 | BucketEncryption: 341 | ServerSideEncryptionConfiguration: 342 | - ServerSideEncryptionByDefault: 343 | SSEAlgorithm: AES256 344 | PublicAccessBlockConfiguration: 345 | BlockPublicAcls: true 346 | BlockPublicPolicy: true 347 | IgnorePublicAcls: true 348 | RestrictPublicBuckets: true 349 | CodeDeployRole: 350 | Type: AWS::IAM::Role 351 | Properties: 352 | AssumeRolePolicyDocument: 353 | Version: "2012-10-17" 354 | Statement: 355 | - 356 | Effect: "Allow" 357 | Principal: 358 | Service: 359 | - "codedeploy.amazonaws.com" 360 | Action: 361 | - "sts:AssumeRole" 362 | Path: "/" 363 | ManagedPolicyArns: 364 | - arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole 365 | Policies: 366 | - 367 | PolicyName: allow-autoscaling 368 | PolicyDocument: 369 | Version: "2012-10-17" 370 | Statement: 371 | - 372 | Effect: Allow 373 | Action: 374 | - ec2:RunInstances 375 | - ec2:CreateTags 376 | - iam:PassRole 377 | Resource: 378 | - !Sub 'arn:${AWS::Partition}:codedeploy:*:${AWS::AccountId}:*' 379 | 380 | WebappInstanceProfile: 381 | Type: AWS::IAM::InstanceProfile 382 | Properties: 383 | Roles: 384 | - Ref: WebappRole 385 | WebappLaunchConfig: 386 | Type: AWS::AutoScaling::LaunchConfiguration 387 | Properties: 388 | AssociatePublicIpAddress: true 389 | ImageId: 390 | Ref: ImageId 391 | InstanceType: 392 | Ref: InstanceType 393 | SecurityGroups: 394 | - Ref: WebappSecurityGroup 395 | IamInstanceProfile: 396 | Ref: WebappInstanceProfile 397 | UserData: 398 | "Fn::Base64": 399 | !Sub | 400 | #!/bin/bash 401 | 402 | yum install -y java-1.8.0-openjdk-devel wget 403 | java -version 404 | cd /usr/local 405 | wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.43/bin/apache-tomcat-9.0.43.zip 406 | wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.43/bin/apache-tomcat-9.0.43.zip.asc 407 | wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.43/bin/apache-tomcat-9.0.43.zip.sha512 408 | 409 | # verify hash / are these two outputs the same 410 | cat apache-tomcat-9.0.43.zip.sha512 411 | sha512sum apache-tomcat-9.0.43.zip 412 | 413 | gpg --keyserver pgpkeys.mit.edu --recv-key A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 414 | gpg --verify apache-tomcat-9.0.43.zip.asc apache-tomcat-9.0.43.zip 415 | 416 | # if hash and signature are ok: 417 | unzip apache-tomcat-9.0.43.zip 418 | mv apache-tomcat-9.0.43 tomcat9 419 | echo 'JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true "' > /usr/local/tomcat9/bin/setenv.sh 420 | ls -la tomcat9/ 421 | useradd -r tomcat 422 | chown -R tomcat:tomcat /usr/local/tomcat9 423 | ls -l /usr/local/tomcat9 424 | 425 | echo "[Unit] 426 | Description=Apache Tomcat Server 427 | After=syslog.target network.target 428 | 429 | [Service] 430 | Type=forking 431 | User=tomcat 432 | Group=tomcat 433 | 434 | Environment=CATALINA_PID=/usr/local/tomcat9/temp/tomcat.pid 435 | Environment=CATALINA_HOME=/usr/local/tomcat9 436 | Environment=CATALINA_BASE=/usr/local/tomcat9 437 | 438 | ExecStart=/usr/local/tomcat9/bin/catalina.sh start 439 | ExecStop=/usr/local/tomcat9/bin/catalina.sh stop 440 | 441 | RestartSec=10 442 | Restart=always 443 | [Install] 444 | WantedBy=multi-user.target" > /etc/systemd/system/tomcat.service 445 | 446 | # firewall-cmd --zone=public --permanent --add-port=8080/tcp 447 | # firewall-cmd --zone=public --permanent --add-port=8443/tcp 448 | # firewall-cmd --reload 449 | cd /usr/local/tomcat9/bin && chmod +x catalina.sh 450 | systemctl daemon-reload 451 | systemctl start tomcat.service 452 | systemctl enable tomcat.service 453 | systemctl status tomcat.service 454 | yum install ruby -y 455 | wget https://aws-codedeploy-${AWS::Region}.s3.${AWS::Region}.amazonaws.com/latest/install 456 | chmod +x ./install 457 | ./install auto 458 | cd /tmp 459 | yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 460 | systemctl enable amazon-ssm-agent 461 | systemctl start amazon-ssm-agent 462 | 463 | AutoScalingGroup: 464 | Type: AWS::AutoScaling::AutoScalingGroup 465 | Properties: 466 | HealthCheckType: ELB 467 | HealthCheckGracePeriod: 300 468 | DesiredCapacity: !Ref AutoScalingGroupDesiredCapacity 469 | MinSize: !Ref AutoScalingGroupMinSize 470 | MaxSize: !Ref AutoScalingGroupMaxSize 471 | LaunchConfigurationName: 472 | Ref: WebappLaunchConfig 473 | VPCZoneIdentifier: 474 | - Ref: PrivateSubnet1 475 | - Ref: PrivateSubnet2 476 | TargetGroupARNs: 477 | - Ref: ALBTargetGroup 478 | Tags: 479 | - Key: Name 480 | Value: webapp-example 481 | PropagateAtLaunch: true 482 | ALBListener: 483 | Type: AWS::ElasticLoadBalancingV2::Listener 484 | Properties: 485 | DefaultActions: 486 | - 487 | Type: forward 488 | TargetGroupArn: 489 | Ref: ALBTargetGroup 490 | LoadBalancerArn: 491 | Ref: ApplicationLoadBalancer 492 | Port: 8080 493 | Protocol: HTTP 494 | ApplicationLoadBalancer: 495 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 496 | DependsOn: InternetGateway 497 | Properties: 498 | Scheme: internet-facing 499 | Subnets: 500 | - Ref: PublicSubnet1 501 | - Ref: PublicSubnet2 502 | SecurityGroups: 503 | - Ref: ALBSecurityGroup 504 | ALBTargetGroup: 505 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 506 | Properties: 507 | HealthCheckIntervalSeconds: 10 508 | UnhealthyThresholdCount: 2 509 | HealthyThresholdCount: 2 510 | HealthCheckPath: "/" 511 | Port: 8080 512 | Protocol: HTTP 513 | VpcId: !Ref VPC 514 | Outputs: 515 | 516 | WebappUrl: 517 | Description: Webapp URL 518 | Value: 519 | Fn::Join: 520 | - '' 521 | - - http:// 522 | - !GetAtt ApplicationLoadBalancer.DNSName 523 | - ':8080/SpringBootHelloWorldExampleApplication' 524 | DeploymentGroup: 525 | Description: Webapp Deployment Group 526 | Value: !Ref WebappDeploymentGroup 527 | DeploymentBucket: 528 | Description: Deployment bucket 529 | Value: !Ref WebappDeploymentBucket 530 | ApplicationName: 531 | Description: CodeDeploy Application name 532 | Value: !Ref WebappApplication 533 | GithubIAMRoleArn: 534 | Description: IAM role for GitHub 535 | Value: !GetAtt GitHubIAMRole.Arn 536 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | spring-boot-hello-world-example 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.common.project.facet.core.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.validation.validationbuilder 20 | 21 | 22 | 23 | 24 | org.springframework.ide.eclipse.core.springbuilder 25 | 26 | 27 | 28 | 29 | org.springframework.ide.eclipse.boot.validation.springbootbuilder 30 | 31 | 32 | 33 | 34 | org.eclipse.m2e.core.maven2Builder 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.ide.eclipse.core.springnature 41 | org.eclipse.jem.workbench.JavaEMFNature 42 | org.eclipse.wst.common.modulecore.ModuleCoreNature 43 | org.eclipse.jdt.core.javanature 44 | org.eclipse.m2e.core.maven2Nature 45 | org.eclipse.wst.common.project.facet.core.nature 46 | org.eclipse.wst.jsdt.core.jsNature 47 | 48 | 49 | 50 | 1638392068018 51 | 52 | 30 53 | 54 | org.eclipse.core.resources.regexFilterMatcher 55 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/test/java=UTF-8 5 | encoding//src/test/resources=UTF-8 6 | encoding/=UTF-8 7 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.apt.aptEnabled=false 3 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.compliance=1.8 5 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 6 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 7 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 8 | org.eclipse.jdt.core.compiler.processAnnotations=disabled 9 | org.eclipse.jdt.core.compiler.release=disabled 10 | org.eclipse.jdt.core.compiler.source=1.8 11 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.2.BUILD-SNAPSHOT 9 | 10 | 11 | com.springtest 12 | SpringBootHelloWorldExampleApplication 13 | 1 14 | SpringBootHelloWorldExampleApplication 15 | Demo project for Spring Boot 16 | war 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | 26 | spring-boot-starter-tomcat 27 | 28 | provided 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-parent 34 | 2.2.1.RELEASE 35 | pom 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | org.junit.vintage 49 | junit-vintage-engine 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | 62 | maven-war-plugin 63 | 64 | 3.2.3 65 | 66 | SpringBootHelloWorldExampleApplication 67 | 68 | 69 | 70 | 71 | 72 | default-war 73 | 74 | prepare-package 75 | 76 | 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | spring-milestones 93 | Spring Milestones 94 | https://repo.spring.io/milestone 95 | 96 | 97 | spring-snapshots 98 | Spring Snapshots 99 | https://repo.spring.io/snapshot 100 | 101 | true 102 | 103 | 104 | 105 | 106 | 107 | spring-milestones 108 | Spring Milestones 109 | https://repo.spring.io/milestone 110 | 111 | 112 | spring-snapshots 113 | Spring Snapshots 114 | https://repo.spring.io/snapshot 115 | 116 | true 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /spring-boot-hello-world-example/src/main/java/com/helloworld/SpringBootHelloWorldExampleApplication.java: -------------------------------------------------------------------------------- 1 | package com.helloworld; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | @SpringBootApplication 9 | public class SpringBootHelloWorldExampleApplication extends SpringBootServletInitializer 10 | { 11 | @Override 12 | 13 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 14 | 15 | return application.sources(SpringBootHelloWorldExampleApplication.class); 16 | } 17 | public static void main(String[] args) 18 | { 19 | SpringApplication.run(SpringBootHelloWorldExampleApplication.class, args); 20 | } 21 | } -------------------------------------------------------------------------------- /spring-boot-hello-world-example/src/main/java/com/helloworld/controller/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package com.helloworld.controller; 2 | import org.springframework.web.bind.annotation.RequestMapping; 3 | import org.springframework.web.bind.annotation.RestController; 4 | @RestController 5 | public class HelloWorldController 6 | { 7 | @RequestMapping("/") 8 | public String hello() 9 | { 10 | return "

Congratulations. You have successfully deployed the sample Spring Boot Application.

"; 11 | } 12 | } 13 | --------------------------------------------------------------------------------