├── .github └── dependabot.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cf-pipeline.yaml ├── codecommit-repo ├── LICENSE ├── README.md ├── buildspec.yml ├── cf-build-image.yaml ├── cf-deploy-image.yaml ├── pom.xml └── src │ └── main │ └── java │ └── hello │ ├── Application.java │ └── HelloController.java ├── create-zip-for-s3.sh └── images ├── architecture-detail.png └── architecture-high-level.png /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/codecommit-repo" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | codecommit-repo.zip 3 | -------------------------------------------------------------------------------- /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 | ## AWS Sample: Immutable Server Pipeline 2 | 3 | This code sample supports the blog post ["Create immutable servers using EC2 Image Builder and AWS CodePipeline"](https://aws.amazon.com/blogs/mt/create-immutable-servers-using-ec2-image-builder-aws-codepipeline/). The blog post contains an AWS CloudFormation quick-create link to launch the pipeline. This repository contains the source code and detailed instructions so you can customize the example pipeline. 4 | 5 | > :warning: **This AWS Sample is Archived as of 2023-03-20**: While this sample continues to be usable for quite some time, be aware that there are no more version updates to the software and libraries used in this repository. Notably: Amazon Linux runs on Amazon Linux 2 and may benefit from an update to Amazon Linux 2023 or up (at the time of writing, this is a very recent release and not yet available in EC2 Image Builder); Amazon Corretto 17 once the next LTS version comes out; Python 3.9 once AWS Lambda starts supporting newer versions; Spring Boot 3.0.4 when new releases follow (minor releases follow regularly) and POM minimum Java compatibility up from 11. 6 | 7 | ## Prerequisites 8 | 9 | Before you can install this demo pipeline, you need the following: 10 | 11 | 1. An AWS account - this demo assumes you are well-familiar with basics and core concepts of AWS, have run CloudFormation before and are comfortable with EC2, S3, VPCs and IAM. 12 | 13 | 2. The AWS command line tools with sufficient IAM permissions to create resources for IAM, EC2 Image Builder, EC2 and S3. Consider using AWS Cloud9 for convenience. 14 | 15 | 16 | 3. A name for your stack: 17 | 18 | ``` 19 | export STACK_NAME=Demo 20 | ``` 21 | 22 | 3. An S3 bucket to host a ZIP file to pre-fill the CodeCommit repository with required content 23 | 24 | ``` 25 | export BUCKET_NAME=my-bucket-name 26 | ``` 27 | 28 | 4. VPC Subnets that can be used to spin up EC2 instances. For example, to select the subnets from the default VPC: 29 | 30 | ``` 31 | export SUBNETS=$( \ 32 | echo $( \ 33 | aws ec2 describe-subnets \ 34 | --output text \ 35 | --query 'Subnets[?DefaultForAz==`true`].SubnetId' \ 36 | ) | sed "s/ /\\\,/g") 37 | ``` 38 | 39 | Alternatively, the aws cli option `--filter` adds options to select subnets other parameters such as specific tags. 40 | 41 | ## Customizing the pipeline 42 | 43 | To customize the pipeline: 44 | 45 | 1. Open a terminal window and download the code with git 46 | 47 | ``` 48 | git clone https://github.com/aws-samples/immutable-server-pipeline.git 49 | ``` 50 | 51 | 2. Open your favorite editor and modify the AWS CloudFormation templates to meet your requirements. 52 | 53 | ``` 54 | cd immutable-server-pipeline 55 | vi cf-pipeline.yaml 56 | ``` 57 | 58 | Replace `vi` with the editor of your choice. 59 | 60 | 3. Package the code as a ZIP file: 61 | 62 | ``` 63 | . create-zip-for-s3.sh 64 | ``` 65 | 66 | 4. Upload the resulting ZIP file to S3: 67 | 68 | ``` 69 | aws s3 cp codecommit-repo.zip s3://$BUCKET_NAME/codecommit-repo.zip 70 | ``` 71 | 72 | 5. Apply the CloudFormation template: 73 | 74 | ``` 75 | export STACK_ID=$( 76 | aws cloudformation create-stack \ 77 | --stack-name $STACK_NAME \ 78 | --template-body file://cf-pipeline.yaml \ 79 | --parameters \ 80 | ParameterKey=InitialCodeBucketName,ParameterValue=$BUCKET_NAME \ 81 | ParameterKey=Subnets,ParameterValue=$SUBNETS \ 82 | --capabilities CAPABILITY_IAM \ 83 | --output text \ 84 | --query StackId) 85 | aws cloudformation wait stack-create-complete --stack-name $STACK_ID 86 | ``` 87 | 88 | 6. Open the AWS Codepipeline console and verify that the pipeline is running. Expect the Build Image step to take ~25-30 minutes. 89 | 90 | 91 | ## Customizing the image 92 | 93 | To make changes to the content of the AMI: 94 | 95 | 1. Open the AWS Console for AWS CodeCommit, choose **Repositories** and select the repository corresponding to your stack name. 96 | 97 | 2. In the repository, find the file you would like to edit and choose **Edit** 98 | 99 | 3. After you are done modifying the file: 100 | 101 | - Fill in **Author name** and **Email address** 102 | - Describe the change in a **Commit message** 103 | - Choose **Commit changes** 104 | 105 | 4. Go back to the AWS Console for AWS CodePipeline and verify that a new pipeline run was initiated. Expect additional time for the 2nd run of the pipeline because of the rolling update. 106 | 107 | 108 | ## Avoiding cost by stopping the instance: 109 | 110 | To avoid cost of running EC2 instances unnecessarily, reduce the desired capacity of the AutoScaling group. 111 | 112 | 1. Find the name of the AutoScaling group generated by AWS CloudFormation: 113 | 114 | ``` 115 | export ASG_NAME=$(aws cloudformation describe-stack-resources \ 116 | --stack-name $STACK_NAME-DeployImg \ 117 | --query 'StackResources[?ResourceType==`AWS::AutoScaling::AutoScalingGroup`]'.PhysicalResourceId \ 118 | --output text) 119 | ``` 120 | 121 | 2. Change the desired capacity for the AutoScaling group: 122 | 123 | ``` 124 | aws autoscaling update-auto-scaling-group --auto-scaling-group-name $ASG_NAME --desired-capacity 0 125 | ``` 126 | 127 | ## Cleaning up: 128 | 129 | To remove all the resources created by the sample code: 130 | 131 | 1. Delete the Cloudformation stack that created an AutoScaling group and launch template: 132 | 133 | ``` 134 | aws cloudformation delete-stack --stack-name $STACK_NAME-DeployImg 135 | aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME-DeployImg 136 | ``` 137 | 138 | 2. Remove the AMIs and snapshots: 139 | 140 | - a. Verify that the specified query returns the desired images before deleting them (any images unintentionally named after your stack-name would be deleted): 141 | 142 | ``` 143 | aws ec2 describe-images --owner self --query 'Images[?Name!=`null`]|[?starts_with(Name, `'"$STACK_NAME"'-Image`) == `true`].[ImageId,Name]' --output text 144 | ``` 145 | 146 | - b. Remove the AMIs and snapshots: 147 | 148 | ``` 149 | export AMIS=$(aws ec2 describe-images --owner self --query 'Images[?Name!=`null`]|[?starts_with(Name, `'"$STACK_NAME"'-Image`) == `true`].[ImageId]' --output text) 150 | export SNAPSHOTS=$(aws ec2 describe-images --owner self --query 'Images[?Name!=`null`]|[?starts_with(Name, `'"$STACK_NAME"'-Image`) == `true`].[BlockDeviceMappings[0].Ebs.SnapshotId]' --output text) 151 | ARR=($(echo "$AMIS" | tr ' ' '\n')); for i in ${ARR[@]}; do aws ec2 deregister-image --image-id $i; done 152 | ARR=($(echo "$SNAPSHOTS" | tr ' ' '\n')); for i in ${ARR[@]}; do aws ec2 delete-snapshot --snapshot-id $i; done 153 | ``` 154 | 155 | 3. Delete the CloudFormation stack that created the EC2 Image Builder resources to build the AMI: 156 | 157 | ``` 158 | aws cloudformation delete-stack --stack-name $STACK_NAME-BuildImg 159 | aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME-BuildImg 160 | ``` 161 | 162 | 4. Remove the S3 bucket with artifacts from the pipeline: 163 | 164 | ``` 165 | export ARTIFACT_BUCKET=$(aws cloudformation describe-stack-resources \ 166 | --stack-name $STACK_NAME \ 167 | --query 'StackResources[?ResourceType==`AWS::S3::Bucket`].PhysicalResourceId' \ 168 | --output text) 169 | aws s3 rm s3://$ARTIFACT_BUCKET/ --recursive 170 | aws s3api delete-bucket --bucket $ARTIFACT_BUCKET 171 | ``` 172 | 173 | 5. Delete the original CloudFormation stack: 174 | 175 | ``` 176 | aws cloudformation delete-stack --stack-name $STACK_NAME 177 | aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME 178 | ``` 179 | 180 | 6. Remove the version number for the images: 181 | 182 | ``` 183 | aws ssm delete-parameter --name /$STACK_NAME/ImageVersion 184 | ``` 185 | 186 | ## Relevant files in this repository 187 | 188 | This repository contains multiple files and folders. For the purpose of understanding how this pipeline works, these files are relevant in particular: 189 | 190 | - cf-pipeline.yaml – The CloudFormation template defining the overall AWS CodePipeline 191 | - codecommit-repo/ – Pre-packaged content for the AWS CodeCommit repository: 192 | - src/ – Source code for Stage 1 of the pipeline (Hello World in Java with Spring Boot) 193 | - cf-build-image.yaml – AWS CloudFormation template for Stage 2 of the pipeline 194 | - cf-deploy-image.yaml – The AWS CloudFormation template for Stage 3 of the pipeline 195 | 196 | ## Detailed overview of resources created by AWS CloudFormation 197 | 198 | The [blog post](https://aws.amazon.com/blogs/mt/create-immutable-servers-using-ec2-image-builder-aws-codepipeline/) gives an explanation of the high level architecture for this pipeline that builds AMIs. For your convenience, here is the illustration: 199 | 200 | ![High Level Architecture](images/architecture-high-level.png) 201 | 202 | If we take the high-level architecture and dive one level deeper into what is happening, we notice there is a relatively large number of IAM Roles (each with their own Policies) required to make the pipeline work. To understand the purpose of each role, here is an alternative version of the architecture that focuses on all the roles being created: 203 | 204 | ![Detailed Architecture](images/architecture-detail.png) 205 | 206 | Below is a description for each role in the diagram. Note that you will find the same description in the comments in the CloudFormation template. 207 | 208 | | Role Name | Description | 209 | | --- | --- | 210 | | DemoPipelineRole | Role assumed by AWS Codepipeline overall, to access S3, CodeCommit, CodeBuild and for AWS Codepipeline actions to assume roles to invoke AWS Lambda and AWS Cloudformation templates | 211 | | InitiatePipelineRole | Role assumed by AWS CloudWatch Event Rules to start AWS Codepipeline upon a commit to the main branch in AWS CodeCommit | 212 | | CodeBuildRole | Role assumed by AWS CodeBuild to perform the compilation | 213 | | BuildImgParamLambdaRole | Role assumed by AWS Lambda function itself to prepare parameters for the next stage | 214 | | BuildImgParamActionRole | Role assumed by the specific action in CodePipeline that calls the AWS Lambda that prepares parameters for the next stage | 215 | | BuildImgCFRole | Role assumed by AWS CloudFormation to call EC2 Image Builder and build the AMI | 216 | | BuildImgCreateActionRole | Role assumed by the specific action in CodePipeline that calls CloudFormation to create a change set for calling EC2 Image Builder | 217 | | BuildImgApplyActionRole | Role assumed by the specific action in CodePipeline that calls CloudFormation to apply a change set for calling EC2 Image Builder | 218 | | DeployImgCFRole | Role assumed by AWS CloudFormation to create a LaunchTemplate version and perform a rolling deployment on the AutoScalingGroup | 219 | | DeployImgCreateActionRole | Role assumed by the specific action in CodePipeline that calls CloudFormation to create a change set for rolling out the AMI | 220 | | DeployImgApplyActionRole | Role assumed by the specific action in CodePipeline that calls CloudFormation to apply a change set for rolling out the AMI | 221 | | BuildImgEC2Role | Role assumed by temporary EC2 instance used by EC2 Image Builder while building the new AMI | 222 | | AWSServiceRoleForImageBuilder | Service Linked Role for EC2 Image Builder. If EC2 Image Builder has not yet been used before in the account, EC2 Image Builder will need permissions to create this role and hence iam:CreateServiceLinkedRole permissions are provided for EC2 Image Builder. | 223 | | AWSServiceRoleForAutoScaling | Service Linked Role for AutoScaling. If Auto Scaling has not yet been used before in the account, AutoScaling will need permissions to create this role and hence iam:CreateServiceLinkedRole permissions are provided for Autoscaling. | 224 | 225 | 226 | ## Known limitations 227 | 228 | - The Spring Boot version used in the Java code sample is not necessarily up-to-date with the latest versions or security patches. Update the demo Spring Boot application to the latest version of Spring Boot before opening up access to the outside world. 229 | - The final step of the pipeline creates an AutoScaling group. The demo does _not_ create an Application Load Balancer. Create an Application Load Balancer to review the result of the demo in a browser. It is recommended to limit access to only your own IP initially. 230 | - The installation script for the AMI is built-in in the AWS CloudFormation template and stored in the same AWS CodeCommit repository as the Java source code. If you want to maintain this script separately, you can extract it into a separate file. You can even create a sparate AWS CodeCommit repository for all infrastructure related code and add it as an additional input source to the AWS Codepipeline 231 | - The pipeline creates an AMI in a single AWS Region. If you want to distribute AMIs across multiple regions, you can adopt additional constructs from EC2 Image Builder like distribution configuration. 232 | - The version number is generated from an AWS Systems Manager Parameter Store parameter. You can replace this mechanism with an alternative of your choice. For example, some teams have a version number in the source code itself. Please be aware that this pipeline requires the versions to be unique for each iteration. 233 | - The pipeline does a single deployment to a single environment. In order to extend the pipeline to subsequent environments (that is quality assurance, production) it is recommended to create new, separate LaunchTemplates with their own versions for each environment. This way you keep environment specific metadata separated while reusing the exact same AMI across environments. 234 | 235 | ## Security 236 | 237 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 238 | 239 | ## License 240 | 241 | This library is licensed under the MIT-0 License. See the LICENSE file. 242 | 243 | -------------------------------------------------------------------------------- /cf-pipeline.yaml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | Parameters: 4 | InitialCodeBucketName: 5 | Type: "String" 6 | Default: "your-bucket-name-here" 7 | Description: S3 Bucket containing build artifacts; should come from the CodeBuild step in Codepipeline 8 | InitialCodeObjectKey: 9 | Type: String 10 | Default: "codecommit-repo.zip" 11 | Description: Specific reference to the build file within the bucket; should come from the CodeBuild step in Codepipeline 12 | Subnets: 13 | Type: List 14 | Description: Select target subnets for autoscaling group at the end of the pipeline. These will be exported across CloudFormation stacks to avoid manual intervention at later stages in the pipeline. 15 | Architecture: 16 | Type: String 17 | Default: arm64 18 | AllowedValues: 19 | - x86 20 | - arm64 21 | Description: Select between Intel/AMD (x86) and ARM architecture. Defaults to Arm 22 | InstanceType: 23 | Type: String 24 | Default: t4g.nano 25 | AllowedValues: 26 | - t2.micro 27 | - t3.nano 28 | - t3a.nano 29 | - t4g.nano 30 | - t4g.micro 31 | - m5.large 32 | - m5a.large 33 | - m6g.medium 34 | - m6g.large 35 | - c7g.medium 36 | - c7g.large 37 | Description: For x86, select t3/m5(a). For ARM, select t4g/m6g instances 38 | Resources: 39 | # The CI/CD pipeline stitching the full mechanism together 40 | DemoPipeline: 41 | Type: AWS::CodePipeline::Pipeline 42 | Properties: 43 | Name: !Sub "${AWS::StackName}-Pipeline" 44 | RoleArn: !GetAtt DemoPipelineRole.Arn 45 | Stages: 46 | - Actions: 47 | # Initiate Pipeline from CodeCommit 48 | - ActionTypeId: 49 | Version: '1' 50 | Provider: CodeCommit 51 | Category: Source 52 | Owner: AWS 53 | OutputArtifacts: 54 | - Name: source 55 | InputArtifacts: [] 56 | Name: source 57 | Configuration: 58 | RepositoryName: !Sub "${AWS::StackName}-Repository" 59 | BranchName: main 60 | PollForSourceChanges: 'false' 61 | RunOrder: 1 62 | Namespace: SourceVars 63 | Name: Initiate 64 | - Actions: 65 | # Build Java code with CodeBuild 66 | - ActionTypeId: 67 | Category: Build 68 | Owner: AWS 69 | Provider: CodeBuild 70 | Version: "1" 71 | Configuration: 72 | ProjectName: !Ref DemoCodeBuild 73 | InputArtifacts: 74 | - Name: source 75 | Name: build 76 | OutputArtifacts: 77 | - Name: jar 78 | RunOrder: 1 79 | Namespace: BuildVars 80 | Name: BuildCode 81 | - Actions: 82 | # Prepare input parameters using AWS Lambda 83 | - ActionTypeId: 84 | Category: Invoke 85 | Owner: AWS 86 | Provider: Lambda 87 | Version: "1" 88 | Configuration: 89 | FunctionName: 90 | Ref: BuildImgParamLambda 91 | UserParameters: !Sub "/${AWS::StackName}/ImageVersion" 92 | InputArtifacts: 93 | - Name: jar 94 | Name: convert_build_img_params 95 | RoleArn: !GetAtt BuildImgParamActionRole.Arn 96 | RunOrder: 1 97 | Namespace: BuildImgParamVars 98 | # Create Change Set for AWS CloudFormation which in turn uses EC2 Image Builder to create a new AMI 99 | - ActionTypeId: 100 | Category: Deploy 101 | Owner: AWS 102 | Provider: CloudFormation 103 | Version: "1" 104 | Configuration: 105 | StackName: !Sub "${AWS::StackName}-BuildImg" 106 | RoleArn: !GetAtt BuildImgCFRole.Arn 107 | ParameterOverrides: !Sub 108 | - "{${Map}}" 109 | - "Map": !Join 110 | - "," 111 | - - '"BucketName": "#{BuildImgParamVars.BucketName}"' 112 | - '"ObjectKey": "#{BuildImgParamVars.ObjectKey}"' 113 | - '"Version": "#{BuildImgParamVars.Version}"' 114 | - !Sub '"InstanceProfile": "${BuildImgEC2InstanceProfile}"' 115 | - !Sub '"Architecture": "${Architecture}"' 116 | - !Sub '"InstanceType": "${InstanceType}"' 117 | ActionMode: CHANGE_SET_REPLACE 118 | ChangeSetName: buildimage 119 | TemplatePath: source::cf-build-image.yaml 120 | InputArtifacts: 121 | - Name: source 122 | Name: create_change_set 123 | RoleArn: !GetAtt BuildImgCreateActionRole.Arn 124 | RunOrder: 2 125 | Namespace: BuildImgCFParamCreateVars 126 | # Apply Change Set for AWS CloudFormation which in turn uses EC2 Image Builder to create a new AMI 127 | - ActionTypeId: 128 | Category: Deploy 129 | Owner: AWS 130 | Provider: CloudFormation 131 | Version: "1" 132 | Configuration: 133 | StackName: !Sub "${AWS::StackName}-BuildImg" 134 | ActionMode: CHANGE_SET_EXECUTE 135 | ChangeSetName: buildimage 136 | Name: apply_change_set 137 | RoleArn: !GetAtt BuildImgApplyActionRole.Arn 138 | RunOrder: 3 139 | Namespace: BuildImgCFParamApplyVars 140 | Name: BuildImage 141 | - Actions: 142 | # Create Change Set for AWS CloudFormation which in turn Deploys the new AMI through an AutoScalingGroup 143 | - ActionTypeId: 144 | Category: Deploy 145 | Owner: AWS 146 | Provider: CloudFormation 147 | Version: "1" 148 | Configuration: 149 | StackName: !Sub "${AWS::StackName}-DeployImg" 150 | RoleArn: !GetAtt DeployImgCFRole.Arn 151 | ParameterOverrides: !Sub 152 | - "{${Map}}" 153 | - "Map": !Join 154 | - "," 155 | - - '"AmiId": "#{BuildImgCFParamApplyVars.DemoImageId}"' 156 | - '"Version": "#{BuildImgParamVars.Version}"' 157 | - '"SourceAuthorDate": "#{SourceVars.AuthorDate}"' 158 | - '"SourceBranchName": "#{SourceVars.BranchName}"' 159 | - '"SourceCommitId": "#{SourceVars.CommitId}"' 160 | - '"SourceCommitMessage": "#{SourceVars.CommitMessage}"' 161 | - '"SourceCommitterDate": "#{SourceVars.CommitterDate}"' 162 | - '"SourceRepositoryName": "#{SourceVars.RepositoryName}"' 163 | - !Sub 164 | - '"Subnets": "${JoinSubnets}"' 165 | - 'JoinSubnets': !Join [",", !Ref Subnets] 166 | - !Sub '"InstanceType": "${InstanceType}"' 167 | ActionMode: CHANGE_SET_REPLACE 168 | ChangeSetName: deployimage 169 | TemplatePath: source::cf-deploy-image.yaml 170 | InputArtifacts: 171 | - Name: source 172 | Name: create_change_set 173 | RoleArn: !GetAtt DeployImgCreateActionRole.Arn 174 | RunOrder: 1 175 | Namespace: DeployImgCFParamCreateVars 176 | # Apply Change Set for AWS CloudFormation which in turn Deploys the new AMI through an AutoScalingGroup 177 | - ActionTypeId: 178 | Category: Deploy 179 | Owner: AWS 180 | Provider: CloudFormation 181 | Version: "1" 182 | Configuration: 183 | StackName: !Sub "${AWS::StackName}-DeployImg" 184 | ActionMode: CHANGE_SET_EXECUTE 185 | ChangeSetName: deployimage 186 | Name: apply_change_set 187 | RoleArn: !GetAtt DeployImgApplyActionRole.Arn 188 | RunOrder: 2 189 | Namespace: DeployImgCFParamApplyVars 190 | Name: DeployImage 191 | ArtifactStore: 192 | Location: !Ref DemoArtifactBucket 193 | Type: S3 194 | DependsOn: 195 | - DemoPipelinePolicy 196 | # Git repository containing Hello World level Java code for demo purposes 197 | DemoRepository: 198 | Type: AWS::CodeCommit::Repository 199 | Properties: 200 | Code: 201 | S3: 202 | Bucket: !Ref InitialCodeBucketName 203 | Key: !Ref InitialCodeObjectKey 204 | RepositoryDescription: !Sub "Hello World Spring Boot application to demo in ${AWS::StackName} pipeline" 205 | RepositoryName: !Sub "${AWS::StackName}-Repository" 206 | # AWS CodeBuild to facilitate Maven Build process of the Java project. 207 | DemoCodeBuild: 208 | Type: AWS::CodeBuild::Project 209 | Properties: 210 | Artifacts: 211 | Type: CODEPIPELINE 212 | Environment: 213 | ComputeType: BUILD_GENERAL1_SMALL 214 | Image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 215 | PrivilegedMode: false 216 | Type: LINUX_CONTAINER 217 | Name: !Sub "${AWS::StackName}-Build" 218 | ServiceRole: !GetAtt CodeBuildRole.Arn 219 | Source: 220 | Type: CODEPIPELINE 221 | # Lambda function that takes the reference to the output from the 222 | # CodeBuild project (an S3 reference) and passes it on as AWS 223 | # CodePipeline Paratemeters to be injected into the next Cloud- 224 | # Formation stage. It also calculates the latest version number 225 | # and passes it as a parameter. 226 | BuildImgParamLambda: 227 | Type: AWS::Lambda::Function 228 | Properties: 229 | Code: 230 | # Contents of Lambda inline as it is sufficiently small 231 | # this saves the complexity of additional S3 buckets needed 232 | ZipFile: | 233 | import boto3 234 | 235 | codepipeline = boto3.client('codepipeline') 236 | ssm = boto3.client('ssm') 237 | 238 | # Constants for Parameter Version 239 | default_version = '1.0.0' 240 | 241 | # This Lambda takes the reference to the input artifact (typically from 242 | # CodeBuild but could be any) and transforms it into a template_config.json 243 | # file that can be used as parameters file for CloudFormation. 244 | def handler(event, context): 245 | job = event['CodePipeline.job'] 246 | 247 | try: 248 | # Read SSM Key Name from User Parameters passed to Lambda 249 | key_name = job['data']['actionConfiguration']['configuration']['UserParameters'] 250 | 251 | # Read reference to input artifact 252 | input_artifacts = read_artifacts(job['data']['inputArtifacts']) 253 | 254 | # Get recent version and increase by one 255 | version = get_next_version(key_name) 256 | 257 | # Then, tell CodePipeline the job succeeded, returning output variables 258 | codepipeline.put_job_success_result( 259 | jobId=job['id'], 260 | executionDetails={ 261 | 'summary': 'Automation job succeeded' 262 | }, 263 | outputVariables={ 264 | "BucketName": input_artifacts['bucket_name'], 265 | "ObjectKey": input_artifacts['object_key'], 266 | "Version": version 267 | } 268 | ) 269 | except Exception as e: 270 | # In case of any exception, tell CodePipeline the job FAILED 271 | print(e) 272 | codepipeline.put_job_failure_result(jobId=job['id'], failureDetails={ 273 | 'message': 'Automation job failed with Exception', 274 | 'type': 'JobFailed' 275 | }) 276 | return 277 | 278 | # Utility function for extracting input/output artifact references from 279 | # CodePipeline 280 | def read_artifacts(artifacts): 281 | result = { 282 | 'name': 'unknown', 283 | 'object_key': 'unknown', 284 | 'bucket_name': 'unknown' 285 | } 286 | for artifact in artifacts: 287 | if 'location' in artifact.keys(): 288 | location = artifact['location'] 289 | if location['type'] == 'S3': 290 | result['object_key'] = location['s3Location']['objectKey'] 291 | result['bucket_name'] = location['s3Location']['bucketName'] 292 | 293 | elif 'name' in artifact.keys(): 294 | result['name'] = artifact['name'] 295 | 296 | return result 297 | 298 | # Read current version from SSM Parameter Store (if any), increase and store back 299 | def get_next_version(key_name): 300 | param_exists = ssm.describe_parameters( 301 | ParameterFilters=[ 302 | { 303 | 'Key': 'Name', 304 | 'Values': [key_name] 305 | } 306 | ]) 307 | 308 | current_version = default_version 309 | if len(param_exists['Parameters']) > 0: 310 | param = ssm.get_parameter(Name=key_name) 311 | current_version = param['Parameter']['Value'] 312 | 313 | new_version = increment_version(current_version) 314 | 315 | ssm.put_parameter( 316 | Name=key_name, 317 | Value=new_version, 318 | Type='String', 319 | Overwrite=True 320 | ) 321 | 322 | return current_version 323 | 324 | # Utility function to increase version by 1 325 | def increment_version(current_version): 326 | version_no = current_version.split('.') 327 | version_no[len(version_no) - 1] = str(int(version_no[len(version_no) - 1]) + 1) 328 | return '.'.join(version_no) 329 | Handler: index.handler 330 | Role: !GetAtt BuildImgParamLambdaRole.Arn 331 | Runtime: python3.9 332 | Architectures: 333 | - arm64 334 | Timeout: 30 335 | # S3 Bucket to contain the build artifacts from CodeBuild in order to 336 | # hand them over to CloudFormation 337 | DemoArtifactBucket: 338 | Type: AWS::S3::Bucket 339 | Properties: 340 | BucketEncryption: 341 | ServerSideEncryptionConfiguration: 342 | - ServerSideEncryptionByDefault: 343 | SSEAlgorithm: 'AES256' 344 | VersioningConfiguration: 345 | Status: Enabled 346 | # Take care: After deleting the CloudFormation template, this bucket 347 | # is retained. So full clean up requires manual steps. The alternative 348 | # of a Delete policy is practically not better since it still requires 349 | # manual steps to empty the bucket to avoid error messages upon 350 | # template removal. 351 | UpdateReplacePolicy: Retain 352 | DeletionPolicy: Retain 353 | # Role assumed by AWS Codepipeline overall; see the 354 | # policy for detail 355 | InitiatePipelineRule: 356 | Type: AWS::Events::Rule 357 | Properties: 358 | EventPattern: 359 | source: 360 | - aws.codecommit 361 | detail-type: 362 | - 'CodeCommit Repository State Change' 363 | resources: 364 | - !Sub "arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${AWS::StackName}-Repository" 365 | detail: 366 | event: 367 | - referenceCreated 368 | - referenceUpdated 369 | referenceType: 370 | - branch 371 | referenceName: 372 | - main 373 | Targets: 374 | - Arn: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${DemoPipeline}" 375 | RoleArn: !GetAtt InitiatePipelineRole.Arn 376 | Id: !Sub "${AWS::StackName}-InitiatePipeline" 377 | # Role assumed by AWS Codepipeline overall, allowing access to S3, CodeCommit, 378 | # CodeBuild and allowing AWS Codepipeline actions to assume roles to initiate 379 | # AWS Lambda and AWS Cloudformation templates 380 | DemoPipelineRole: 381 | Type: AWS::IAM::Role 382 | Properties: 383 | AssumeRolePolicyDocument: 384 | Version: "2012-10-17" 385 | Statement: 386 | - Action: sts:AssumeRole 387 | Effect: Allow 388 | Principal: 389 | Service: codepipeline.amazonaws.com 390 | # Role assumed by AWS CodeBuild to perform the compilation; see the 391 | # attached policy for detail 392 | CodeBuildRole: 393 | Type: AWS::IAM::Role 394 | Properties: 395 | AssumeRolePolicyDocument: 396 | Version: "2012-10-17" 397 | Statement: 398 | - Action: sts:AssumeRole 399 | Effect: Allow 400 | Principal: 401 | Service: codebuild.amazonaws.com 402 | # Role assumed by AWS Lambda function itself to prepare parameters 403 | # for the next stage 404 | BuildImgParamLambdaRole: 405 | Type: AWS::IAM::Role 406 | Properties: 407 | AssumeRolePolicyDocument: 408 | Version: "2012-10-17" 409 | Statement: 410 | - Action: sts:AssumeRole 411 | Effect: Allow 412 | Principal: 413 | Service: lambda.amazonaws.com 414 | ManagedPolicyArns: 415 | - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 416 | # Role assumed by the specific action in CodePipeline that calls the 417 | # AWS Lambda that prepares parameters for the next stage 418 | BuildImgParamActionRole: 419 | Type: AWS::IAM::Role 420 | Properties: 421 | AssumeRolePolicyDocument: 422 | Version: "2012-10-17" 423 | Statement: 424 | - Action: sts:AssumeRole 425 | Effect: Allow 426 | Principal: 427 | AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" 428 | # Role assumed by AWS CloudFormation to call EC2 Image Builder and 429 | # build the AMI 430 | BuildImgCFRole: 431 | Type: AWS::IAM::Role 432 | Properties: 433 | AssumeRolePolicyDocument: 434 | Version: "2012-10-17" 435 | Statement: 436 | - Action: sts:AssumeRole 437 | Effect: Allow 438 | Principal: 439 | Service: cloudformation.amazonaws.com 440 | # Role assumed by the specific action in CodePipeline that calls 441 | # CloudFormation to GENERATE a change set for calling EC2 Image Builder 442 | BuildImgCreateActionRole: 443 | Type: AWS::IAM::Role 444 | Properties: 445 | AssumeRolePolicyDocument: 446 | Version: "2012-10-17" 447 | Statement: 448 | - Action: sts:AssumeRole 449 | Effect: Allow 450 | Principal: 451 | AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" 452 | # Role assumed by the specific action in CodePipeline that calls 453 | # CloudFormation to apply a change set for calling EC2 Image Builder 454 | BuildImgApplyActionRole: 455 | Type: AWS::IAM::Role 456 | Properties: 457 | AssumeRolePolicyDocument: 458 | Version: "2012-10-17" 459 | Statement: 460 | - Action: sts:AssumeRole 461 | Effect: Allow 462 | Principal: 463 | AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" 464 | # Role assumed by AWS CloudFormation to create a LaunchTemplate version 465 | # and perform a rolling deployment on the AutoScalingGroup 466 | DeployImgCFRole: 467 | Type: AWS::IAM::Role 468 | Properties: 469 | AssumeRolePolicyDocument: 470 | Version: "2012-10-17" 471 | Statement: 472 | - Action: sts:AssumeRole 473 | Effect: Allow 474 | Principal: 475 | Service: cloudformation.amazonaws.com 476 | ManagedPolicyArns: 477 | - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSImageBuilderReadOnlyAccess" 478 | # Role assumed by the specific action in CodePipeline that calls 479 | # CloudFormation to GENERATE a change set for rolling out the AMI 480 | DeployImgCreateActionRole: 481 | Type: AWS::IAM::Role 482 | Properties: 483 | AssumeRolePolicyDocument: 484 | Version: "2012-10-17" 485 | Statement: 486 | - Action: sts:AssumeRole 487 | Effect: Allow 488 | Principal: 489 | AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" 490 | # Role assumed by the specific action in CodePipeline that calls 491 | # CloudFormation to apply a change set for rolling out the AMI 492 | DeployImgApplyActionRole: 493 | Type: AWS::IAM::Role 494 | Properties: 495 | AssumeRolePolicyDocument: 496 | Version: "2012-10-17" 497 | Statement: 498 | - Action: sts:AssumeRole 499 | Effect: Allow 500 | Principal: 501 | AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" 502 | # Role assumed by temporary EC2 instance used by EC2 Image Builder while 503 | # building the new AMI 504 | BuildImgEC2Role: 505 | Type: AWS::IAM::Role 506 | Properties: 507 | AssumeRolePolicyDocument: 508 | Version: "2012-10-17" 509 | Statement: 510 | - Action: sts:AssumeRole 511 | Effect: Allow 512 | Principal: 513 | Service: !Sub "ec2.${AWS::URLSuffix}" 514 | ManagedPolicyArns: 515 | - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" 516 | - !Sub "arn:${AWS::Partition}:iam::aws:policy/EC2InstanceProfileForImageBuilder" 517 | # Role assumed by AWS CloudWatch Event Rules to initiate AWS Codepipeline 518 | # upon a commit to the main branch in AWS CodeCommit 519 | InitiatePipelineRole: 520 | Type: AWS::IAM::Role 521 | Properties: 522 | AssumeRolePolicyDocument: 523 | Version: "2012-10-17" 524 | Statement: 525 | - Action: sts:AssumeRole 526 | Effect: Allow 527 | Principal: 528 | Service: 529 | - events.amazonaws.com 530 | # EC2 Instance Profile to allow emporary EC2 instance used by EC2 Image Builder 531 | # to assume its role 532 | BuildImgEC2InstanceProfile: 533 | Type: AWS::IAM::InstanceProfile 534 | Properties: 535 | Roles: 536 | - !Ref BuildImgEC2Role 537 | # IAM Policy for AWS Codepipeline overall; mainly to allow access to 538 | # CodeCommit, CodeBuild, the S3 bucket for artifacts and permission 539 | # to assume the respective roles for lambda and cloudformation actions 540 | DemoPipelinePolicy: 541 | Type: AWS::IAM::ManagedPolicy 542 | Properties: 543 | PolicyDocument: 544 | Version: "2012-10-17" 545 | Statement: 546 | - Action: 547 | - codebuild:BatchGetBuilds 548 | - codebuild:StartBuild 549 | - codebuild:StopBuild 550 | Effect: Allow 551 | Resource: !GetAtt DemoCodeBuild.Arn 552 | - Action: 553 | - codecommit:CancelUploadArchive 554 | - codecommit:GetBranch 555 | - codecommit:GetCommit 556 | - codecommit:GetUploadArchiveStatus 557 | - codecommit:UploadArchive 558 | Effect: Allow 559 | Resource: !GetAtt DemoRepository.Arn 560 | - Action: 561 | - s3:GetObject 562 | - s3:ListBucket 563 | - s3:PutObject 564 | Effect: Allow 565 | Resource: 566 | - !GetAtt DemoArtifactBucket.Arn 567 | - !Sub "${DemoArtifactBucket.Arn}/*" 568 | - Action: sts:AssumeRole 569 | Effect: Allow 570 | Resource: !GetAtt BuildImgParamActionRole.Arn 571 | - Action: sts:AssumeRole 572 | Effect: Allow 573 | Resource: !GetAtt BuildImgCFRole.Arn 574 | - Action: sts:AssumeRole 575 | Effect: Allow 576 | Resource: !GetAtt BuildImgCreateActionRole.Arn 577 | - Action: sts:AssumeRole 578 | Effect: Allow 579 | Resource: !GetAtt BuildImgApplyActionRole.Arn 580 | - Action: sts:AssumeRole 581 | Effect: Allow 582 | Resource: !GetAtt DeployImgCFRole.Arn 583 | - Action: sts:AssumeRole 584 | Effect: Allow 585 | Resource: !GetAtt DeployImgCreateActionRole.Arn 586 | - Action: sts:AssumeRole 587 | Effect: Allow 588 | Resource: !GetAtt DeployImgApplyActionRole.Arn 589 | Roles: 590 | - Ref: DemoPipelineRole 591 | # Policy for AWS CodeBuild to perform the compilation; mainly needs 592 | # access to S3 bucket for storing artifacts and to CloudWatch Logs 593 | CodeBuildPolicy: 594 | Type: AWS::IAM::ManagedPolicy 595 | Properties: 596 | PolicyDocument: 597 | Version: "2012-10-17" 598 | Statement: 599 | - Action: 600 | - logs:CreateLogGroup 601 | - logs:CreateLogStream 602 | - logs:PutLogEvents 603 | Effect: Allow 604 | Resource: 605 | - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${DemoCodeBuild}" 606 | - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${DemoCodeBuild}:*" 607 | - Action: 608 | - s3:GetObject 609 | - s3:ListBucket 610 | - s3:PutObject 611 | Effect: Allow 612 | Resource: 613 | - !GetAtt DemoArtifactBucket.Arn 614 | - !Sub "${DemoArtifactBucket.Arn}/*" 615 | Roles: 616 | - Ref: CodeBuildRole 617 | # Policy for the specific action in CodePipeline that calls the 618 | # AWS Lambda that prepares parameters for the next stage. Uses SSM 619 | # Paraemeter store to keep the latest version number. Posts result 620 | # back to CodePipeline. 621 | BuildImgParamLambdaPolicy: 622 | Type: AWS::IAM::ManagedPolicy 623 | Properties: 624 | PolicyDocument: 625 | Version: "2012-10-17" 626 | Statement: 627 | - Action: 628 | - codepipeline:PutJobSuccessResult 629 | - codepipeline:PutJobFailureResult 630 | Effect: Allow 631 | Resource: "*" 632 | - Action: 633 | - ssm:DescribeParameters 634 | Effect: Allow 635 | Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:*" 636 | - Action: 637 | - ssm:PutParameter 638 | - ssm:GetParameter 639 | Effect: Allow 640 | Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/ImageVersion" 641 | Roles: 642 | - Ref: BuildImgParamLambdaRole 643 | # Policy for the specific action in CodePipeline that calls the 644 | # AWS Lambda that prepares parameters for the next stage 645 | BuildImgParamActionPolicy: 646 | Type: AWS::IAM::ManagedPolicy 647 | Properties: 648 | PolicyDocument: 649 | Version: "2012-10-17" 650 | Statement: 651 | - Action: lambda:InvokeFunction 652 | Effect: Allow 653 | Resource: !GetAtt BuildImgParamLambda.Arn 654 | - Action: lambda:ListFunctions 655 | Effect: Allow 656 | Resource: "*" 657 | Roles: 658 | - Ref: BuildImgParamActionRole 659 | # Policy for AWS CloudFormation to call EC2 Image Builder and 660 | # build the AMI 661 | BuildImgCFPolicy: 662 | Type: AWS::IAM::ManagedPolicy 663 | Properties: 664 | PolicyDocument: 665 | Version: "2012-10-17" 666 | Statement: 667 | - Action: 668 | - iam:CreateServiceLinkedRole 669 | Effect: Allow 670 | Resource: 671 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/imagebuilder.amazonaws.com/AWSServiceRoleForImageBuilder" 672 | Condition: 673 | StringLike: 674 | iam:AWSServiceName: "imagebuilder.amazonaws.com" 675 | - Action: 676 | - iam:GetInstanceProfile 677 | Effect: Allow 678 | Resource: 679 | - !GetAtt BuildImgEC2InstanceProfile.Arn 680 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:instance-profile/*imagebuilder*" 681 | - Action: 682 | - iam:GetRole 683 | Effect: Allow 684 | Resource: 685 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/imagebuilder.amazonaws.com/AWSServiceRoleForImageBuilder" 686 | - Action: 687 | - iam:ListInstanceProfiles 688 | - iam:ListRoles 689 | Effect: Allow 690 | Resource: "*" 691 | - Action: 692 | - iam:PassRole 693 | Effect: Allow 694 | Resource: 695 | - !GetAtt BuildImgEC2Role.Arn 696 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:instance-profile/*imagebuilder*" 697 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*imagebuilder*" 698 | Condition: 699 | StringEquals: 700 | iam:PassedToService: "ec2.amazonaws.com" 701 | - Action: 702 | - imagebuilder:GetImage 703 | - imagebuilder:GetComponent 704 | - imagebuilder:CreateImage 705 | - imagebuilder:GetInfrastructureConfiguration 706 | - imagebuilder:DeleteImage 707 | - imagebuilder:CreateComponent 708 | - imagebuilder:DeleteComponent 709 | - imagebuilder:GetImageRecipe 710 | - imagebuilder:CreateImageRecipe 711 | - imagebuilder:DeleteImageRecipe 712 | - imagebuilder:CreateInfrastructureConfiguration 713 | - imagebuilder:DeleteInfrastructureConfiguration 714 | - imagebuilder:UpdateInfrastructureConfiguration 715 | - imagebuilder:TagResource 716 | - imagebuilder:UntagResource 717 | Effect: Allow 718 | Resource: 719 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:${AWS::AccountId}:image/*/*" 720 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:*:image/amazon-linux-2-*/*/*" 721 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:${AWS::AccountId}:image-recipe/*/*" 722 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:${AWS::AccountId}:component/*/*" 723 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:${AWS::AccountId}:component/*/*/*" 724 | - !Sub "arn:${AWS::Partition}:imagebuilder:${AWS::Region}:${AWS::AccountId}:infrastructure-configuration/*" 725 | - Action: 726 | - ec2:DescribeInstances 727 | - ec2:DescribeImages 728 | - ec2:DescribeTags 729 | - ec2:DescribeSnapshots 730 | Effect: Allow 731 | Resource: "*" 732 | Roles: 733 | - Ref: BuildImgCFRole 734 | # Policy for the specific action in CodePipeline that calls 735 | # CloudFormation to GENERATE a change set for rolling out the AMI 736 | BuildImgCreateActionPolicy: 737 | Type: AWS::IAM::ManagedPolicy 738 | Properties: 739 | PolicyDocument: 740 | Version: "2012-10-17" 741 | Statement: 742 | - Action: 743 | - cloudformation:CreateChangeSet 744 | - cloudformation:DeleteChangeSet 745 | - cloudformation:DescribeChangeSet 746 | - cloudformation:DescribeStacks 747 | Condition: 748 | StringEqualsIfExists: 749 | cloudformation:ChangeSetName: buildimage 750 | Effect: Allow 751 | Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}-BuildImg/*" 752 | - Action: iam:PassRole 753 | Effect: Allow 754 | Resource: !GetAtt BuildImgCFRole.Arn 755 | - Action: 756 | - s3:GetObject 757 | - s3:ListBucket 758 | Effect: Allow 759 | Resource: 760 | - !GetAtt DemoArtifactBucket.Arn 761 | - !Sub "${DemoArtifactBucket.Arn}/*" 762 | Roles: 763 | - Ref: BuildImgCreateActionRole 764 | # Policy for the specific action in CodePipeline that calls 765 | # CloudFormation to apply a change set for calling EC2 Image Builder 766 | BuildImgApplyActionPolicy: 767 | Type: AWS::IAM::ManagedPolicy 768 | Properties: 769 | PolicyDocument: 770 | Version: "2012-10-17" 771 | Statement: 772 | - Action: 773 | - cloudformation:DescribeChangeSet 774 | - cloudformation:DescribeStacks 775 | - cloudformation:ExecuteChangeSet 776 | Condition: 777 | StringEqualsIfExists: 778 | cloudformation:ChangeSetName: buildimage 779 | Effect: Allow 780 | Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}-BuildImg/*" 781 | Roles: 782 | - Ref: BuildImgApplyActionRole 783 | # Policy for AWS CloudFormation to create a LaunchTemplate version 784 | # and perform a rolling deployment on the AutoScalingGroup 785 | DeployImgCFPolicy: 786 | Type: AWS::IAM::ManagedPolicy 787 | Properties: 788 | PolicyDocument: 789 | Version: "2012-10-17" 790 | Statement: 791 | - Action: 792 | - autoscaling:CreateAutoScalingGroup 793 | - autoscaling:DeleteAutoScalingGroup 794 | - autoscaling:DeletePolicy 795 | - autoscaling:ApplyPolicy 796 | - autoscaling:PutScalingPolicy 797 | - autoscaling:SetDesiredCapacity 798 | - autoscaling:SetInstanceHealth 799 | - autoscaling:TerminateInstanceInAutoScalingGroup 800 | - autoscaling:UpdateAutoScalingGroup 801 | Effect: Allow 802 | Resource: 803 | - !Sub "arn:${AWS::Partition}:autoscaling:${AWS::Region}:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName}-*" 804 | - Action: 805 | - autoscaling:DescribeAutoScalingGroups 806 | - autoscaling:DescribeScalingActivities 807 | - autoscaling:DescribeScheduledActions 808 | Effect: Allow 809 | Resource: "*" 810 | - Action: 811 | - cloudformation:ExecuteChangeSet 812 | - cloudformation:DescribeStacks 813 | - cloudformation:DescribeChangeSet 814 | Effect: Allow 815 | Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}-DeployImg/*" 816 | - Action: 817 | - ec2:CreateTags 818 | Effect: Allow 819 | Resource: 820 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*" 821 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*" 822 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:volume/*" 823 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:launch-template/*" 824 | Condition: 825 | StringEquals: 826 | ec2:CreateAction: RunInstances 827 | - Action: 828 | - ec2:CreateLaunchTemplate 829 | - ec2:CreateLaunchTemplateVersion 830 | Effect: Allow 831 | Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:launch-template/*" 832 | - Action: 833 | - ec2:DescribeLaunchTemplates 834 | - ec2:DescribeLaunchTemplateVersions 835 | - ec2:GetLaunchTemplateData 836 | Effect: Allow 837 | Resource: "*" 838 | - Action: 839 | - ec2:DescribeAccountAttributes 840 | - ec2:DescribeAvailabilityZones 841 | - ec2:DescribeImages 842 | - ec2:DescribeInstanceAttribute 843 | - ec2:DescribeInstances 844 | - ec2:DescribeKeyPairs 845 | - ec2:DescribeLaunchTemplateVersions 846 | - ec2:DescribePlacementGroups 847 | - ec2:DescribeSecurityGroups 848 | - ec2:DescribeSpotInstanceRequests 849 | - ec2:DescribeSubnets 850 | - ec2:DescribeVpcClassicLink 851 | Effect: Allow 852 | Resource: "*" 853 | - Action: 854 | - ec2:DeleteLaunchTemplate 855 | - ec2:DeleteLaunchTemplateVersions 856 | - ec2:ModifyLaunchTemplate 857 | Effect: Allow 858 | Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:launch-template/*" 859 | - Action: 860 | - ec2:RunInstances 861 | Effect: Allow 862 | Resource: 863 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}::image/ami-*" 864 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*" 865 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/*" 866 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*" 867 | - Action: 868 | - ec2:RunInstances 869 | Effect: Allow 870 | Resource: 871 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*" 872 | Condition: 873 | Bool: 874 | ec2:IsLaunchTemplateResource: true 875 | StringEquals: 876 | ec2:InstanceType: !Ref InstanceType 877 | - Action: 878 | - ec2:RunInstances 879 | Effect: Allow 880 | Resource: 881 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:volume/*" 882 | - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:launch-template/*" 883 | Condition: 884 | Bool: 885 | ec2:IsLaunchTemplateResource: true 886 | - Action: 887 | - iam:CreateServiceLinkedRole 888 | Effect: Allow 889 | Resource: 890 | - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling" 891 | Condition: 892 | StringLike: 893 | iam:AWSServiceName: "autoscaling.amazonaws.com" 894 | Roles: 895 | - Ref: DeployImgCFRole 896 | # Policy for the specific action in CodePipeline that calls 897 | # CloudFormation to GENERATE a change set for rolling out the AMI 898 | DeployImgCreateActionPolicy: 899 | Type: AWS::IAM::ManagedPolicy 900 | Properties: 901 | PolicyDocument: 902 | Version: "2012-10-17" 903 | Statement: 904 | - Action: iam:PassRole 905 | Effect: Allow 906 | Resource: !GetAtt DeployImgCFRole.Arn 907 | - Action: 908 | - s3:GetObject 909 | - s3:ListBucket 910 | Effect: Allow 911 | Resource: 912 | - !GetAtt DemoArtifactBucket.Arn 913 | - !Sub "${DemoArtifactBucket.Arn}/*" 914 | - Action: 915 | - cloudformation:CreateChangeSet 916 | - cloudformation:DeleteChangeSet 917 | - cloudformation:DescribeChangeSet 918 | - cloudformation:DescribeStacks 919 | Condition: 920 | StringEqualsIfExists: 921 | cloudformation:ChangeSetName: deployimage 922 | Effect: Allow 923 | Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}-DeployImg/*" 924 | Roles: 925 | - Ref: DeployImgCreateActionRole 926 | # Policy for the specific action in CodePipeline that calls 927 | # CloudFormation to apply a change set for rolling out the AMI 928 | DeployImgApplyActionPolicy: 929 | Type: AWS::IAM::ManagedPolicy 930 | Properties: 931 | PolicyDocument: 932 | Version: "2012-10-17" 933 | Statement: 934 | - Action: 935 | - cloudformation:DescribeChangeSet 936 | - cloudformation:DescribeStacks 937 | - cloudformation:ExecuteChangeSet 938 | Condition: 939 | StringEqualsIfExists: 940 | cloudformation:ChangeSetName: deployimage 941 | Effect: Allow 942 | Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}-DeployImg/*" 943 | Roles: 944 | - Ref: DeployImgApplyActionRole 945 | # Policy for the temporary EC2 instance used by EC2 Image Builder while 946 | # building the new AMI 947 | BuildImgEC2Policy: 948 | Type: AWS::IAM::ManagedPolicy 949 | Properties: 950 | PolicyDocument: 951 | Version: "2012-10-17" 952 | Statement: 953 | - Action: 954 | - s3:GetObject 955 | - s3:ListBucket 956 | Effect: Allow 957 | Resource: 958 | - !GetAtt DemoArtifactBucket.Arn 959 | - !Sub "${DemoArtifactBucket.Arn}/*" 960 | Roles: 961 | - Ref: BuildImgEC2Role 962 | # Policy to allow AWS CloudWatch Event Rules to initiate AWS Codepipeline 963 | # upon a commit to the main branch in AWS CodeCommit 964 | InitiatePipelinePolicy: 965 | Type: AWS::IAM::ManagedPolicy 966 | Properties: 967 | PolicyDocument: 968 | Version: "2012-10-17" 969 | Statement: 970 | - Action: codepipeline:StartPipelineExecution 971 | Effect: Allow 972 | Resource: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${DemoPipeline}" 973 | Roles: 974 | - Ref: InitiatePipelineRole -------------------------------------------------------------------------------- /codecommit-repo/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 | -------------------------------------------------------------------------------- /codecommit-repo/README.md: -------------------------------------------------------------------------------- 1 | # Part of AWS Sample: Immutable Server Pipeline 2 | 3 | This CodeCommit repository is part of the ["code sample"](https://github.com/aws-samples/immutable-server-pipeline) supporting the blog ["Create Immutable Servers with EC2 Image Builder & AWS CodePipeline"](https://aws.amazon.com/blogs/mt/create-immutable-servers-using-ec2-image-builder-aws-codepipeline/) on the AWS Management Tools blog. 4 | 5 | Please see the respective code sample and blog for further documentation of the snippets provided here. 6 | 7 | ## License 8 | 9 | This library is licensed under the MIT-0 License. See the LICENSE file. -------------------------------------------------------------------------------- /codecommit-repo/buildspec.yml: -------------------------------------------------------------------------------- 1 | 2 | version: 0.1 3 | 4 | phases: 5 | build: 6 | commands: 7 | - echo Build started on `date` 8 | - mvn test 9 | post_build: 10 | commands: 11 | - echo Build completed on `date` 12 | - mvn package 13 | artifacts: 14 | files: 15 | - target/gs-spring-boot-0.1.0.jar 16 | -------------------------------------------------------------------------------- /codecommit-repo/cf-build-image.yaml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | Parameters: 4 | BucketName: 5 | Type: "String" 6 | Description: "S3 Bucket containing build artifacts; should come from the CodeBuild step in Codepipeline" 7 | ObjectKey: 8 | Type: String 9 | Description: "Specific reference to the build file within the bucket; should come from the CodeBuild step in Codepipeline" 10 | Version: 11 | Type: String 12 | Description: "Version number for the ImageBuilder Component; provided by AWS Lambda in previous preparation step." 13 | Architecture: 14 | Type: "String" 15 | Description: "Select between x86 and ARM instances" 16 | InstanceType: 17 | Type: "String" 18 | Description: "Instance Type for building the instances" 19 | InstanceProfile: 20 | Type: "String" 21 | Description: "Instance Profile required for EC2 during the build of the instance" 22 | Resources: 23 | # This Component in the EC2 Image Builder setup contains the main logi 24 | # for installing the AMI. In this demo, the full script is inline. This 25 | # could be modified to read a script from an S3 bucket the same way that 26 | # the main build artifact is taken from an S3 bucket. Since this Cloud- 27 | # Formation template itself is also in Version Control, it can easily 28 | # be updated when needed for demo purposes. 29 | DemoComponent: 30 | Type: AWS::ImageBuilder::Component 31 | Properties: 32 | ChangeDescription: !Sub 33 | - "Update ${ShortName} Image (${Architecture}) to version ${Version}" 34 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 35 | Data: !Sub | 36 | name: Spring Boot Application on Amazon Linux 2 37 | description: Current version - ${Version} 38 | schemaVersion: 1.0 39 | phases: 40 | - name: build 41 | steps: 42 | - name: HelloWorldStep 43 | action: ExecuteBash 44 | inputs: 45 | commands: 46 | - cd /opt 47 | - sudo su 48 | - yum upgrade -y 49 | - yum install -y java-17-amazon-corretto-headless 50 | - useradd springboot 51 | - result=`aws s3 cp s3://${BucketName}/${ObjectKey} /tmp` 52 | - destination=${!result##* } 53 | - unzip $destination && mv target springboot 54 | - chown -R springboot:springboot springboot 55 | - rm -rf $destination 56 | - echo "[Unit]" > /etc/systemd/system/springboot.service 57 | - echo "Description=Test Spring Boot Getting Started" >> /etc/systemd/system/springboot.service 58 | - echo "After=syslog.target" >> /etc/systemd/system/springboot.service 59 | - echo "" >> /etc/systemd/system/springboot.service 60 | - echo "[Service]" >> /etc/systemd/system/springboot.service 61 | - echo "User=springboot" >> /etc/systemd/system/springboot.service 62 | - echo "WorkingDirectory=/opt/springboot" >> /etc/systemd/system/springboot.service 63 | - echo "ExecStart=/usr/bin/java -jar /opt/springboot/gs-spring-boot-0.1.0.jar" >> /etc/systemd/system/springboot.service 64 | - echo "SuccessExitStatus=143" >> /etc/systemd/system/springboot.service 65 | - echo "" >> /etc/systemd/system/springboot.service 66 | - echo "[Install]" >> /etc/systemd/system/springboot.service 67 | - echo "WantedBy=multi-user.target" >> /etc/systemd/system/springboot.service 68 | - systemctl daemon-reload 69 | - systemctl enable springboot.service 70 | - name: validate 71 | steps: 72 | - name: HelloWorldStep 73 | action: ExecuteBash 74 | inputs: 75 | commands: 76 | - systemctl is-enabled --quiet springboot.service 77 | - name: test 78 | steps: 79 | - name: HelloWorldStep 80 | action: ExecuteBash 81 | inputs: 82 | commands: 83 | - systemctl is-active --quiet springboot.service 84 | - curl --fail http://localhost:8080/ 85 | Description: !Sub 86 | - "${ShortName} Component" 87 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 88 | Name: !Sub "${AWS::StackName}-Component" 89 | Platform: Linux 90 | Version: !Sub ${Version} 91 | # Image is the ultimate outcome of the EC2 Image Builder process. Notice 92 | # the reference to this item in the Outputs. It comnbines the Recipe with 93 | # the Infrastructure Configuration. 94 | DemoImage: 95 | Type: AWS::ImageBuilder::Image 96 | Properties: 97 | ImageRecipeArn: !GetAtt DemoImageRecipe.Arn 98 | InfrastructureConfigurationArn: !GetAtt DemoInfrastructureConfiguration.Arn 99 | EnhancedImageMetadataEnabled: false 100 | Tags: 101 | Application: 'DemoImageBuilder' 102 | # The recipe is a set of Components required to build an Image. In this case 103 | # we need only a single component as it contains all the logic in one. 104 | DemoImageRecipe: 105 | Type: AWS::ImageBuilder::ImageRecipe 106 | Properties: 107 | Components: 108 | - ComponentArn: !GetAtt DemoComponent.Arn 109 | # This becomes the AMI description 110 | Description: !Sub 111 | - "${ShortName} Image (${Architecture}) version ${Version}" 112 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 113 | Name: !Sub 114 | - "${ShortName}-Image" 115 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 116 | ParentImage: !Sub "arn:aws:imagebuilder:${AWS::Region}:aws:image/amazon-linux-2-${Architecture}/x.x.x" 117 | Version: !Sub ${Version} 118 | # The infrastructure configuration specifies the machine type and role name. 119 | # For this purpose, a t3.nano or t3a.nano is sufficient and helps keep 120 | # cost low. 121 | DemoInfrastructureConfiguration: 122 | Type: AWS::ImageBuilder::InfrastructureConfiguration 123 | Properties: 124 | InstanceProfileName: !Sub "${InstanceProfile}" 125 | InstanceTypes: 126 | - !Sub "${InstanceType}" 127 | Name: !Sub "${AWS::StackName}-InfraConfig" 128 | Outputs: 129 | DemoImageArn: 130 | Description: Reference to EC2 Image Builder Output Arn 131 | Value: !GetAtt DemoImage.Arn 132 | DemoImageId: 133 | Description: Reference to EC2 Image Builder Output ImageId 134 | Value: !GetAtt DemoImage.ImageId -------------------------------------------------------------------------------- /codecommit-repo/cf-deploy-image.yaml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | Parameters: 4 | AmiId: 5 | Type: "AWS::EC2::Image::Id" 6 | Description: Identifier of new AMI to be deployed 7 | Version: 8 | Type: "String" 9 | Description: Version of the software deployed 10 | SourceAuthorDate: 11 | Type: "String" 12 | Description: "Source parameter Author Date provided by AWS CodeCommit through AWS CodePipeline" 13 | SourceBranchName: 14 | Type: "String" 15 | Description: "Source parameter Branch Name provided by AWS CodeCommit through AWS CodePipeline" 16 | SourceCommitId: 17 | Type: "String" 18 | Description: "Source parameter Commit ID provided by AWS CodeCommit through AWS CodePipeline" 19 | SourceCommitMessage: 20 | Type: "String" 21 | Description: "Source parameter Commit Message provided by AWS CodeCommit through AWS CodePipeline" 22 | SourceCommitterDate: 23 | Type: "String" 24 | Description: "Source parameter Commit Date provided by AWS CodeCommit through AWS CodePipeline" 25 | SourceRepositoryName: 26 | Type: "String" 27 | Description: "Source parameter Repository Name provided by AWS CodeCommit through AWS CodePipeline" 28 | Subnets: 29 | Type: "String" 30 | Description: "Subnets where ASG should deploy the new instances" 31 | InstanceType: 32 | Type: "String" 33 | Description: "Instance Type for running the instances" 34 | Resources: 35 | # Metadata around AMI to roll it out to a Development environment 36 | # using an AutoScalingGroup 37 | LaunchTemplate: 38 | Type: AWS::EC2::LaunchTemplate 39 | Properties: 40 | LaunchTemplateName: !Sub "${AWS::StackName}-Template" 41 | LaunchTemplateData: 42 | ImageId: !Ref AmiId 43 | InstanceType: !Sub "${InstanceType}" 44 | BlockDeviceMappings: 45 | - DeviceName: "/dev/xvda" 46 | Ebs: 47 | DeleteOnTermination: true 48 | Encrypted: true 49 | TagSpecifications: 50 | - ResourceType: instance 51 | Tags: 52 | - Key: Application 53 | Value: !Sub 54 | - "${ShortName}" 55 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 56 | - Key: Name 57 | Value: !Sub 58 | - "${ShortName} version ${Version}" 59 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 60 | - Key: Version 61 | Value: !Sub "${Version}" 62 | - Key: "Source/AuthorDate" 63 | Value: !Sub "${SourceAuthorDate}" 64 | - Key: "Source/BranchName" 65 | Value: !Sub "${SourceBranchName}" 66 | - Key: "Source/CommitId" 67 | Value: !Sub "${SourceCommitId}" 68 | - Key: "Source/CommitMessage" 69 | Value: !Sub "${SourceCommitMessage}" 70 | - Key: "Source/CommitterDate" 71 | Value: !Sub "${SourceCommitterDate}" 72 | - Key: "Source/RepositoryName" 73 | Value: !Sub "${SourceRepositoryName}" 74 | - ResourceType: volume 75 | Tags: 76 | - Key: Application 77 | Value: !Sub 78 | - "${ShortName}" 79 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 80 | - Key: Name 81 | Value: !Sub 82 | - "${ShortName} version ${Version}" 83 | - ShortName: !Select [0, !Split ['-', !Ref AWS::StackName]] 84 | # AutoScalingGroup used as Development Environment for rolling out 85 | # the newly created AMI 86 | AutoScalingGroup: 87 | Type: AWS::AutoScaling::AutoScalingGroup 88 | Properties: 89 | MinSize: "0" 90 | MaxSize: "3" 91 | # Please REMOVE the next line if you want to avoid interfering 92 | # with existing capacity in the ASG 93 | DesiredCapacity: "1" 94 | LaunchTemplate: 95 | LaunchTemplateId: !Ref LaunchTemplate 96 | Version: !GetAtt LaunchTemplate.LatestVersionNumber 97 | VPCZoneIdentifier: !Split [ ",", !Sub "${Subnets}"] 98 | UpdatePolicy: 99 | AutoScalingRollingUpdate: 100 | MinInstancesInService: 1 101 | MaxBatchSize: 2 102 | PauseTime: PT10S 103 | -------------------------------------------------------------------------------- /codecommit-repo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 4.0.0 7 | 8 | org.springframework 9 | gs-spring-boot 10 | 0.1.0 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 3.0.4 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-web 22 | 23 | 24 | 25 | 26 | 11 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-maven-plugin 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /codecommit-repo/src/main/java/hello/Application.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | package hello; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.ApplicationContext; 10 | 11 | @SpringBootApplication 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | ApplicationContext ctx = SpringApplication.run(Application.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /codecommit-repo/src/main/java/hello/HelloController.java: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | package hello; 4 | 5 | import org.springframework.web.bind.annotation.RestController; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | @RestController 9 | public class HelloController { 10 | 11 | @RequestMapping("/") 12 | public String index() { 13 | return "Hello World from Immutable EC2 Instance\n"; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /create-zip-for-s3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd codecommit-repo 3 | zip -r ../codecommit-repo.zip . -x ".*" -x "*.DS_Store" -x "__MACOSX" 4 | popd 5 | -------------------------------------------------------------------------------- /images/architecture-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/immutable-server-pipeline/5a0c0b12ebe3950908bddd1e0fe3a1aece17be6c/images/architecture-detail.png -------------------------------------------------------------------------------- /images/architecture-high-level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/immutable-server-pipeline/5a0c0b12ebe3950908bddd1e0fe3a1aece17be6c/images/architecture-high-level.png --------------------------------------------------------------------------------