├── docs ├── README.md └── _config.yml ├── img └── vers3.png ├── .gitignore ├── LICENSE ├── nested-stacks ├── README.md ├── elb │ └── alb-in-vpc.yaml ├── security-groups │ └── ecs-in-vpc.yaml ├── cloudfront │ ├── single-apig-custom-domain.yaml │ └── angular-custom-domain.yaml ├── apig │ ├── single-lambda-proxy-with-CORS.yaml │ └── single-lambda-proxy-require-api-key-with-CORS.yaml ├── vpc │ └── three-sub-nat-gateway.yaml ├── ecs │ └── cluster-in-vpc.yaml └── rds │ └── aurora.yaml ├── bin ├── fargate-ssm-env-var-helper.sh └── lambda-ssm-env-var-helper.sh ├── README.md └── pipelines ├── todo ├── pipeline-resources-s3.yaml └── pipeline-resources-github.yaml └── cicd ├── cloudformation-singlestage.yaml ├── single-lambda-test-staging-prod.yaml └── cloudformation-test-staging-prod.yaml /docs/README.md: -------------------------------------------------------------------------------- 1 | Hello world!≈ 2 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /img/vers3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rynop/aws-blueprint/HEAD/img/vers3.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.exe~ 6 | *.dll 7 | *.so 8 | *.dylib 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | #Ignores for golang examples. You should check this into real projects 17 | _tools 18 | vendor 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ryan Pendergast 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nested-stacks/README.md: -------------------------------------------------------------------------------- 1 | An s3 bucket has to be made in your aws account to hold [CloudFormation nested stacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html). Nested stacks are reuseable, CloudFormation snippets. Nested stacks MUST be stored in s3. 2 | 3 | 1. Create a new **versioned** S3 bucket to hold these nested stacks. We recommend naming it: `[region]--aws-blueprint.yourdomain.com`. Ex: `us-east-1--aws-blueprint.yourdomain.com`. You can enable versioning in the S3 web console by checking this box: 4 | 5 | 6 | 1. Clone this repo 7 | 1. `cd` to the `nested-stacks` dir int the cloned repo and run: `aws s3 sync . s3://us-east-1--aws-blueprint.yourdomain.com/nested-stacks` 8 | 9 | From time to time, nested stacks in this repo are updated. If you want to pull in these changes, they need be synced to your s3 aws-blueprint nested stacks bucket. Remember your bucket is **versioned**, and aws-blueprint references the stacks by the version - so you don't have to worry about breaking anything in the field. 10 | 11 | ### Note 12 | 13 | You can validate your CloudFormation template from the CLI. Ex: `aws cloudformation validate-template --template-body file://apig/single-lambda-proxy-with-CORS.yaml`. If it passes the validator, but you get strange errors running CloudFormation, create the nested stack yaml directly in the cloudformation console. It gives better errors. 14 | -------------------------------------------------------------------------------- /bin/fargate-ssm-env-var-helper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script will help you setup cmds to run in order to setup your env vars in SSM 4 | 5 | while [[ -z "$VARS" ]]; do 6 | read -p "Comma seperated env vars (MY_VAR1,MY_VAR2): " VARS 7 | done 8 | VARS="$(echo -e "${VARS}" | tr -d '[:space:]')" 9 | 10 | while [[ -z "$stage" ]]; do 11 | read -p "Stage (Ex: test,staging, or prod): " stage 12 | done 13 | 14 | read -p "aws cli profile [default]: " awsCliProfile 15 | awsCliProfile=${awsCliProfile:-default} 16 | 17 | repoPath=$(basename `git rev-parse --show-toplevel 2>/dev/null` 2>/dev/null) 18 | while [[ -z "$githubRepoName" ]]; do 19 | read -p "Github repo name (omit org) [${repoPath}]: " githubRepoName 20 | githubRepoName=${githubRepoName:-$repoPath} 21 | done 22 | 23 | read -p "Git branch [master]: " gitBranch 24 | gitBranch=${gitBranch:-master} 25 | 26 | echo "" 27 | echo "--Bash script start--" 28 | echo "\ 29 | #!/usr/bin/env bash 30 | 31 | #DO NOT CHECK IN this file with values defined. Imagine if you accidentally overwrote prod vaules with test ones.. 32 | 33 | #Generated from https://github.com/rynop/aws-blueprint/blob/master/bin/fargate-ssm-env-var-helper.sh 34 | 35 | STAGE=${stage} 36 | BRANCH=${gitBranch} 37 | AWS_CLI_PROFILE=${awsCliProfile} 38 | " 39 | 40 | IFS=',' read -ra ADDR <<< "$VARS" 41 | for i in "${ADDR[@]}"; do 42 | echo "aws ssm put-parameter --profile \$AWS_CLI_PROFILE --name \"/\$STAGE/${githubRepoName}/\$BRANCH/ecsEnvs/${i}\" --type 'SecureString' --value ''" 43 | done 44 | 45 | echo "--Bash script end--" 46 | 47 | echo "" 48 | echo "Make sure aws/cloudformation/parameters/${stage}--ecs-codepipeline-parameters.json has SsmEnvPrefix set to /${stage}/${githubRepoName}/${gitBranch}/ecsEnvs/" 49 | -------------------------------------------------------------------------------- /bin/lambda-ssm-env-var-helper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script will help you setup cmds to run in order to setup your env vars in SSM 4 | 5 | while [[ -z "$VARS" ]]; do 6 | read -p "Comma seperated env vars (MY_VAR1,MY_VAR2): " VARS 7 | done 8 | VARS="$(echo -e "${VARS}" | tr -d '[:space:]')" 9 | 10 | while [[ -z "$stage" ]]; do 11 | read -p "Stage (Ex: test,staging, or prod): " stage 12 | done 13 | 14 | read -p "aws cli profile [default]: " awsCliProfile 15 | awsCliProfile=${awsCliProfile:-default} 16 | 17 | repoPath=$(basename `git rev-parse --show-toplevel 2>/dev/null` 2>/dev/null) 18 | while [[ -z "$githubRepoName" ]]; do 19 | read -p "Github repo name (omit org) [${repoPath}]: " githubRepoName 20 | githubRepoName=${githubRepoName:-$repoPath} 21 | done 22 | 23 | read -p "Git branch [master]: " gitBranch 24 | gitBranch=${gitBranch:-master} 25 | 26 | while [[ -z "$lambdaName" ]]; do 27 | read -p "Lambda Name (LambdaName param to your CloudFormation, no -- in name): " lambdaName 28 | done 29 | 30 | echo "--Bash script start--" 31 | echo "\ 32 | #!/usr/bin/env bash 33 | 34 | #DO NOT CHECK IN this file with values defined. Imagine if you accidentally overwrote prod vaules with test ones.. 35 | 36 | #Generated from https://github.com/rynop/aws-blueprint/blob/master/bin/lambda-ssm-env-var-helper.sh 37 | 38 | STAGE=${stage} 39 | BRANCH=${gitBranch} 40 | LAMBDA_NAME=${lambdaName} 41 | AWS_CLI_PROFILE=${awsCliProfile} 42 | " 43 | 44 | IFS=',' read -ra ADDR <<< "$VARS" 45 | for i in "${ADDR[@]}"; do 46 | echo "aws ssm put-parameter --profile \$AWS_CLI_PROFILE --name \"/\$STAGE/${githubRepoName}/\$BRANCH/\$LAMBDA_NAME/lambdaEnvs/${i}\" --type 'SecureString' --value ''" 47 | done 48 | 49 | echo "" 50 | echo "aws ssm put-parameter --profile \$AWS_CLI_PROFILE --name \"/\$STAGE/${githubRepoName}/\$BRANCH/\$LAMBDA_NAME/lambdaTimeout\" --type 'String' --value ''" 51 | echo "aws ssm put-parameter --profile \$AWS_CLI_PROFILE --name \"/\$STAGE/${githubRepoName}/\$BRANCH/\$LAMBDA_NAME/lambdaMemory\" --type 'String' --value ''" 52 | 53 | echo "--Bash script end--" 54 | -------------------------------------------------------------------------------- /nested-stacks/elb/alb-in-vpc.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys an Application Load Balancer into a VPC. 3 | 4 | Parameters: 5 | 6 | EnvironmentName: 7 | Description: An environment name that will be prefixed to resource names 8 | Type: String 9 | 10 | VPC: 11 | Type: AWS::EC2::VPC::Id 12 | Description: Choose which VPC the Application Load Balancer should be deployed to 13 | 14 | Subnets: 15 | Description: Choose which subnets the Application Load Balancer should be deployed to 16 | Type: List 17 | 18 | SecurityGroup: 19 | Description: Select the Security Group to apply to the Application Load Balancer 20 | Type: AWS::EC2::SecurityGroup::Id 21 | 22 | Resources: 23 | 24 | LoadBalancer: 25 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 26 | Properties: 27 | Name: !Ref EnvironmentName 28 | Subnets: !Ref Subnets 29 | SecurityGroups: 30 | - !Ref SecurityGroup 31 | Tags: 32 | - Key: Name 33 | Value: !Ref EnvironmentName 34 | 35 | LoadBalancerListener: 36 | Type: AWS::ElasticLoadBalancingV2::Listener 37 | Properties: 38 | LoadBalancerArn: !Ref LoadBalancer 39 | Port: 80 40 | Protocol: HTTP 41 | DefaultActions: 42 | - Type: forward 43 | TargetGroupArn: !Ref DefaultTargetGroup 44 | 45 | # We define a default target group here, as this is a mandatory Parameters 46 | # when creating an Application Load Balancer Listener. This is not used, instead 47 | # a target group is created per-service in each service template (../services/*) 48 | DefaultTargetGroup: 49 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 50 | Properties: 51 | Name: !Sub ${EnvironmentName}-default 52 | VpcId: !Ref VPC 53 | Port: 80 54 | Protocol: HTTP 55 | 56 | Outputs: 57 | 58 | LoadBalancer: 59 | Description: A reference to the Application Load Balancer 60 | Value: !Ref LoadBalancer 61 | 62 | LoadBalancerUrl: 63 | Description: The URL of the ALB 64 | Value: !GetAtt LoadBalancer.DNSName 65 | 66 | Listener: 67 | Description: A reference to a port 80 listener 68 | Value: !Ref LoadBalancerListener 69 | 70 | 71 | -------------------------------------------------------------------------------- /nested-stacks/security-groups/ecs-in-vpc.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template contains the security groups required by our entire stack. 3 | 4 | Parameters: 5 | 6 | EnvironmentName: 7 | Description: An environment name that will be prefixed to resource names 8 | Type: String 9 | 10 | VPC: 11 | Type: AWS::EC2::VPC::Id 12 | Description: Choose which VPC the security groups should be deployed to 13 | 14 | Resources: 15 | 16 | # This security group defines who/where is allowed to access the ECS hosts directly. 17 | # By default we're just allowing access from the load balancer. If you want to SSH 18 | # into the hosts, or expose non-load balanced services you can open their ports here. 19 | ECSHostSecurityGroup: 20 | Type: AWS::EC2::SecurityGroup 21 | Properties: 22 | VpcId: !Ref VPC 23 | GroupDescription: Access to the ECS hosts and the tasks/containers that run on them 24 | SecurityGroupIngress: 25 | # Only allow inbound access to ECS from the ELB 26 | - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 27 | IpProtocol: -1 28 | Tags: 29 | - Key: Name 30 | Value: !Sub ${EnvironmentName}-ECS-Hosts 31 | 32 | # This security group defines who/where is allowed to access the Application Load Balancer. 33 | # By default, we've opened this up to the public internet (0.0.0.0/0) but can you restrict 34 | # it further if you want. 35 | LoadBalancerSecurityGroup: 36 | Type: AWS::EC2::SecurityGroup 37 | Properties: 38 | VpcId: !Ref VPC 39 | GroupDescription: Access to the load balancer that sits in front of ECS 40 | SecurityGroupIngress: 41 | # Allow access from anywhere to our ECS services 42 | - CidrIp: 0.0.0.0/0 43 | IpProtocol: -1 44 | Tags: 45 | - Key: Name 46 | Value: !Sub ${EnvironmentName}-LoadBalancers 47 | 48 | Outputs: 49 | 50 | ECSHostSecurityGroup: 51 | Description: A reference to the security group for ECS hosts 52 | Value: !Ref ECSHostSecurityGroup 53 | Export: 54 | Name: !Sub "${EnvironmentName}-ECSHostSecurityGroup" 55 | 56 | LoadBalancerSecurityGroup: 57 | Description: A reference to the security group for load balancers 58 | Value: !Ref LoadBalancerSecurityGroup 59 | Export: 60 | Name: !Sub "${EnvironmentName}-LoadBalancerSecurityGroup" 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-blueprint 2 | 3 | Easy to use CI/CD driven, convention based application harness that facilitates production grade, multi-stage approval based deployments. Each developer on your team can easily create production replica toplogies from a git feature branch. 4 | 5 | Architected to work with a low cost (but scaleable) APIGateway+Lambda OR a high transaction ECS Fargate environment. Includes an upgrade path from the APIG+Lambda to Fargate if/when the workload outgrows Lambda. 6 | 7 | ## Prerequisites 8 | 9 | There are a handful of necessary steps prior to running any of the Blueprints below. These steps only need to be done once. 10 | 11 | 1. [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) 12 | 1. Create a [personal github access token](https://github.com/settings/tokens). This token will be used by the CI/CD to pull code. If you work in a team, it is recommended to create a seperate github user account for this. 13 | 1. Create a **versioned** S3 bucket to store CloudFormation nested stack templates. See [nested-stacks](./nested-stacks) for instructions. 14 | 1. If you are running MacOS, install GNU tools ([below](https://github.com/rynop/aws-blueprint#gnu-tools-for-macos)) 15 | 16 | ## Terminology 17 | 18 | The Stage/Stages term referred to thoughout the Blueprints are: `test`,`staging`,`prod`. All CI/CD enviornments created by Blueprint will give you these 3 stages by default. 19 | 20 | ## Blueprints 21 | 22 | View the setup instructions in the `README.md` of the blueprint you wish to use. 23 | 24 | - [abp-sam-twirp](https://github.com/rynop/abp-sam-twirp): CloudFront -> API Gateway -> Lambda running [Twirp](https://github.com/twitchtv/twirp) (RPC, protobuf&JSON) using [Serverless Application Module (SAM)](https://github.com/awslabs/serverless-application-model). Complete with local API Gateway, Lambda and DyanmoDB simulation. 25 | - [abp-sam-nestjs](https://github.com/rynop/abp-sam-nestjs): API Gateway -> Lambda running [NestJS](https://nestjs.com/) using [Serverless Application Module (SAM)](https://github.com/awslabs/serverless-application-model). Complete with local API Gateway, Lambda and DyanmoDB simulation. 26 | - [abp-single-lambda-api](https://github.com/rynop/abp-single-lambda-api): API backed by a single Lambda 27 | - [abp-singlle-lambda-no-web-api](https://github.com/rynop/abp-single-lambda-api#lambda-with-no-web-api): Single Lambda with no web API (ex: invoked by SNS). 28 | - [abp-fargate](https://github.com/rynop/abp-fargate): Fargate based app (with or without ELB) 29 | - [abp-angular](https://github.com/rynop/abp-angular): Running Angular web apps on AWS 30 | 31 | ## GNU tools for MacOS 32 | 33 | Not required, but recomended. Some blueprints have a quickstart script, that will require GNU versions if running MacOS. 34 | 35 | ``` 36 | brew install wget coreutils gnu-sed 37 | 38 | #add the following to your PATH 39 | 40 | #### fish example (~/.config/fish/config.fish): 41 | set -x GNU_BIN_PATH /usr/local/opt/coreutils/libexec/gnubin 42 | set -x GNU_SED_PATH /usr/local/opt/gnu-sed/libexec/gnubin 43 | set -x PATH $GNU_BIN_PATH $GNU_SED_PATH $PATH 44 | 45 | #### Bash example (~/.bash_profile) 46 | GNU_BIN_PATH=/usr/local/opt/coreutils/libexec/gnubin 47 | GNU_SED_PATH=/usr/local/opt/gnu-sed/libexec/gnubin 48 | export PATH="$GNU_BIN_PATH:$GNU_SED_PATH:$PATH" 49 | ``` 50 | 51 | ## Tips 52 | 53 | - [HOWTO](https://rynop.com/2019/05/09/howto-mobile-development-against-a-localhost-https-api/) develop against a localhost HTTPS API. 54 | -------------------------------------------------------------------------------- /nested-stacks/cloudfront/single-apig-custom-domain.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | 3 | Description: CloudFront > APIG 4 | SSL only, custom DomainName, custom header to verify requests come through CloudFront, Edge Gzip, Caching OPTIONS 5 | 6 | Parameters: 7 | 8 | Stage: 9 | Description: Deployment stage 10 | Type: String 11 | 12 | ApiGatewayId: 13 | Description: API gateway ID (NOT the deployment ID) 14 | Type: String 15 | 16 | AcmCertificateArn: 17 | Description: The ARN of a certificate from AWS Certificate Manager (ACM) 18 | Type: String 19 | 20 | Route53HostedZoneId: 21 | Description: Hosted Zone ID where domain name for CloudFront will be made 22 | Type: String 23 | Default: Z2FDTNDATAQYW2 24 | 25 | CloudFrontCname: 26 | Description: Custom domain name to use (foo.yourdomain.com). Will prefix this with [Stage]-- 27 | Type: String 28 | Default: blah.yourdomain.com 29 | 30 | VerifyFromCfHeaderVal: 31 | Description: A custom header X-From-CDN with val below be passed to your orgin, to verify request came 32 | Type: String 33 | 34 | Resources: 35 | #### Cloudfront and DNS stuff 36 | CloudFront: 37 | Type: AWS::CloudFront::Distribution 38 | Properties: 39 | DistributionConfig: 40 | Enabled: true 41 | IPV6Enabled: true 42 | HttpVersion: http2 43 | Comment: !Join [ '', [!Ref 'AWS::StackName', ' Cloud Front']] 44 | Aliases: 45 | - !Sub ${Stage}--${CloudFrontCname} 46 | ViewerCertificate: 47 | AcmCertificateArn: !Ref AcmCertificateArn 48 | SslSupportMethod: sni-only 49 | MinimumProtocolVersion: TLSv1.1_2016 50 | Origins: 51 | - Id: APIGOrigin 52 | DomainName: !Sub ${ApiGatewayId}.execute-api.${AWS::Region}.amazonaws.com 53 | OriginPath: !Sub /${Stage} 54 | CustomOriginConfig: 55 | HTTPSPort: 443 56 | OriginProtocolPolicy: https-only 57 | OriginCustomHeaders: 58 | - HeaderName: 'X-From-CDN' 59 | HeaderValue: !Ref VerifyFromCfHeaderVal 60 | DefaultCacheBehavior: 61 | AllowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] 62 | CachedMethods: ["GET", "HEAD", "OPTIONS"] 63 | ForwardedValues: 64 | Headers: 65 | - Access-Control-Request-Headers 66 | - Access-Control-Request-Method 67 | - Origin 68 | - Authorization 69 | - Accept 70 | # - Host APIG needs to use SNI 71 | QueryString: true 72 | TargetOriginId: APIGOrigin 73 | ViewerProtocolPolicy: https-only 74 | Compress: true 75 | DefaultTTL: 0 76 | CustomErrorResponses: 77 | - ErrorCachingMinTTL: 0 78 | ErrorCode: 400 79 | - ErrorCachingMinTTL: 1 80 | ErrorCode: 403 81 | - ErrorCachingMinTTL: 5 82 | ErrorCode: 500 83 | DNSARecord: 84 | Type: AWS::Route53::RecordSet 85 | Properties: 86 | Comment: !Ref 'AWS::StackName' 87 | Name: !Sub ${Stage}--${CloudFrontCname} 88 | Type: A 89 | HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']] 90 | AliasTarget: 91 | HostedZoneId: !Ref Route53HostedZoneId 92 | DNSName: !GetAtt CloudFront.DomainName 93 | DNSAAAARecord: 94 | Type: AWS::Route53::RecordSet 95 | Properties: 96 | Comment: !Ref 'AWS::StackName' 97 | Name: !Sub ${Stage}--${CloudFrontCname} 98 | Type: AAAA 99 | HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']] 100 | AliasTarget: 101 | HostedZoneId: !Ref Route53HostedZoneId 102 | DNSName: !GetAtt CloudFront.DomainName 103 | 104 | ####END Cloudfront and DNS stuff 105 | 106 | Outputs: 107 | Version: 108 | Description: CF-APIG-Single-Lambda template version 109 | Value: 1.0.0 110 | 111 | CloudFront: 112 | Description: CloudFront ID 113 | Value: !Ref CloudFront 114 | CNAME: 115 | Description: Custom domain for Cloudfront 116 | Value: !Ref DNSARecord 117 | 118 | -------------------------------------------------------------------------------- /nested-stacks/apig/single-lambda-proxy-with-CORS.yaml: -------------------------------------------------------------------------------- 1 | #TODO: add 4xx,5xx errors. Test binary 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | 4 | Description: API Gateway that points to a single Lambda via proxy+. Supports CORS. Has application/protobuf defined as binary. 5 | 6 | Parameters: 7 | 8 | Stage: 9 | Description: Deployment stage 10 | Type: String 11 | 12 | Repo: 13 | Description: Code repo 14 | Type: String 15 | 16 | Branch: 17 | Description: Code branch 18 | Type: String 19 | 20 | LambdaName: 21 | Description: Lambda function name (should not exist). This value will be prepended with [Repo]--[Branch]-- 22 | Type: String 23 | 24 | Resources: 25 | APIGCloudWatchRole: 26 | Type: "AWS::IAM::Role" 27 | Properties: 28 | AssumeRolePolicyDocument: 29 | Version: "2012-10-17" 30 | Statement: 31 | - Effect: Allow 32 | Principal: 33 | Service: 34 | - "apigateway.amazonaws.com" 35 | Action: "sts:AssumeRole" 36 | Path: "/" 37 | ManagedPolicyArns: 38 | - "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" 39 | 40 | APIGAccount: 41 | Type: AWS::ApiGateway::Account 42 | Properties: 43 | CloudWatchRoleArn: !GetAtt APIGCloudWatchRole.Arn 44 | 45 | #### Can't add lambda invoke permission to APIG cuz Lambda dont exist yet. Permission will be added during build. Per https://stackoverflow.com/questions/39905255/how-can-i-grant-permission-to-api-gateway-to-invoke-lambda-functions-through-clo 46 | 47 | ApiGatewayLambdaProxy: 48 | Type : AWS::ApiGateway::RestApi 49 | Properties : 50 | Name : !Sub ${Stage}--${Repo}--${Branch}--${LambdaName} 51 | Description: "Single Lambda proxy" 52 | # BinaryMediaTypes: - this is broken in CloudFormation on create 53 | # - "application/protobuf" 54 | Parameters: 55 | endpointConfigurationTypes: EDGE 56 | Body : 57 | swagger: "2.0" 58 | info: 59 | version: "2018-01-23T15:50:43Z" 60 | title: "Lambda Proxy" 61 | schemes: 62 | - "https" 63 | paths: 64 | "/{proxy+}": 65 | options: 66 | consumes: 67 | - "application/json" 68 | produces: 69 | - "application/json" 70 | responses: 71 | "200": 72 | description: "200 response" 73 | schema: 74 | $ref: "#/definitions/Empty" 75 | headers: 76 | Cache-Control: 77 | type: "string" 78 | Access-Control-Allow-Origin: 79 | type: "string" 80 | Access-Control-Allow-Methods: 81 | type: "string" 82 | Access-Control-Max-Age: 83 | type: "string" 84 | Access-Control-Allow-Headers: 85 | type: "string" 86 | x-amazon-apigateway-integration: 87 | responses: 88 | default: 89 | statusCode: "200" 90 | responseParameters: 91 | method.response.header.Cache-Control: "'max-age=604800'" 92 | method.response.header.Access-Control-Max-Age: "'604800'" 93 | method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" 94 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" 95 | method.response.header.Access-Control-Allow-Origin: "'*'" 96 | passthroughBehavior: "when_no_match" 97 | requestTemplates: 98 | application/json: "{\"statusCode\": 200}" 99 | type: "mock" 100 | x-amazon-apigateway-any-method: 101 | produces: 102 | - "application/json" 103 | parameters: 104 | - name: "proxy" 105 | in: "path" 106 | required: true 107 | type: "string" 108 | responses: {} 109 | x-amazon-apigateway-integration: 110 | uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Repo}--${Branch}--${LambdaName}:${Stage}/invocations 111 | httpMethod: "POST" 112 | type: "aws_proxy" 113 | definitions: 114 | Empty: 115 | type: "object" 116 | title: "Empty Schema" 117 | x-amazon-apigateway-binary-media-types: 118 | - "application/protobuf" 119 | 120 | APIGDeployment: 121 | Type: AWS::ApiGateway::Deployment 122 | Properties: 123 | RestApiId: 124 | Ref: "ApiGatewayLambdaProxy" 125 | Description: "Deployment" 126 | StageName: !Sub ${Stage} 127 | 128 | Outputs: 129 | Version: 130 | Description: APIG-Single-Lambda-proxy-with-CORS template version 131 | Value: 1.0.0 132 | 133 | APIGID: 134 | Description: APIG root resource ID 135 | Value: !Ref ApiGatewayLambdaProxy 136 | 137 | APIGURL: 138 | Description: APIG stage URL 139 | Value: !Sub 140 | - https://${apigId}.execute-api.${AWS::Region}.amazonaws.com/${Stage} 141 | - { apigId: !Ref ApiGatewayLambdaProxy } 142 | 143 | APIGLambdaInvocationArn: 144 | Description: ARN of lambda that will be called from APIG 145 | Value: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Repo}--${Branch}--${LambdaName}:${Stage}/invocations -------------------------------------------------------------------------------- /nested-stacks/apig/single-lambda-proxy-require-api-key-with-CORS.yaml: -------------------------------------------------------------------------------- 1 | #TODO: add 4xx,5xx errors. Test binary 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | 4 | Description: API Gateway that points to a single Lambda via proxy+. Supports CORS. Has application/protobuf defined as binary. 5 | 6 | Parameters: 7 | 8 | Stage: 9 | Description: Deployment stage 10 | Type: String 11 | 12 | Repo: 13 | Description: Code repo 14 | Type: String 15 | 16 | Branch: 17 | Description: Code branch 18 | Type: String 19 | 20 | LambdaName: 21 | Description: Lambda function name (should not exist). This value will be prepended with [Repo]--[Branch]-- 22 | Type: String 23 | 24 | Resources: 25 | APIGCloudWatchRole: 26 | Type: "AWS::IAM::Role" 27 | Properties: 28 | AssumeRolePolicyDocument: 29 | Version: "2012-10-17" 30 | Statement: 31 | - Effect: Allow 32 | Principal: 33 | Service: 34 | - "apigateway.amazonaws.com" 35 | Action: "sts:AssumeRole" 36 | Path: "/" 37 | ManagedPolicyArns: 38 | - "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" 39 | 40 | APIGAccount: 41 | Type: AWS::ApiGateway::Account 42 | Properties: 43 | CloudWatchRoleArn: !GetAtt APIGCloudWatchRole.Arn 44 | 45 | #### Can't add lambda invoke permission to APIG cuz Lambda dont exist yet. Permission will be added during build. Per https://stackoverflow.com/questions/39905255/how-can-i-grant-permission-to-api-gateway-to-invoke-lambda-functions-through-clo 46 | 47 | ApiGatewayLambdaProxy: 48 | Type : AWS::ApiGateway::RestApi 49 | Properties : 50 | Name : !Sub ${Stage}--${Repo}--${Branch}--${LambdaName} 51 | Description: "Single Lambda proxy" 52 | # BinaryMediaTypes: - this is broken in CloudFormation on create 53 | # - "application/protobuf" 54 | Parameters: 55 | endpointConfigurationTypes: EDGE 56 | Body : 57 | swagger: "2.0" 58 | info: 59 | version: "2018-01-23T15:50:43Z" 60 | title: "Lambda Proxy" 61 | schemes: 62 | - "https" 63 | paths: 64 | "/{proxy+}": 65 | options: 66 | consumes: 67 | - "application/json" 68 | produces: 69 | - "application/json" 70 | responses: 71 | "200": 72 | description: "200 response" 73 | schema: 74 | $ref: "#/definitions/Empty" 75 | headers: 76 | Cache-Control: 77 | type: "string" 78 | Access-Control-Allow-Origin: 79 | type: "string" 80 | Access-Control-Allow-Methods: 81 | type: "string" 82 | Access-Control-Max-Age: 83 | type: "string" 84 | Access-Control-Allow-Headers: 85 | type: "string" 86 | x-amazon-apigateway-integration: 87 | responses: 88 | default: 89 | statusCode: "200" 90 | responseParameters: 91 | method.response.header.Cache-Control: "'max-age=604800'" 92 | method.response.header.Access-Control-Max-Age: "'604800'" 93 | method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" 94 | method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" 95 | method.response.header.Access-Control-Allow-Origin: "'*'" 96 | passthroughBehavior: "when_no_match" 97 | requestTemplates: 98 | application/json: "{\"statusCode\": 200}" 99 | type: "mock" 100 | x-amazon-apigateway-any-method: 101 | produces: 102 | - "application/json" 103 | parameters: 104 | - name: "proxy" 105 | in: "path" 106 | required: true 107 | type: "string" 108 | responses: {} 109 | security: 110 | - api_key: [] 111 | x-amazon-apigateway-integration: 112 | uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Repo}--${Branch}--${LambdaName}:${Stage}/invocations 113 | responses: 114 | default: 115 | statusCode: "200" 116 | passthroughBehavior: "when_no_match" 117 | httpMethod: "POST" 118 | contentHandling: "CONVERT_TO_TEXT" 119 | type: "aws_proxy" 120 | securityDefinitions: 121 | api_key: 122 | type: "apiKey" 123 | name: "x-api-key" 124 | in: "header" 125 | definitions: 126 | Empty: 127 | type: "object" 128 | title: "Empty Schema" 129 | x-amazon-apigateway-binary-media-types: 130 | - "application/protobuf" 131 | 132 | APIGDeployment: 133 | Type: AWS::ApiGateway::Deployment 134 | Properties: 135 | RestApiId: 136 | Ref: "ApiGatewayLambdaProxy" 137 | Description: "Deployment" 138 | StageName: !Sub ${Stage} 139 | 140 | Outputs: 141 | Version: 142 | Description: APIG-Single-Lambda-proxy-with-CORS template version 143 | Value: 1.0.0 144 | 145 | APIGID: 146 | Description: APIG root resource ID 147 | Value: !Ref ApiGatewayLambdaProxy 148 | 149 | APIGURL: 150 | Description: APIG stage URL 151 | Value: !Sub 152 | - https://${apigId}.execute-api.${AWS::Region}.amazonaws.com/${Stage} 153 | - { apigId: !Ref ApiGatewayLambdaProxy } 154 | 155 | APIGLambdaInvocationArn: 156 | Description: ARN of lambda that will be called from APIG 157 | Value: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Repo}--${Branch}--${LambdaName}:${Stage}/invocations -------------------------------------------------------------------------------- /nested-stacks/cloudfront/angular-custom-domain.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | 3 | Description: CloudFront > APIG 4 | Redirect to SSL, custom DomainName, Edge Gzip, Angular redirects, S3 origin bucket 5 | 6 | Parameters: 7 | 8 | Stage: 9 | Description: Deployment stage 10 | Type: String 11 | 12 | Branch: 13 | Description: Git branch 14 | Type: String 15 | 16 | AcmCertificateArn: 17 | Description: The ARN of a certificate from AWS Certificate Manager (ACM) 18 | Type: String 19 | 20 | Route53HostedZoneId: 21 | Description: Hosted Zone ID where domain name for CloudFront will be made 22 | Type: String 23 | Default: Z2FDTNDATAQYW2 24 | 25 | CloudFrontCname: 26 | Description: Custom domain name to use (foo.yourdomain.com). Will prefix this with [Stage]-- 27 | Type: String 28 | Default: blah.yourdomain.com 29 | AllowedPattern : (([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.){2,}([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]){2,} 30 | ConstraintDescription : Must be a valid DNS zone name WITHOUT trailing period 31 | 32 | Conditions: 33 | IsProd: !Equals [ !Ref Stage, prod ] 34 | 35 | Resources: 36 | #### S3 bucket that hosts Angular app 37 | AppBucket: 38 | Type: AWS::S3::Bucket 39 | DeletionPolicy: 'Delete' #TODO: https://forums.aws.amazon.com/post!post.jspa?forumID=92&threadID=152800&messageID=859017&reply=true 40 | Properties: 41 | BucketName: !Sub ${AWS::Region}--${Stage}--${Branch}--${CloudFrontCname} 42 | # ReplicationConfiguration: 43 | # Role: !GetAtt [WorkItemBucketBackupRole, Arn] 44 | # Rules: 45 | # - Destination: 46 | # Bucket: !Join ['', ['arn:aws:s3:::', !Join ['-', [!Ref 'AWS::Region', !Ref 'AWS::StackName', 47 | # replicationbucket]]]] 48 | # StorageClass: STANDARD 49 | # Id: Backup 50 | # Prefix: '' 51 | # Status: Enabled 52 | AccessControl: PublicRead 53 | WebsiteConfiguration: 54 | IndexDocument: index.html 55 | VersioningConfiguration: 56 | Status: Enabled 57 | 58 | AppBucketPolicy: 59 | Type: AWS::S3::BucketPolicy 60 | Properties: 61 | PolicyDocument: 62 | Id: WebAppBucketPolicy 63 | Version: 2012-10-17 64 | Statement: 65 | - Sid: PublicReadForGetBucketObjects 66 | Effect: Allow 67 | Principal: '*' 68 | Action: 's3:GetObject' 69 | Resource: !Sub 70 | - arn:aws:s3:::${AppBucket}/* 71 | - { AppBucket: !Ref AppBucket } 72 | Bucket: !Ref AppBucket 73 | 74 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-cloudfrontoriginaccessidentity.html 75 | OriginAccessIdentity: 76 | Type: AWS::CloudFront::CloudFrontOriginAccessIdentity 77 | Properties: 78 | CloudFrontOriginAccessIdentityConfig: 79 | Comment: Access identity between CloudFront and S3 bucket 80 | #### End s3 81 | 82 | #### Cloudfront and DNS stuff 83 | CloudFront: 84 | Type: AWS::CloudFront::Distribution 85 | DependsOn: AppBucket 86 | Properties: 87 | DistributionConfig: 88 | Enabled: true 89 | IPV6Enabled: true 90 | HttpVersion: http2 91 | DefaultRootObject: index.html 92 | Comment: !Join [ '', [!Ref 'AWS::StackName', ' Angular CloudFront']] 93 | Aliases: 94 | - !Sub ${Stage}--${CloudFrontCname} 95 | - !If [IsProd, !Ref CloudFrontCname, !Ref "AWS::NoValue"] 96 | ViewerCertificate: 97 | AcmCertificateArn: !Ref AcmCertificateArn 98 | SslSupportMethod: sni-only 99 | MinimumProtocolVersion: TLSv1.1_2016 100 | Origins: 101 | - DomainName: !Sub ${AppBucket}.s3.amazonaws.com 102 | Id: myS3Origin 103 | S3OriginConfig: 104 | OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${OriginAccessIdentity} 105 | DefaultCacheBehavior: 106 | AllowedMethods: ["GET", "HEAD", "OPTIONS"] 107 | CachedMethods: ["GET", "HEAD", "OPTIONS"] 108 | ForwardedValues: 109 | Headers: 110 | - Access-Control-Request-Headers 111 | - Access-Control-Request-Method 112 | - Origin 113 | - Authorization 114 | QueryString: false 115 | Cookies: 116 | Forward: none 117 | TargetOriginId: myS3Origin 118 | ViewerProtocolPolicy: redirect-to-https 119 | Compress: true 120 | DefaultTTL: 0 121 | CustomErrorResponses: 122 | - ErrorCachingMinTTL: 300 123 | ErrorCode: 404 124 | ResponseCode: 200 125 | ResponsePagePath: /index.html 126 | - ErrorCachingMinTTL: 0 127 | ErrorCode: 400 128 | - ErrorCachingMinTTL: 1 129 | ErrorCode: 403 130 | - ErrorCachingMinTTL: 5 131 | ErrorCode: 500 132 | DNSARecord: 133 | Type: AWS::Route53::RecordSet 134 | Properties: 135 | Comment: !Ref 'AWS::StackName' 136 | Name: !Sub ${Stage}--${CloudFrontCname} 137 | Type: A 138 | HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']] 139 | AliasTarget: 140 | HostedZoneId: !Ref Route53HostedZoneId 141 | DNSName: !GetAtt CloudFront.DomainName 142 | DNSAAAARecord: 143 | Type: AWS::Route53::RecordSet 144 | Properties: 145 | Comment: !Ref 'AWS::StackName' 146 | Name: !Sub ${Stage}--${CloudFrontCname} 147 | Type: AAAA 148 | HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']] 149 | AliasTarget: 150 | HostedZoneId: !Ref Route53HostedZoneId 151 | DNSName: !GetAtt CloudFront.DomainName 152 | 153 | ####END Cloudfront and DNS stuff 154 | 155 | Outputs: 156 | Version: 157 | Description: angular-custom-domain template version 158 | Value: 1.0.0 159 | 160 | CloudFront: 161 | Description: CloudFront ID 162 | Value: !Ref CloudFront 163 | CNAME: 164 | Description: Custom domain for Cloudfront 165 | Value: !Ref DNSARecord 166 | AppURL: 167 | Description: App URL 168 | Value: !Sub 169 | - https://${DN} 170 | - { DN: !Ref DNSARecord } 171 | 172 | -------------------------------------------------------------------------------- /pipelines/todo/pipeline-resources-s3.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | 3 | Description: > 4 | AWS CloudFormation Sample Template Continuous Delievery: This template 5 | builds an AWS CodePipeline pipeline that implements a continuous delivery release 6 | process for AWS CloudFormation stacks. Submit a CloudFormation source artifact 7 | to an Amazon S3 location before building the pipeline. The pipeline uses the 8 | artifact to automatically create stacks and change sets. 9 | **WARNING** This template creates an Amazon EC2 instance. You will be billed 10 | for the AWS resources used when you create a stack using this template. 11 | 12 | Parameters: 13 | PipelineName: 14 | Description: A name for pipeline 15 | Type: String 16 | S3Bucket: 17 | Description: The name of the S3 bucket that contains the source artifact, which must be in the same region as this stack 18 | Type: String 19 | SourceS3Key: 20 | Description: The file name of the source artifact, such as myfolder/myartifact.zip 21 | Type: String 22 | StackName: 23 | Description: The suffix for the name of the created stacks 24 | Type: String 25 | TemplateFileName: 26 | Default: cloudformation.yaml 27 | Description: The file name of the cloudformation template 28 | Type: String 29 | TestStackConfig: 30 | Default: test-config.json 31 | Description: The configuration file name for the test stack 32 | Type: String 33 | ProdStackConfig: 34 | Default: prod-config.json 35 | Description: The configuration file name for the production stack 36 | Type: String 37 | Email: 38 | Default: you@you.com 39 | Description: The email address where CodePipeline sends pipeline notifications 40 | Type: String 41 | 42 | Metadata: 43 | AWS::CloudFormation::Interface: 44 | ParameterGroups: 45 | - Label: 46 | default: "CodePipeline Settings" 47 | Parameters: 48 | - PipelineName 49 | - S3Bucket 50 | - SourceS3Key 51 | - Email 52 | - Label: 53 | default: "Stack Settings" 54 | Parameters: 55 | - StackName 56 | - TemplateFileName 57 | - Label: 58 | default: "Test Stack Settings" 59 | Parameters: 60 | - TemplateFileName 61 | - TestStackConfig 62 | - Label: 63 | default: "Production Stack Settings" 64 | Parameters: 65 | - ProdStackConfig 66 | 67 | Resources: 68 | ArtifactStoreBucket: 69 | Type: AWS::S3::Bucket 70 | Properties: 71 | VersioningConfiguration: 72 | Status: Enabled 73 | 74 | CodePipelineSNSTopic: 75 | Type: AWS::SNS::Topic 76 | Properties: 77 | Subscription: 78 | - Endpoint: !Ref Email 79 | Protocol: email 80 | 81 | Pipeline: 82 | Type: AWS::CodePipeline::Pipeline 83 | Properties: 84 | ArtifactStore: 85 | Location: !Ref 'ArtifactStoreBucket' 86 | Type: S3 87 | DisableInboundStageTransitions: [] 88 | Name: !Ref 'PipelineName' 89 | RoleArn: !GetAtt [PipelineRole, Arn] 90 | Stages: 91 | - Name: S3Source 92 | Actions: 93 | - Name: TemplateSource 94 | ActionTypeId: 95 | Category: Source 96 | Owner: AWS 97 | Provider: S3 98 | Version: '1' 99 | Configuration: 100 | S3Bucket: !Ref 'S3Bucket' 101 | S3ObjectKey: !Ref 'SourceS3Key' 102 | OutputArtifacts: 103 | - Name: TemplateSource 104 | RunOrder: '1' 105 | - Name: Test 106 | Actions: 107 | - Name: CreateStack 108 | ActionTypeId: 109 | Category: Deploy 110 | Owner: AWS 111 | Provider: CloudFormation 112 | Version: '1' 113 | InputArtifacts: 114 | - Name: TemplateSource 115 | Configuration: 116 | ActionMode: REPLACE_ON_FAILURE 117 | Capabilities: CAPABILITY_IAM 118 | RoleArn: !GetAtt [CFNRole, Arn] 119 | StackName: !Join [ '-', [ 'test', !Ref StackName ] ] 120 | TemplateConfiguration: !Sub "TemplateSource::${TestStackConfig}" 121 | TemplatePath: !Sub "TemplateSource::${TemplateFileName}" 122 | RunOrder: '1' 123 | - Name: ApproveTestStack 124 | ActionTypeId: 125 | Category: Approval 126 | Owner: AWS 127 | Provider: Manual 128 | Version: '1' 129 | Configuration: 130 | NotificationArn: !Ref CodePipelineSNSTopic 131 | CustomData: !Sub 'Do you want to create a change set against the production stack and delete the test-${StackName} stack?' 132 | RunOrder: '2' 133 | # - Name: DeleteTestStack 134 | # ActionTypeId: 135 | # Category: Deploy 136 | # Owner: AWS 137 | # Provider: CloudFormation 138 | # Version: '1' 139 | # Configuration: 140 | # ActionMode: DELETE_ONLY 141 | # RoleArn: !GetAtt [CFNRole, Arn] 142 | # StackName: !Join [ '-', [ 'test', !Ref 'AWS::StackName' ] ] 143 | # RunOrder: '3' 144 | - Name: Prod 145 | Actions: 146 | - Name: CreateChangeSet 147 | ActionTypeId: 148 | Category: Deploy 149 | Owner: AWS 150 | Provider: CloudFormation 151 | Version: '1' 152 | InputArtifacts: 153 | - Name: TemplateSource 154 | Configuration: 155 | ActionMode: CHANGE_SET_REPLACE 156 | Capabilities: CAPABILITY_IAM 157 | RoleArn: !GetAtt [CFNRole, Arn] 158 | StackName: !Join [ '-', [ 'prod', !Ref StackName ] ] 159 | ChangeSetName: ChangeSet 160 | TemplateConfiguration: !Sub "TemplateSource::${ProdStackConfig}" 161 | TemplatePath: !Sub "TemplateSource::${TemplateFileName}" 162 | RunOrder: '1' 163 | - Name: ApproveChangeSet 164 | ActionTypeId: 165 | Category: Approval 166 | Owner: AWS 167 | Provider: Manual 168 | Version: '1' 169 | Configuration: 170 | NotificationArn: !Ref CodePipelineSNSTopic 171 | CustomData: !Sub 'A new change set was created for the prod-${StackName} stack. Do you want to implement the changes?' 172 | RunOrder: '2' 173 | - Name: ExecuteChangeSet 174 | ActionTypeId: 175 | Category: Deploy 176 | Owner: AWS 177 | Provider: CloudFormation 178 | Version: '1' 179 | Configuration: 180 | ActionMode: CHANGE_SET_EXECUTE 181 | ChangeSetName: ChangeSet 182 | RoleArn: !GetAtt [CFNRole, Arn] 183 | StackName: !Join [ '-', [ 'prod', !Ref StackName ] ] 184 | RunOrder: '3' 185 | CFNRole: 186 | Type: AWS::IAM::Role 187 | Properties: 188 | AssumeRolePolicyDocument: 189 | Statement: 190 | - Action: ['sts:AssumeRole'] 191 | Effect: Allow 192 | Principal: 193 | Service: [cloudformation.amazonaws.com] 194 | Version: '2012-10-17' 195 | Path: / 196 | Policies: 197 | - PolicyName: CloudFormationRole 198 | PolicyDocument: 199 | Version: '2012-10-17' 200 | Statement: 201 | - Action: 202 | - '*' 203 | Effect: Allow 204 | Resource: '*' 205 | 206 | PipelineRole: 207 | Type: AWS::IAM::Role 208 | Properties: 209 | AssumeRolePolicyDocument: 210 | Statement: 211 | - Action: ['sts:AssumeRole'] 212 | Effect: Allow 213 | Principal: 214 | Service: [codepipeline.amazonaws.com] 215 | Version: '2012-10-17' 216 | Path: / 217 | Policies: 218 | - PolicyName: CodePipelineAccess 219 | PolicyDocument: 220 | Version: '2012-10-17' 221 | Statement: 222 | - Action: 223 | - 's3:*' 224 | - 'cloudformation:CreateStack' 225 | - 'cloudformation:DescribeStacks' 226 | - 'cloudformation:DeleteStack' 227 | - 'cloudformation:UpdateStack' 228 | - 'cloudformation:CreateChangeSet' 229 | - 'cloudformation:ExecuteChangeSet' 230 | - 'cloudformation:DeleteChangeSet' 231 | - 'cloudformation:DescribeChangeSet' 232 | - 'cloudformation:SetStackPolicy' 233 | - 'iam:PassRole' 234 | - 'sns:Publish' 235 | Effect: Allow 236 | Resource: '*' 237 | -------------------------------------------------------------------------------- /pipelines/todo/pipeline-resources-github.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | 4 | Description: > 5 | AWS CloudFormation Sample Template Continuous Delievery: This template 6 | builds an AWS CodePipeline pipeline that implements a continuous delivery release 7 | process for AWS CloudFormation stacks. Submit a CloudFormation source artifact 8 | to an Amazon S3 location before building the pipeline. The pipeline uses the 9 | artifact to automatically create stacks and change sets. 10 | **WARNING** This template creates an Amazon EC2 instance. You will be billed 11 | for the AWS resources used when you create a stack using this template. 12 | 13 | Parameters: 14 | PipelineName: 15 | Description: A name for pipeline 16 | Type: String 17 | Repo: 18 | Description: The GitHub Repo 19 | Type: String 20 | Branch: 21 | Description: The branch in the repo 22 | Type: String 23 | Default: master 24 | OAuthToken: 25 | Description: The GitHub OAuthToken so CodePipeline can get the code 26 | Type: String 27 | NoEcho: true 28 | TemplateFileName: 29 | Default: cloudformation.yaml 30 | Description: The file name of the cloudformation template 31 | Type: String 32 | TestStackName: 33 | Description: A name for the test WordPress stack 34 | Type: String 35 | TestStackConfig: 36 | Default: test-config.json 37 | Description: The configuration file name for the test stack 38 | Type: String 39 | ProdStackName: 40 | Description: A name for the production stack 41 | Type: String 42 | ProdStackConfig: 43 | Default: prod-config.json 44 | Description: The configuration file name for the production stack 45 | Type: String 46 | ChangeSetName: 47 | Description: A name for the production stack change set 48 | Type: String 49 | Email: 50 | Default: you@you.com 51 | Description: The email address where CodePipeline sends pipeline notifications 52 | Type: String 53 | 54 | Metadata: 55 | AWS::CloudFormation::Interface: 56 | ParameterGroups: 57 | - Label: 58 | default: "GitHub Settings" 59 | Parameters: 60 | - Repo 61 | - Branch 62 | - OAuthToken 63 | - Label: 64 | default: "CodePipeline Settings" 65 | Parameters: 66 | - PipelineName 67 | - Email 68 | - Label: 69 | default: "Test Stack Settings" 70 | Parameters: 71 | - TestStackName 72 | - TemplateFileName 73 | - TestStackConfig 74 | - Label: 75 | default: "Production Stack Settings" 76 | Parameters: 77 | - ChangeSetName 78 | - ProdStackName 79 | - ProdStackConfig 80 | 81 | Resources: 82 | ArtifactStoreBucket: 83 | Type: AWS::S3::Bucket 84 | Properties: 85 | VersioningConfiguration: 86 | Status: Enabled 87 | 88 | CodePipelineSNSTopic: 89 | Type: AWS::SNS::Topic 90 | Properties: 91 | Subscription: 92 | - Endpoint: !Ref Email 93 | Protocol: email 94 | 95 | Pipeline: 96 | Type: AWS::CodePipeline::Pipeline 97 | Properties: 98 | ArtifactStore: 99 | Location: !Ref 'ArtifactStoreBucket' 100 | Type: S3 101 | DisableInboundStageTransitions: [] 102 | Name: !Ref 'PipelineName' 103 | RoleArn: !GetAtt [PipelineRole, Arn] 104 | Stages: 105 | - Name: Source 106 | Actions: 107 | - Name: TemplateSource 108 | ActionTypeId: 109 | Category: Source 110 | Owner: ThirdParty 111 | Provider: GitHub 112 | Version: '1' 113 | Configuration: 114 | Owner: !Ref GithubOrg 115 | PollForSourceChanges: true 116 | Repo: !Ref Repo 117 | Branch: !Ref Branch 118 | OAuthToken: !Ref OAuthToken 119 | OutputArtifacts: 120 | - Name: TemplateSource 121 | RunOrder: '1' 122 | - Name: TestStage 123 | Actions: 124 | - Name: CreateStack 125 | ActionTypeId: 126 | Category: Deploy 127 | Owner: AWS 128 | Provider: CloudFormation 129 | Version: '1' 130 | InputArtifacts: 131 | - Name: TemplateSource 132 | Configuration: 133 | ActionMode: REPLACE_ON_FAILURE 134 | Capabilities: CAPABILITY_IAM 135 | RoleArn: !GetAtt [CFNRole, Arn] 136 | StackName: !Ref TestStackName 137 | TemplateConfiguration: !Sub "TemplateSource::${TestStackConfig}" 138 | TemplatePath: !Sub "TemplateSource::${TemplateFileName}" 139 | RunOrder: '1' 140 | - Name: ApproveTestStack 141 | ActionTypeId: 142 | Category: Approval 143 | Owner: AWS 144 | Provider: Manual 145 | Version: '1' 146 | Configuration: 147 | NotificationArn: !Ref CodePipelineSNSTopic 148 | CustomData: !Sub 'Do you want to create a change set against the production stack and delete the ${TestStackName} stack?' 149 | RunOrder: '2' 150 | - Name: DeleteTestStack 151 | ActionTypeId: 152 | Category: Deploy 153 | Owner: AWS 154 | Provider: CloudFormation 155 | Version: '1' 156 | Configuration: 157 | ActionMode: DELETE_ONLY 158 | RoleArn: !GetAtt [CFNRole, Arn] 159 | StackName: !Ref TestStackName 160 | RunOrder: '3' 161 | - Name: ProdStage 162 | Actions: 163 | - Name: CreateChangeSet 164 | ActionTypeId: 165 | Category: Deploy 166 | Owner: AWS 167 | Provider: CloudFormation 168 | Version: '1' 169 | InputArtifacts: 170 | - Name: TemplateSource 171 | Configuration: 172 | ActionMode: CHANGE_SET_REPLACE 173 | Capabilities: CAPABILITY_IAM 174 | RoleArn: !GetAtt [CFNRole, Arn] 175 | StackName: !Ref ProdStackName 176 | ChangeSetName: !Ref ChangeSetName 177 | TemplateConfiguration: !Sub "TemplateSource::${ProdStackConfig}" 178 | TemplatePath: !Sub "TemplateSource::${TemplateFileName}" 179 | RunOrder: '1' 180 | - Name: ApproveChangeSet 181 | ActionTypeId: 182 | Category: Approval 183 | Owner: AWS 184 | Provider: Manual 185 | Version: '1' 186 | Configuration: 187 | NotificationArn: !Ref CodePipelineSNSTopic 188 | CustomData: !Sub 'A new change set was created for the ${ProdStackName} stack. Do you want to implement the changes?' 189 | RunOrder: '2' 190 | - Name: ExecuteChangeSet 191 | ActionTypeId: 192 | Category: Deploy 193 | Owner: AWS 194 | Provider: CloudFormation 195 | Version: '1' 196 | Configuration: 197 | ActionMode: CHANGE_SET_EXECUTE 198 | ChangeSetName: !Ref ChangeSetName 199 | RoleArn: !GetAtt [CFNRole, Arn] 200 | StackName: !Ref ProdStackName 201 | RunOrder: '3' 202 | CFNRole: 203 | Type: AWS::IAM::Role 204 | Properties: 205 | AssumeRolePolicyDocument: 206 | Statement: 207 | - Action: ['sts:AssumeRole'] 208 | Effect: Allow 209 | Principal: 210 | Service: [cloudformation.amazonaws.com] 211 | Version: '2012-10-17' 212 | Path: / 213 | Policies: 214 | - PolicyName: CloudFormationRole 215 | PolicyDocument: 216 | Version: '2012-10-17' 217 | Statement: 218 | - Action: 219 | - '*' 220 | Effect: Allow 221 | Resource: '*' 222 | 223 | PipelineRole: 224 | Type: AWS::IAM::Role 225 | Properties: 226 | AssumeRolePolicyDocument: 227 | Statement: 228 | - Action: ['sts:AssumeRole'] 229 | Effect: Allow 230 | Principal: 231 | Service: [codepipeline.amazonaws.com] 232 | Version: '2012-10-17' 233 | Path: / 234 | Policies: 235 | - PolicyName: CodePipelineAccess 236 | PolicyDocument: 237 | Version: '2012-10-17' 238 | Statement: 239 | - Action: 240 | - 's3:*' 241 | - 'cloudformation:CreateStack' 242 | - 'cloudformation:DescribeStacks' 243 | - 'cloudformation:DeleteStack' 244 | - 'cloudformation:UpdateStack' 245 | - 'cloudformation:CreateChangeSet' 246 | - 'cloudformation:ExecuteChangeSet' 247 | - 'cloudformation:DeleteChangeSet' 248 | - 'cloudformation:DescribeChangeSet' 249 | - 'cloudformation:SetStackPolicy' 250 | - 'iam:PassRole' 251 | - 'sns:Publish' 252 | Effect: Allow 253 | Resource: '*' 254 | -------------------------------------------------------------------------------- /nested-stacks/vpc/three-sub-nat-gateway.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys a VPC, with 3 public and private subnets spread 3 | across 3 Availabilty Zones. It deploys an Internet Gateway, with a default 4 | route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ), 5 | and default routes for them in the private subnets. 6 | 7 | Parameters: 8 | 9 | EnvironmentName: 10 | Description: An environment name that will be prefixed to resource names 11 | Type: String 12 | 13 | VpcCIDR: 14 | Description: Please enter the IP range (CIDR notation) for this VPC 15 | Type: String 16 | Default: 10.192.0.0/16 17 | 18 | PublicSubnet1CIDR: 19 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 20 | Type: String 21 | Default: 10.192.10.0/24 22 | 23 | PublicSubnet2CIDR: 24 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 25 | Type: String 26 | Default: 10.192.11.0/24 27 | 28 | PublicSubnet3CIDR: 29 | Description: Please enter the IP range (CIDR notation) for the public subnet in the third Availability Zone 30 | Type: String 31 | Default: 10.192.12.0/24 32 | 33 | PrivateSubnet1CIDR: 34 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 35 | Type: String 36 | Default: 10.192.20.0/24 37 | 38 | PrivateSubnet2CIDR: 39 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 40 | Type: String 41 | Default: 10.192.21.0/24 42 | 43 | PrivateSubnet3CIDR: 44 | Description: Please enter the IP range (CIDR notation) for the private subnet in the third Availability Zone 45 | Type: String 46 | Default: 10.192.22.0/24 47 | 48 | Resources: 49 | 50 | VPC: 51 | Type: AWS::EC2::VPC 52 | Properties: 53 | EnableDnsHostnames: true 54 | CidrBlock: !Ref VpcCIDR 55 | Tags: 56 | - Key: Name 57 | Value: !Ref EnvironmentName 58 | 59 | InternetGateway: 60 | Type: AWS::EC2::InternetGateway 61 | Properties: 62 | Tags: 63 | - Key: Name 64 | Value: !Ref EnvironmentName 65 | 66 | InternetGatewayAttachment: 67 | Type: AWS::EC2::VPCGatewayAttachment 68 | Properties: 69 | InternetGatewayId: !Ref InternetGateway 70 | VpcId: !Ref VPC 71 | 72 | PublicSubnet1: 73 | Type: AWS::EC2::Subnet 74 | Properties: 75 | VpcId: !Ref VPC 76 | AvailabilityZone: !Select [ 0, !GetAZs ] 77 | CidrBlock: !Ref PublicSubnet1CIDR 78 | MapPublicIpOnLaunch: true 79 | Tags: 80 | - Key: Name 81 | Value: !Sub ${EnvironmentName} Public Subnet (AZ1) 82 | 83 | PublicSubnet2: 84 | Type: AWS::EC2::Subnet 85 | Properties: 86 | VpcId: !Ref VPC 87 | AvailabilityZone: !Select [ 1, !GetAZs ] 88 | CidrBlock: !Ref PublicSubnet2CIDR 89 | MapPublicIpOnLaunch: true 90 | Tags: 91 | - Key: Name 92 | Value: !Sub ${EnvironmentName} Public Subnet (AZ2) 93 | 94 | PublicSubnet3: 95 | Type: AWS::EC2::Subnet 96 | Properties: 97 | VpcId: !Ref VPC 98 | AvailabilityZone: !Select [ 2, !GetAZs ] 99 | CidrBlock: !Ref PublicSubnet3CIDR 100 | MapPublicIpOnLaunch: true 101 | Tags: 102 | - Key: Name 103 | Value: !Sub ${EnvironmentName} Public Subnet (AZ3) 104 | 105 | PrivateSubnet1: 106 | Type: AWS::EC2::Subnet 107 | Properties: 108 | VpcId: !Ref VPC 109 | AvailabilityZone: !Select [ 0, !GetAZs ] 110 | CidrBlock: !Ref PrivateSubnet1CIDR 111 | MapPublicIpOnLaunch: false 112 | Tags: 113 | - Key: Name 114 | Value: !Sub ${EnvironmentName} Private Subnet (AZ1) 115 | 116 | PrivateSubnet2: 117 | Type: AWS::EC2::Subnet 118 | Properties: 119 | VpcId: !Ref VPC 120 | AvailabilityZone: !Select [ 1, !GetAZs ] 121 | CidrBlock: !Ref PrivateSubnet2CIDR 122 | MapPublicIpOnLaunch: false 123 | Tags: 124 | - Key: Name 125 | Value: !Sub ${EnvironmentName} Private Subnet (AZ2) 126 | 127 | PrivateSubnet3: 128 | Type: AWS::EC2::Subnet 129 | Properties: 130 | VpcId: !Ref VPC 131 | AvailabilityZone: !Select [ 2, !GetAZs ] 132 | CidrBlock: !Ref PrivateSubnet3CIDR 133 | MapPublicIpOnLaunch: false 134 | Tags: 135 | - Key: Name 136 | Value: !Sub ${EnvironmentName} Private Subnet (AZ3) 137 | 138 | NatGateway1EIP: 139 | Type: AWS::EC2::EIP 140 | DependsOn: InternetGatewayAttachment 141 | Properties: 142 | Domain: vpc 143 | 144 | NatGateway2EIP: 145 | Type: AWS::EC2::EIP 146 | DependsOn: InternetGatewayAttachment 147 | Properties: 148 | Domain: vpc 149 | 150 | NatGateway3EIP: 151 | Type: AWS::EC2::EIP 152 | DependsOn: InternetGatewayAttachment 153 | Properties: 154 | Domain: vpc 155 | 156 | NatGateway1: 157 | Type: AWS::EC2::NatGateway 158 | Properties: 159 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 160 | SubnetId: !Ref PublicSubnet1 161 | 162 | NatGateway2: 163 | Type: AWS::EC2::NatGateway 164 | Properties: 165 | AllocationId: !GetAtt NatGateway2EIP.AllocationId 166 | SubnetId: !Ref PublicSubnet2 167 | 168 | NatGateway3: 169 | Type: AWS::EC2::NatGateway 170 | Properties: 171 | AllocationId: !GetAtt NatGateway3EIP.AllocationId 172 | SubnetId: !Ref PublicSubnet3 173 | 174 | PublicRouteTable: 175 | Type: AWS::EC2::RouteTable 176 | Properties: 177 | VpcId: !Ref VPC 178 | Tags: 179 | - Key: Name 180 | Value: !Sub ${EnvironmentName} Public Routes 181 | 182 | DefaultPublicRoute: 183 | Type: AWS::EC2::Route 184 | DependsOn: InternetGatewayAttachment 185 | Properties: 186 | RouteTableId: !Ref PublicRouteTable 187 | DestinationCidrBlock: 0.0.0.0/0 188 | GatewayId: !Ref InternetGateway 189 | 190 | PublicSubnet1RouteTableAssociation: 191 | Type: AWS::EC2::SubnetRouteTableAssociation 192 | Properties: 193 | RouteTableId: !Ref PublicRouteTable 194 | SubnetId: !Ref PublicSubnet1 195 | 196 | PublicSubnet2RouteTableAssociation: 197 | Type: AWS::EC2::SubnetRouteTableAssociation 198 | Properties: 199 | RouteTableId: !Ref PublicRouteTable 200 | SubnetId: !Ref PublicSubnet2 201 | 202 | PublicSubnet3RouteTableAssociation: 203 | Type: AWS::EC2::SubnetRouteTableAssociation 204 | Properties: 205 | RouteTableId: !Ref PublicRouteTable 206 | SubnetId: !Ref PublicSubnet3 207 | 208 | 209 | PrivateRouteTable1: 210 | Type: AWS::EC2::RouteTable 211 | Properties: 212 | VpcId: !Ref VPC 213 | Tags: 214 | - Key: Name 215 | Value: !Sub ${EnvironmentName} Private Routes (AZ1) 216 | 217 | DefaultPrivateRoute1: 218 | Type: AWS::EC2::Route 219 | Properties: 220 | RouteTableId: !Ref PrivateRouteTable1 221 | DestinationCidrBlock: 0.0.0.0/0 222 | NatGatewayId: !Ref NatGateway1 223 | 224 | PrivateSubnet1RouteTableAssociation: 225 | Type: AWS::EC2::SubnetRouteTableAssociation 226 | Properties: 227 | RouteTableId: !Ref PrivateRouteTable1 228 | SubnetId: !Ref PrivateSubnet1 229 | 230 | PrivateRouteTable2: 231 | Type: AWS::EC2::RouteTable 232 | Properties: 233 | VpcId: !Ref VPC 234 | Tags: 235 | - Key: Name 236 | Value: !Sub ${EnvironmentName} Private Routes (AZ2) 237 | 238 | DefaultPrivateRoute2: 239 | Type: AWS::EC2::Route 240 | Properties: 241 | RouteTableId: !Ref PrivateRouteTable2 242 | DestinationCidrBlock: 0.0.0.0/0 243 | NatGatewayId: !Ref NatGateway2 244 | 245 | PrivateSubnet2RouteTableAssociation: 246 | Type: AWS::EC2::SubnetRouteTableAssociation 247 | Properties: 248 | RouteTableId: !Ref PrivateRouteTable2 249 | SubnetId: !Ref PrivateSubnet2 250 | 251 | PrivateRouteTable3: 252 | Type: AWS::EC2::RouteTable 253 | Properties: 254 | VpcId: !Ref VPC 255 | Tags: 256 | - Key: Name 257 | Value: !Sub ${EnvironmentName} Private Routes (AZ3) 258 | 259 | DefaultPrivateRoute3: 260 | Type: AWS::EC2::Route 261 | Properties: 262 | RouteTableId: !Ref PrivateRouteTable3 263 | DestinationCidrBlock: 0.0.0.0/0 264 | NatGatewayId: !Ref NatGateway3 265 | 266 | PrivateSubnet3RouteTableAssociation: 267 | Type: AWS::EC2::SubnetRouteTableAssociation 268 | Properties: 269 | RouteTableId: !Ref PrivateRouteTable3 270 | SubnetId: !Ref PrivateSubnet3 271 | 272 | Outputs: 273 | 274 | VPC: 275 | Description: A reference to the created VPC 276 | Value: !Ref VPC 277 | Export: 278 | Name: !Sub "${EnvironmentName}-VPC" 279 | 280 | PublicSubnets: 281 | Description: A list of the public subnets 282 | Value: !Join [ ",", [ !Ref PublicSubnet1,!Ref PublicSubnet2,!Ref PublicSubnet3 ]] 283 | Export: 284 | Name: !Sub "${EnvironmentName}-PublicSubnets" 285 | 286 | PrivateSubnets: 287 | Description: A list of the private subnets 288 | Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2, !Ref PrivateSubnet3 ]] 289 | Export: 290 | Name: !Sub "${EnvironmentName}-PrivateSubnets" 291 | 292 | PublicSubnet1: 293 | Description: A reference to the public subnet in the 1st Availability Zone 294 | Value: !Ref PublicSubnet1 295 | Export: 296 | Name: !Sub "${EnvironmentName}-PublicSubnet1" 297 | 298 | PublicSubnet2: 299 | Description: A reference to the public subnet in the 2nd Availability Zone 300 | Value: !Ref PublicSubnet2 301 | Export: 302 | Name: !Sub "${EnvironmentName}-PublicSubnet2" 303 | 304 | PublicSubnet3: 305 | Description: A reference to the public subnet in the 3rd Availability Zone 306 | Value: !Ref PublicSubnet3 307 | Export: 308 | Name: !Sub "${EnvironmentName}-PublicSubnet3" 309 | 310 | PrivateSubnet1: 311 | Description: A reference to the private subnet in the 1st Availability Zone 312 | Value: !Ref PrivateSubnet1 313 | Export: 314 | Name: !Sub "${EnvironmentName}-PrivateSubnet1" 315 | 316 | PrivateSubnet2: 317 | Description: A reference to the private subnet in the 2nd Availability Zone 318 | Value: !Ref PrivateSubnet2 319 | Export: 320 | Name: !Sub "${EnvironmentName}-PrivateSubnet2" 321 | 322 | PrivateSubnet3: 323 | Description: A reference to the private subnet in the 3rd Availability Zone 324 | Value: !Ref PrivateSubnet3 325 | Export: 326 | Name: !Sub "${EnvironmentName}-PrivateSubnet3" 327 | -------------------------------------------------------------------------------- /nested-stacks/ecs/cluster-in-vpc.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys an ECS cluster to the provided VPC and subnets 3 | using an Auto Scaling Group 4 | 5 | Parameters: 6 | 7 | EnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | 11 | InstanceType: 12 | Description: Which instance type should we use to build the ECS cluster? 13 | Type: String 14 | Default: c4.large 15 | 16 | ClusterSize: 17 | Description: How many ECS hosts do you want to initially deploy? 18 | Type: Number 19 | Default: 2 20 | 21 | VPC: 22 | Description: Choose which VPC this ECS cluster should be deployed to 23 | Type: AWS::EC2::VPC::Id 24 | 25 | Subnets: 26 | Description: Choose which subnets this ECS cluster should be deployed to 27 | Type: List 28 | 29 | SecurityGroup: 30 | Description: Select the Security Group to use for the ECS cluster hosts 31 | Type: AWS::EC2::SecurityGroup::Id 32 | 33 | Mappings: 34 | 35 | # You can find the latest available on this page of our documentation: 36 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html 37 | # (note the AMI identifier is region specific) 38 | AWSRegionToAMI: 39 | us-east-2: 40 | AMI: ami-028a9de0a7e353ed9 41 | us-east-1: 42 | AMI: ami-00129b193dc81bc31 43 | us-west-2: 44 | AMI: ami-00d4f478 45 | us-west-1: 46 | AMI: ami-0d438d09af26c9583 47 | 48 | Resources: 49 | 50 | ECSCluster: 51 | Type: AWS::ECS::Cluster 52 | Properties: 53 | ClusterName: !Ref EnvironmentName 54 | 55 | ECSAutoScalingGroup: 56 | Type: AWS::AutoScaling::AutoScalingGroup 57 | Properties: 58 | VPCZoneIdentifier: !Ref Subnets 59 | LaunchConfigurationName: !Ref ECSLaunchConfiguration 60 | MinSize: !Ref ClusterSize 61 | MaxSize: !Ref ClusterSize 62 | DesiredCapacity: !Ref ClusterSize 63 | Tags: 64 | - Key: Name 65 | Value: !Sub ${EnvironmentName} ECS host 66 | PropagateAtLaunch: true 67 | CreationPolicy: 68 | ResourceSignal: 69 | Timeout: PT15M 70 | UpdatePolicy: 71 | AutoScalingRollingUpdate: 72 | MinInstancesInService: 1 73 | MaxBatchSize: 1 74 | PauseTime: PT15M 75 | SuspendProcesses: 76 | - HealthCheck 77 | - ReplaceUnhealthy 78 | - AZRebalance 79 | - AlarmNotification 80 | - ScheduledActions 81 | WaitOnResourceSignals: true 82 | 83 | ECSLaunchConfiguration: 84 | Type: AWS::AutoScaling::LaunchConfiguration 85 | Properties: 86 | ImageId: !FindInMap [AWSRegionToAMI, !Ref "AWS::Region", AMI] 87 | InstanceType: !Ref InstanceType 88 | SecurityGroups: 89 | - !Ref SecurityGroup 90 | IamInstanceProfile: !Ref ECSInstanceProfile 91 | UserData: 92 | "Fn::Base64": !Sub | 93 | #!/bin/bash 94 | yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 95 | yum install -y aws-cfn-bootstrap 96 | /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 97 | /opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSAutoScalingGroup 98 | 99 | Metadata: 100 | AWS::CloudFormation::Init: 101 | config: 102 | packages: 103 | yum: 104 | awslogs: [] 105 | 106 | commands: 107 | 01_add_instance_to_cluster: 108 | command: !Sub echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config 109 | files: 110 | "/etc/cfn/cfn-hup.conf": 111 | mode: 000400 112 | owner: root 113 | group: root 114 | content: !Sub | 115 | [main] 116 | stack=${AWS::StackId} 117 | region=${AWS::Region} 118 | 119 | "/etc/cfn/hooks.d/cfn-auto-reloader.conf": 120 | content: !Sub | 121 | [cfn-auto-reloader-hook] 122 | triggers=post.update 123 | path=Resources.ECSLaunchConfiguration.Metadata.AWS::CloudFormation::Init 124 | action=/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 125 | 126 | services: 127 | 128 | "/etc/awslogs/awscli.conf": 129 | content: !Sub | 130 | [plugins] 131 | cwlogs = cwlogs 132 | [default] 133 | region = ${AWS::Region} 134 | 135 | "/etc/awslogs/awslogs.conf": 136 | content: !Sub | 137 | [general] 138 | state_file = /var/lib/awslogs/agent-state 139 | 140 | [/var/log/dmesg] 141 | file = /var/log/dmesg 142 | log_group_name = ${ECSCluster}-/var/log/dmesg 143 | log_stream_name = ${ECSCluster} 144 | 145 | [/var/log/messages] 146 | file = /var/log/messages 147 | log_group_name = ${ECSCluster}-/var/log/messages 148 | log_stream_name = ${ECSCluster} 149 | datetime_format = %b %d %H:%M:%S 150 | 151 | [/var/log/docker] 152 | file = /var/log/docker 153 | log_group_name = ${ECSCluster}-/var/log/docker 154 | log_stream_name = ${ECSCluster} 155 | datetime_format = %Y-%m-%dT%H:%M:%S.%f 156 | 157 | [/var/log/ecs/ecs-init.log] 158 | file = /var/log/ecs/ecs-init.log.* 159 | log_group_name = ${ECSCluster}-/var/log/ecs/ecs-init.log 160 | log_stream_name = ${ECSCluster} 161 | datetime_format = %Y-%m-%dT%H:%M:%SZ 162 | 163 | [/var/log/ecs/ecs-agent.log] 164 | file = /var/log/ecs/ecs-agent.log.* 165 | log_group_name = ${ECSCluster}-/var/log/ecs/ecs-agent.log 166 | log_stream_name = ${ECSCluster} 167 | datetime_format = %Y-%m-%dT%H:%M:%SZ 168 | 169 | [/var/log/ecs/audit.log] 170 | file = /var/log/ecs/audit.log.* 171 | log_group_name = ${ECSCluster}-/var/log/ecs/audit.log 172 | log_stream_name = ${ECSCluster} 173 | datetime_format = %Y-%m-%dT%H:%M:%SZ 174 | 175 | sysvinit: 176 | cfn-hup: 177 | enabled: true 178 | ensureRunning: true 179 | files: 180 | - /etc/cfn/cfn-hup.conf 181 | - /etc/cfn/hooks.d/cfn-auto-reloader.conf 182 | awslogs: 183 | enabled: true 184 | ensureRunning: true 185 | files: 186 | - /etc/awslogs/awslogs.conf 187 | - /etc/awslogs/awscli.conf 188 | 189 | # This IAM Role is attached to all of the ECS hosts. It is based on the default role 190 | # published here: 191 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html 192 | # 193 | # You can add other IAM policy statements here to allow access from your ECS hosts 194 | # to other AWS services. Please note that this role will be used by ALL containers 195 | # running on the ECS host. 196 | 197 | ECSRole: 198 | Type: AWS::IAM::Role 199 | Properties: 200 | Path: / 201 | RoleName: !Sub ${EnvironmentName}-ECSRole-${AWS::Region} 202 | AssumeRolePolicyDocument: | 203 | { 204 | "Statement": [{ 205 | "Action": "sts:AssumeRole", 206 | "Effect": "Allow", 207 | "Principal": { 208 | "Service": "ec2.amazonaws.com" 209 | } 210 | }] 211 | } 212 | Policies: 213 | - PolicyName: ecs-service 214 | PolicyDocument: | 215 | { 216 | "Statement": [{ 217 | "Effect": "Allow", 218 | "Action": [ 219 | "ecs:CreateCluster", 220 | "ecs:DeregisterContainerInstance", 221 | "ecs:DiscoverPollEndpoint", 222 | "ecs:Poll", 223 | "ecs:RegisterContainerInstance", 224 | "ecs:StartTelemetrySession", 225 | "ecs:Submit*", 226 | "logs:CreateLogStream", 227 | "logs:PutLogEvents", 228 | "ecr:BatchCheckLayerAvailability", 229 | "ecr:BatchGetImage", 230 | "ecr:GetDownloadUrlForLayer", 231 | "ecr:GetAuthorizationToken", 232 | "ssm:DescribeAssociation", 233 | "ssm:GetDeployablePatchSnapshotForInstance", 234 | "ssm:GetDocument", 235 | "ssm:GetManifest", 236 | "ssm:GetParameters", 237 | "ssm:ListAssociations", 238 | "ssm:ListInstanceAssociations", 239 | "ssm:PutInventory", 240 | "ssm:PutComplianceItems", 241 | "ssm:PutConfigurePackageResult", 242 | "ssm:UpdateAssociationStatus", 243 | "ssm:UpdateInstanceAssociationStatus", 244 | "ssm:UpdateInstanceInformation", 245 | "ec2messages:AcknowledgeMessage", 246 | "ec2messages:DeleteMessage", 247 | "ec2messages:FailMessage", 248 | "ec2messages:GetEndpoint", 249 | "ec2messages:GetMessages", 250 | "ec2messages:SendReply", 251 | "cloudwatch:PutMetricData", 252 | "ec2:DescribeInstanceStatus", 253 | "ds:CreateComputer", 254 | "ds:DescribeDirectories", 255 | "logs:CreateLogGroup", 256 | "logs:CreateLogStream", 257 | "logs:DescribeLogGroups", 258 | "logs:DescribeLogStreams", 259 | "logs:PutLogEvents", 260 | "s3:PutObject", 261 | "s3:GetObject", 262 | "s3:AbortMultipartUpload", 263 | "s3:ListMultipartUploadParts", 264 | "s3:ListBucket", 265 | "s3:ListBucketMultipartUploads" 266 | ], 267 | "Resource": "*" 268 | }] 269 | } 270 | 271 | ECSInstanceProfile: 272 | Type: AWS::IAM::InstanceProfile 273 | Properties: 274 | Path: / 275 | Roles: 276 | - !Ref ECSRole 277 | 278 | Outputs: 279 | 280 | Cluster: 281 | Description: A reference to the ECS cluster 282 | Value: !Ref ECSCluster 283 | -------------------------------------------------------------------------------- /pipelines/cicd/cloudformation-singlestage.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | 4 | Description: > 5 | Continous Delievery: This template 6 | builds an AWS CodePipeline pipeline that implements a continuous delivery release 7 | process for AWS CloudFormation stacks. Push change to github and it will build 8 | the code, then release it to test. Once approvded in test, will release in 9 | staging. Once approved in staging, it will create 10 | a changeset in prod. Approve the changeset and it will deploy in prod. 11 | 12 | Parameters: 13 | RelCloudFormationTemplatePath: 14 | Description: Path of the cloudformation template, relative to your codebase 15 | Type: String 16 | Default: aws/cloudformation/fargate-no-elb.yaml 17 | CloudFormationTemplateParameters: 18 | Description: Test stack - Relative JSON config file that contains parameters passed to RelCloudFormationTemplatePath CloudFormation 19 | Type: String 20 | Default: aws/cloudformation/parameters/test--ecs-codepipeline-parameters.json 21 | BuildParameterOverrides: 22 | Default: '{"ContainerImage":{"Fn::GetParam":["MyAppBuild","build.json","CodeImage"]}}' 23 | Description: CloudFormation paramater overrides. build.json is exported from your buildspec.yaml 24 | Type: String 25 | ApprovalNotificationArn: 26 | Description: SNS ARN to receive notifications for approvals 27 | Type: String 28 | Default: arn:aws:sns:us-east-1:1111111:code-promotion-approvals 29 | GithubOrg: 30 | Description: The GitHub organization 31 | Type: String 32 | GithubOAuthToken: 33 | Description: The GitHub Personal Access Token so CodePipeline can get the code. https://github.com/settings/tokens. Needs repo scope. 34 | Type: String 35 | NoEcho: true 36 | BuildTimeout: 37 | Description: Timeout in minutes for the build 38 | Type: Number 39 | Default: 5 40 | BuildTimeoutTest: 41 | Description: Timeout in minutes for the lint and tests 42 | Type: Number 43 | Default: 5 44 | CodeBuildImageTest: 45 | Description: Image for the CodeBuild container that runs linting and test cases. Omitting aws/codebuild prefix will pull image from dockerhub 46 | Type: String 47 | Default: golang:1.9.3 48 | CodeBuildImage: 49 | Description: Image for the CodeBuild container that builds the final app docker image. See https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 50 | Type: String 51 | Default: aws/codebuild/docker:1.12.1 52 | CodeEntryPointFilePath: 53 | Description: The path to the root of your source code base, relative to the project root 54 | Type: String 55 | Default: cmd/appName/main.go 56 | RelDockerfilePath: 57 | Description: Path to the Dockerfile to use for the build, relative to your CodeEntryPointFilePath 58 | Type: String 59 | Default: build/Dockerfile 60 | CodeBuildProjectTestBuildSpecPath: 61 | Description: Path to the buildspec used to run testcases, relative to your codebase 62 | Type: String 63 | Default: aws/codebuild/testcases-buildspec.yaml 64 | CodeBuildProjectBuildSpecPath: 65 | Description: Path to the buildspec used to build your app image, relative to your codebase 66 | Type: String 67 | Default: aws/codebuild/app-buildspec.yaml 68 | 69 | Metadata: 70 | AWS::CloudFormation::Interface: 71 | ParameterGroups: 72 | - Label: 73 | default: "CloudFormation Template Settings" 74 | Parameters: 75 | - RelCloudFormationTemplatePath 76 | - Label: 77 | default: "CloudFormation template parameters" 78 | Parameters: 79 | - CloudFormationTemplateParameters 80 | - Label: 81 | default: "Test cases and linting" 82 | Parameters: 83 | - CodeBuildImageTest 84 | - CodeBuildProjectTestBuildSpecPath 85 | - BuildTimeoutTest 86 | - Label: 87 | default: "Build" 88 | Parameters: 89 | - CodeBuildProjectBuildSpecPath 90 | - CodeEntryPointFilePath 91 | - RelDockerfilePath 92 | - BuildParameterOverrides 93 | - CodeBuildImage 94 | - BuildTimeout 95 | 96 | Resources: 97 | ArtifactStoreBucket: 98 | Type: AWS::S3::Bucket 99 | Properties: 100 | VersioningConfiguration: 101 | Status: Enabled 102 | 103 | CodeBuildRole: 104 | Type: AWS::IAM::Role 105 | Properties: 106 | AssumeRolePolicyDocument: 107 | Statement: 108 | - Action: ['sts:AssumeRole'] 109 | Effect: Allow 110 | Principal: 111 | Service: [codebuild.amazonaws.com] 112 | Version: '2012-10-17' 113 | Path: / 114 | Policies: 115 | - PolicyName: CodeBuild 116 | PolicyDocument: 117 | Version: '2012-10-17' 118 | Statement: 119 | - Action: 120 | - "logs:CreateLogGroup" 121 | - "logs:CreateLogStream" 122 | - "logs:PutLogEvents" 123 | Effect: Allow 124 | Resource: 125 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" 126 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*" 127 | - Action: 128 | - "s3:PutObject" 129 | - "s3:GetObject" 130 | - "s3:GetObjectVersion" 131 | Effect: Allow 132 | Resource: 133 | - "arn:aws:s3:::codepipeline-us-east-1-*" 134 | - Action: 135 | - 's3:*' 136 | - 'ecr:*' 137 | Effect: Allow 138 | Resource: '*' 139 | 140 | CodeBuildProjectTest: 141 | Type: AWS::CodeBuild::Project 142 | Properties: 143 | Name: !Join ['--', ['testcases',!Ref 'AWS::StackName']] 144 | Artifacts: 145 | Type: CODEPIPELINE 146 | Environment: 147 | ComputeType: BUILD_GENERAL1_LARGE 148 | PrivilegedMode: false 149 | Type: LINUX_CONTAINER 150 | Image: !Ref CodeBuildImageTest 151 | EnvironmentVariables: 152 | - Name: CODE_ENTRY_POINT_FILE_PATH 153 | Value: !Ref CodeEntryPointFilePath 154 | - Name: AWS_DEFAULT_REGION 155 | Value: !Ref AWS::Region 156 | - Name: AWS_ACCOUNT_ID 157 | Value: !Ref AWS::AccountId 158 | - Name: IMAGE_REPO_NAME 159 | Value: !Join [ '/', [ !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] , !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] ] ] 160 | ServiceRole: !Ref CodeBuildRole 161 | Source: 162 | Type: CODEPIPELINE 163 | BuildSpec: !Ref CodeBuildProjectTestBuildSpecPath 164 | TimeoutInMinutes: !Ref BuildTimeoutTest 165 | 166 | CodeBuildProject: 167 | Type: AWS::CodeBuild::Project 168 | Properties: 169 | Name: !Ref 'AWS::StackName' 170 | Artifacts: 171 | Type: CODEPIPELINE 172 | Environment: 173 | ComputeType: BUILD_GENERAL1_LARGE 174 | PrivilegedMode: true 175 | Type: LINUX_CONTAINER 176 | Image: !Ref CodeBuildImage 177 | EnvironmentVariables: 178 | - Name: CODE_ENTRY_POINT_FILE_PATH 179 | Value: !Ref CodeEntryPointFilePath 180 | - Name: DOCKERFILE_PATH 181 | Value: !Ref RelDockerfilePath 182 | - Name: AWS_DEFAULT_REGION 183 | Value: !Ref AWS::Region 184 | - Name: AWS_ACCOUNT_ID 185 | Value: !Ref AWS::AccountId 186 | - Name: IMAGE_REPO_NAME 187 | Value: !Join [ '/', [ !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] , 'branches' ] ] 188 | ServiceRole: !Ref CodeBuildRole 189 | Source: 190 | Type: CODEPIPELINE 191 | BuildSpec: !Ref CodeBuildProjectBuildSpecPath 192 | TimeoutInMinutes: !Ref BuildTimeout 193 | 194 | Pipeline: 195 | Type: AWS::CodePipeline::Pipeline 196 | Properties: 197 | ArtifactStore: 198 | Location: !Ref 'ArtifactStoreBucket' 199 | Type: S3 200 | DisableInboundStageTransitions: [] 201 | Name: !Ref AWS::StackName 202 | RoleArn: !GetAtt PipelineRole.Arn 203 | Stages: 204 | - Name: Source 205 | Actions: 206 | - Name: Source 207 | ActionTypeId: 208 | Category: Source 209 | Owner: ThirdParty 210 | Provider: GitHub 211 | Version: '1' 212 | OutputArtifacts: 213 | - Name: MyApp 214 | Configuration: 215 | Owner: !Ref GithubOrg 216 | Repo: !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] 217 | PollForSourceChanges: true 218 | Branch: !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] 219 | OAuthToken: !Ref GithubOAuthToken 220 | RunOrder: 1 221 | - Name: Testcases 222 | Actions: 223 | - Name: CodeBuild 224 | InputArtifacts: 225 | - Name: MyApp 226 | ActionTypeId: 227 | Category: Build 228 | Owner: AWS 229 | Provider: CodeBuild 230 | Version: '1' 231 | OutputArtifacts: 232 | - Name: MyAppTestcases 233 | Configuration: 234 | ProjectName: !Ref CodeBuildProjectTest 235 | RunOrder: 1 236 | - Name: Build 237 | Actions: 238 | - Name: CodeBuild 239 | InputArtifacts: 240 | - Name: MyApp 241 | ActionTypeId: 242 | Category: Build 243 | Owner: AWS 244 | Provider: CodeBuild 245 | Version: '1' 246 | OutputArtifacts: 247 | - Name: MyAppBuild 248 | Configuration: 249 | ProjectName: !Ref CodeBuildProject 250 | RunOrder: 1 251 | - Name: !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] 252 | Actions: 253 | - Name: !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] 254 | InputArtifacts: 255 | - Name: MyApp 256 | - Name: MyAppBuild 257 | ActionTypeId: 258 | Category: Deploy 259 | Owner: AWS 260 | Provider: CloudFormation 261 | Version: '1' 262 | RunOrder: 1 263 | Configuration: 264 | ActionMode: CHANGE_SET_REPLACE 265 | RoleArn: !GetAtt CFNRole.Arn 266 | Capabilities: CAPABILITY_IAM 267 | StackName: !Join [ '--', [ 'test', !Ref 'AWS::StackName' ] ] 268 | ChangeSetName: ChangeSet 269 | TemplatePath: !Sub "MyApp::${RelCloudFormationTemplatePath}" 270 | TemplateConfiguration: !Sub "MyApp::${CloudFormationTemplateParameters}" 271 | ParameterOverrides: !Ref BuildParameterOverrides 272 | - Name: ExecuteChangeSet 273 | ActionTypeId: 274 | Category: Deploy 275 | Owner: AWS 276 | Provider: CloudFormation 277 | Version: '1' 278 | Configuration: 279 | ActionMode: CHANGE_SET_EXECUTE 280 | ChangeSetName: ChangeSet 281 | RoleArn: !GetAtt CFNRole.Arn 282 | StackName: !Join [ '--', [ 'test', !Ref 'AWS::StackName' ] ] 283 | RunOrder: 2 284 | 285 | CFNRole: 286 | Type: AWS::IAM::Role 287 | Properties: 288 | AssumeRolePolicyDocument: 289 | Statement: 290 | - Action: ['sts:AssumeRole'] 291 | Effect: Allow 292 | Principal: 293 | Service: [cloudformation.amazonaws.com] 294 | Version: '2012-10-17' 295 | Path: / 296 | Policies: 297 | - PolicyName: CloudFormationRole 298 | PolicyDocument: 299 | Version: '2012-10-17' 300 | Statement: 301 | - Action: 302 | - '*' 303 | Effect: Allow 304 | Resource: '*' 305 | 306 | PipelineRole: 307 | Type: AWS::IAM::Role 308 | Properties: 309 | AssumeRolePolicyDocument: 310 | Statement: 311 | - Action: ['sts:AssumeRole'] 312 | Effect: Allow 313 | Principal: 314 | Service: [codepipeline.amazonaws.com] 315 | Version: '2012-10-17' 316 | Path: / 317 | Policies: 318 | - PolicyName: CodePipelineAccess 319 | PolicyDocument: 320 | Version: '2012-10-17' 321 | Statement: 322 | - Action: 323 | - 's3:*' 324 | - 'cloudformation:CreateStack' 325 | - 'cloudformation:DescribeStacks' 326 | - 'cloudformation:DeleteStack' 327 | - 'cloudformation:UpdateStack' 328 | - 'cloudformation:CreateChangeSet' 329 | - 'cloudformation:ExecuteChangeSet' 330 | - 'cloudformation:DeleteChangeSet' 331 | - 'cloudformation:DescribeChangeSet' 332 | - 'cloudformation:SetStackPolicy' 333 | - 'iam:PassRole' 334 | - 'sns:Publish' 335 | - 'codebuild:BatchGetBuilds' 336 | - 'codebuild:StartBuild' 337 | Effect: Allow 338 | Resource: '*' 339 | -------------------------------------------------------------------------------- /nested-stacks/rds/aurora.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Aurora 5 | 6 | # Create the Aurora MySQL or PostgreSQL database(s). Currently, this template only supports alarms for Aurora MySQL. 7 | 8 | Parameters: 9 | 10 | Stage: 11 | Type: String 12 | Description: the stage 13 | 14 | CreateReplicaInstance: 15 | Default: false 16 | Type: String 17 | Description: Create one replica instance for HA 18 | ConstraintDescription: Only true or false are allowed 19 | AllowedValues: 20 | - true 21 | - false 22 | 23 | DatabaseSecurityGroupId: 24 | Type: String 25 | Description: Database VPC security group ID 26 | 27 | DatabaseSubnets: 28 | Description: Choose which subnets Aurora should be in 29 | Type: List 30 | 31 | DatabaseName: 32 | Default: StartupDB 33 | Type: String 34 | Description: Database name 35 | MinLength: 1 36 | MaxLength: 30 37 | AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*" 38 | ConstraintDescription: Name must begin with a letter and contain only alphanumeric characters 39 | 40 | DatabaseUser: 41 | Default: startupadmin 42 | Type: String 43 | Description: Database admin account name 44 | MinLength: 5 45 | MaxLength: 16 46 | AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*" 47 | ConstraintDescription: Name must begin with a letter and contain only alphanumeric characters 48 | 49 | DatabasePassword: 50 | NoEcho: true 51 | Type: String 52 | Description: Database admin account password 53 | MinLength: 6 54 | MaxLength: 41 55 | AllowedPattern: "[a-zA-Z0-9]*" 56 | ConstraintDescription: Password must contain only alphanumeric characters 57 | 58 | DatabaseEngine: 59 | Default: aurora 60 | Type: String 61 | Description: Database engines - Aurora MySQL or Aurora PostgreSQL 62 | ConstraintDescription: Choose an engine from the drop down 63 | AllowedValues: 64 | - aurora 65 | - aurora-postgresql 66 | 67 | EncryptionAtRest: 68 | Default: false 69 | Type: String 70 | Description: The optional flag for encryption at rest (db.t2.small and above) 71 | ConstraintDescription: Only true or false are allowed 72 | AllowedValues: 73 | - true 74 | - false 75 | 76 | DatabaseInstanceClass: 77 | Default: db.t2.small 78 | Type: String 79 | Description: "Database instance class, e.g. db.t2.micro (free tier) - Engine support: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.html" 80 | ConstraintDescription: DB instance class not supported 81 | AllowedValues: 82 | - db.t2.small 83 | - db.t2.medium 84 | - db.t2.xlarge 85 | - db.r4.large 86 | - db.r4.xlarge 87 | - db.r4.2xlarge 88 | - db.r4.4xlarge 89 | - db.r4.8xlarge 90 | - db.r4.16xlarge 91 | 92 | # The database alarm configuration, currently not supported for Aurora PostgreSQL 93 | DatabaseAlarmMaxCpuPercent: 94 | Description: Database CPU % max for alarm (currently, Aurora MySQL only) 95 | Type: Number 96 | Default: 80 97 | MinValue: 1 98 | MaxValue: 99 99 | ConstraintDescription: Must be a percentage between 1-99% 100 | 101 | DatabaseAlarmReadLatencyMaxSeconds: 102 | Description: Read latency max for alarm (currently, Aurora MySQL only) 103 | Type: Number 104 | Default: 1 105 | MinValue: 1 106 | 107 | DatabaseAlarmWriteLatencyMaxSeconds: 108 | Description: Write latency max for alarm (currently, Aurora MySQL only) 109 | Type: Number 110 | Default: 1 111 | MinValue: 1 112 | 113 | DatabaseAlarmEvaluationPeriods: 114 | Description: The number of periods over which data is compared to the specified threshold (currently, Aurora MySQL only) 115 | Type: Number 116 | Default: 2 117 | MinValue: 2 118 | 119 | DatabaseAlarmEvaluationPeriodSeconds: 120 | Description: The time over which the specified statistic is applied. Specify time in seconds, in multiples of 60. Enhanced monitoring must be enabled if less than 500 seconds (currently, Aurora MySQL only) 121 | Type: Number 122 | Default: 300 123 | MinValue: 60 124 | ConstraintDescription: Must be at least 60 seconds 125 | 126 | EnhancedMonitoring: 127 | Default: false 128 | Type: String 129 | Description: The optional flag for enhanced monitoring (additional charges apply - https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.OS.html) (currently, Aurora MySQL only) 130 | ConstraintDescription: Only true or false are allowed 131 | AllowedValues: 132 | - true 133 | - false 134 | 135 | # Default is 200 MB 136 | DatabaseAlarmSwapUsageInBytes: 137 | Default: 209715200 138 | Type: Number 139 | Description: Number of swap usage bytes for alarm (if enabled - Aurora MySQL only) 140 | MinValue: 1 141 | ConstraintDescription: Enter a value of at least one byte 142 | 143 | EnableAlarms: 144 | Default: false 145 | Type: String 146 | Description: Set to true to enable (additional charges - https://aws.amazon.com/cloudwatch/pricing/ - currently, Aurora MySQL only) 147 | ConstraintDescription: Only true or false are allowed 148 | AllowedValues: 149 | - true 150 | - false 151 | 152 | 153 | Conditions: 154 | 155 | IsAuroraMySQL: !Equals [ !Ref DatabaseEngine, aurora ] 156 | 157 | AlarmsEnabled: !And 158 | - !Condition IsAuroraMySQL 159 | - !Equals [ !Ref EnableAlarms, true ] 160 | 161 | EnhancedMonitoringSupprtedAndEnabled: !And 162 | - !Condition AlarmsEnabled 163 | - !Equals [ !Ref EnhancedMonitoring, true ] 164 | 165 | ShouldCreateReplicaInstance: !Equals [ !Ref CreateReplicaInstance, true ] 166 | 167 | Resources: 168 | 169 | EnhancedMonitoringRole: 170 | Type: AWS::IAM::Role 171 | Condition: EnhancedMonitoringSupprtedAndEnabled 172 | Properties: 173 | Path: / 174 | AssumeRolePolicyDocument: 175 | Version: 2012-10-17 176 | Statement: 177 | - Effect: Allow 178 | Principal: 179 | Service: monitoring.rds.amazonaws.com 180 | Action: sts:AssumeRole 181 | ManagedPolicyArns: 182 | - arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole 183 | 184 | DatabaseAlarmTopic: 185 | Type: AWS::SNS::Topic 186 | Condition: AlarmsEnabled 187 | Properties: 188 | DisplayName: Database Alarm Topic 189 | 190 | DatabaseSubnetGroup: 191 | Type: AWS::RDS::DBSubnetGroup 192 | Properties: 193 | DBSubnetGroupDescription: Database subnet group 194 | SubnetIds: !Ref DatabaseSubnets 195 | Tags: 196 | - Key: Name 197 | Value: !Ref AWS::StackName 198 | - Key: Stage 199 | Value: !Ref Stage 200 | 201 | AuroraCluster: 202 | Type: AWS::RDS::DBCluster 203 | Properties: 204 | Engine: !Ref DatabaseEngine 205 | MasterUsername: !Ref DatabaseUser 206 | MasterUserPassword: !Ref DatabasePassword 207 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 208 | StorageEncrypted: !Ref EncryptionAtRest 209 | DatabaseName: !Ref DatabaseName 210 | DBClusterParameterGroupName: !If [ IsAuroraMySQL, default.aurora5.6, default.aurora-postgresql9.6 ] 211 | Port: !If [ IsAuroraMySQL, 3306, 5432 ] 212 | Tags: 213 | - Key: Stage 214 | Value: !Ref Stage 215 | VpcSecurityGroupIds: 216 | - !Ref DatabaseSecurityGroupId 217 | DependsOn: DatabaseSubnetGroup 218 | 219 | AuroraInstance0: 220 | Type: AWS::RDS::DBInstance 221 | Properties: 222 | Engine: !Ref DatabaseEngine 223 | DBClusterIdentifier: !Ref AuroraCluster 224 | DBInstanceClass: !Ref DatabaseInstanceClass 225 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 226 | StorageEncrypted: !Ref EncryptionAtRest 227 | DBParameterGroupName: !If [ IsAuroraMySQL, default.aurora5.6, default.aurora-postgresql9.6 ] 228 | MonitoringInterval: !If [ EnhancedMonitoringSupprtedAndEnabled, 60, 0 ] 229 | MonitoringRoleArn: !If [ EnhancedMonitoringSupprtedAndEnabled, !GetAtt EnhancedMonitoringRole.Arn, !Ref "AWS::NoValue" ] 230 | CopyTagsToSnapshot: true 231 | Tags: 232 | - Key: Name 233 | Value: !Ref AWS::StackName 234 | - Key: Stage 235 | Value: !Ref Stage 236 | DependsOn: AuroraCluster 237 | 238 | AuroraInstance1: 239 | Type: AWS::RDS::DBInstance 240 | Condition: ShouldCreateReplicaInstance 241 | Properties: 242 | Engine: !Ref DatabaseEngine 243 | DBClusterIdentifier: !Ref AuroraCluster 244 | DBInstanceClass: !Ref DatabaseInstanceClass 245 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 246 | StorageEncrypted: !Ref EncryptionAtRest 247 | DBParameterGroupName: !If [ IsAuroraMySQL, default.aurora5.6, default.aurora-postgresql9.6 ] 248 | MonitoringInterval: !If [ EnhancedMonitoringSupprtedAndEnabled, 60, 0 ] 249 | MonitoringRoleArn: !If [ EnhancedMonitoringSupprtedAndEnabled, !GetAtt EnhancedMonitoringRole.Arn, !Ref "AWS::NoValue" ] 250 | CopyTagsToSnapshot: true 251 | Tags: 252 | - Key: Name 253 | Value: !Ref AWS::StackName 254 | - Key: Stage 255 | Value: !Ref Stage 256 | DependsOn: AuroraCluster 257 | 258 | DatabaseCpuAlarm: 259 | Type: AWS::CloudWatch::Alarm 260 | Condition: AlarmsEnabled 261 | Properties: 262 | AlarmDescription: !Sub DB CPU utilization is over ${DatabaseAlarmMaxCpuPercent}% for ${DatabaseAlarmEvaluationPeriods} period(s) of ${DatabaseAlarmEvaluationPeriodSeconds} seconds 263 | TreatMissingData: notBreaching 264 | Namespace: AWS/RDS 265 | MetricName: CPUUtilization 266 | Unit: Percent 267 | Statistic: Average 268 | EvaluationPeriods: !Ref DatabaseAlarmEvaluationPeriods 269 | Period: !Ref DatabaseAlarmEvaluationPeriodSeconds 270 | Threshold: !Ref DatabaseAlarmMaxCpuPercent 271 | ComparisonOperator: GreaterThanOrEqualToThreshold 272 | Dimensions: 273 | - Name: DBClusterIdentifier 274 | Value: !Ref AuroraCluster 275 | - Name: Role 276 | Value: WRITER 277 | AlarmActions: 278 | - !Ref DatabaseAlarmTopic 279 | DependsOn: AuroraCluster 280 | 281 | DatabaseSelectLatencyAlarm: 282 | Type: AWS::CloudWatch::Alarm 283 | Condition: AlarmsEnabled 284 | Properties: 285 | AlarmDescription: !Sub DB read latency is over ${DatabaseAlarmReadLatencyMaxSeconds} for ${DatabaseAlarmEvaluationPeriods} period(s) of ${DatabaseAlarmEvaluationPeriodSeconds} seconds 286 | TreatMissingData: notBreaching 287 | Namespace: AWS/RDS 288 | MetricName: SelectLatency 289 | Unit: Seconds 290 | Statistic: Average 291 | EvaluationPeriods: !Ref DatabaseAlarmEvaluationPeriods 292 | Period: !Ref DatabaseAlarmEvaluationPeriodSeconds 293 | Threshold: !Ref DatabaseAlarmReadLatencyMaxSeconds 294 | ComparisonOperator: GreaterThanOrEqualToThreshold 295 | Dimensions: 296 | - Name: DBClusterIdentifier 297 | Value: !Ref AuroraCluster 298 | - Name: Role 299 | Value: WRITER 300 | AlarmActions: 301 | - !Ref DatabaseAlarmTopic 302 | DependsOn: AuroraCluster 303 | 304 | DatabaseInsertLatencyAlarm: 305 | Type: AWS::CloudWatch::Alarm 306 | Condition: AlarmsEnabled 307 | Properties: 308 | AlarmDescription: !Sub DB insert latency is over ${DatabaseAlarmWriteLatencyMaxSeconds} for ${DatabaseAlarmEvaluationPeriods} period(s) of ${DatabaseAlarmEvaluationPeriodSeconds} seconds 309 | TreatMissingData: notBreaching 310 | Namespace: AWS/RDS 311 | MetricName: InsertLatency 312 | Unit: Seconds 313 | Statistic: Average 314 | EvaluationPeriods: !Ref DatabaseAlarmEvaluationPeriods 315 | Period: !Ref DatabaseAlarmEvaluationPeriodSeconds 316 | Threshold: !Ref DatabaseAlarmWriteLatencyMaxSeconds 317 | ComparisonOperator: GreaterThanOrEqualToThreshold 318 | Dimensions: 319 | - Name: DBClusterIdentifier 320 | Value: !Ref AuroraCluster 321 | - Name: Role 322 | Value: WRITER 323 | AlarmActions: 324 | - !Ref DatabaseAlarmTopic 325 | DependsOn: AuroraCluster 326 | 327 | DatabaseUpdateLatencyAlarm: 328 | Type: AWS::CloudWatch::Alarm 329 | Condition: AlarmsEnabled 330 | Properties: 331 | AlarmDescription: !Sub DB update latency is over ${DatabaseAlarmWriteLatencyMaxSeconds} for ${DatabaseAlarmEvaluationPeriods} period(s) of ${DatabaseAlarmEvaluationPeriodSeconds} seconds 332 | TreatMissingData: notBreaching 333 | Namespace: AWS/RDS 334 | MetricName: UpdateLatency 335 | Unit: Seconds 336 | Statistic: Average 337 | EvaluationPeriods: !Ref DatabaseAlarmEvaluationPeriods 338 | Period: !Ref DatabaseAlarmEvaluationPeriodSeconds 339 | Threshold: !Ref DatabaseAlarmWriteLatencyMaxSeconds 340 | ComparisonOperator: GreaterThanOrEqualToThreshold 341 | Dimensions: 342 | - Name: DBClusterIdentifier 343 | Value: !Ref AuroraCluster 344 | - Name: Role 345 | Value: WRITER 346 | AlarmActions: 347 | - !Ref DatabaseAlarmTopic 348 | DependsOn: AuroraCluster 349 | 350 | DatabaseDeleteLatencyAlarm: 351 | Type: AWS::CloudWatch::Alarm 352 | Condition: AlarmsEnabled 353 | Properties: 354 | AlarmDescription: !Sub DB update latency is over ${DatabaseAlarmWriteLatencyMaxSeconds} for ${DatabaseAlarmEvaluationPeriods} period(s) of ${DatabaseAlarmEvaluationPeriodSeconds} seconds 355 | TreatMissingData: notBreaching 356 | Namespace: AWS/RDS 357 | MetricName: DeleteLatency 358 | Unit: Seconds 359 | Statistic: Average 360 | EvaluationPeriods: !Ref DatabaseAlarmEvaluationPeriods 361 | Period: !Ref DatabaseAlarmEvaluationPeriodSeconds 362 | Threshold: !Ref DatabaseAlarmWriteLatencyMaxSeconds 363 | ComparisonOperator: GreaterThanOrEqualToThreshold 364 | Dimensions: 365 | - Name: DBClusterIdentifier 366 | Value: !Ref AuroraCluster 367 | - Name: Role 368 | Value: WRITER 369 | AlarmActions: 370 | - !Ref DatabaseAlarmTopic 371 | DependsOn: AuroraCluster 372 | 373 | 374 | Outputs: 375 | 376 | Name: 377 | Description: Aurora Stack Name 378 | Value: !Ref AWS::StackName 379 | Export: 380 | Name: !Sub ${AWS::StackName}-Name 381 | 382 | AuroraClusterId: 383 | Description: Aurora Cluster ID 384 | Value: !Ref AuroraCluster 385 | Export: 386 | Name: !Sub ${AWS::StackName}-AuroraClusterID 387 | 388 | AuroraDbURL: 389 | Description: Aurora Database URL 390 | Value: !GetAtt AuroraCluster.Endpoint.Address 391 | Export: 392 | Name: !Sub ${AWS::StackName}-DatabaseURL 393 | 394 | AuroraReadDbURL: 395 | Description: Aurora Database Read URL 396 | Value: !GetAtt AuroraCluster.ReadEndpoint.Address 397 | Export: 398 | Name: !Sub ${AWS::StackName}-DatabaseReadURL 399 | 400 | DbUser: 401 | Description: RDS Database admin account user 402 | Value: !Ref DatabaseUser 403 | Export: 404 | Name: !Sub ${AWS::StackName}-DatabaseUser 405 | 406 | DatabaseAlarmTopicArn: 407 | Description: Database Alarm Topic ARN 408 | Condition: AlarmsEnabled 409 | Value: !Ref DatabaseAlarmTopic 410 | Export: 411 | Name: !Sub ${AWS::StackName}-DatabaseAlarmTopicArn 412 | 413 | DatabaseAlarmTopicName: 414 | Description: Database Alarm Topic Name 415 | Condition: AlarmsEnabled 416 | Value: !GetAtt DatabaseAlarmTopic.TopicName 417 | Export: 418 | Name: !Sub ${AWS::StackName}-DatabaseAlarmTopicName 419 | -------------------------------------------------------------------------------- /pipelines/cicd/single-lambda-test-staging-prod.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | 3 | Description: 4 | CI/CD CodePipeline for a single lambda. Naming conveention, [repo]--[branch]--[eyecatcher]--cicd 5 | Lint > Build & Testcases > Deploy to Test stage > Approve > Deploy Staging > Approve > Deploy Prod 6 | Will create lambda with test,staging,prod aliases - all versioned with their own enviornment vars 7 | 8 | 9 | Parameters: 10 | ApprovalNotificationArn: 11 | Description: SNS ARN to receive notifications for approvals 12 | Type: String 13 | Default: arn:aws:sns:us-east-1:1111111:code-promotion-approvals 14 | GithubOrg: 15 | Description: The GitHub organization 16 | Type: String 17 | GithubOAuthToken: 18 | Description: The GitHub Personal Access Token so CodePipeline can get the code. https://github.com/settings/tokens. Needs repo scope. 19 | Type: String 20 | NoEcho: true 21 | 22 | LambdaName: 23 | Description: Value of FullLambdaName in your -r resources stack output. Should not exist until after 1st CICD run. ex [Repo]--[Branch]--[LambdaName] 24 | Type: String 25 | LambdaRuntime: 26 | Description: Lambda runtime 27 | Type: String 28 | Default: "go1.x" 29 | AllowedValues: 30 | - nodejs8.10 31 | - java8 32 | - python2.7 33 | - python3.6 34 | - dotnetcore1.0 35 | - dotnetcore2.0 36 | - go1.x 37 | LambdaDesc: 38 | Description: Lambda desc 39 | Type: String 40 | HandlerPath: 41 | Description: The path to the entrypoint of your application, relative to project root. Defines lambda handler and compile/package point. Node ex, src/lambda/handlers/test.handler Go ex, cmd/appName/main 42 | Type: String 43 | Default: cmd/appName/main 44 | S3BucketForLambdaPackageZips: 45 | Description: Bucket to store lambda code zips. S3 key will be [repo]--[branch]--[eyecatcher]--cicd/lambdazips 46 | Type: String 47 | Default: "deploy.yourorg.com" 48 | 49 | BuildTimeout: 50 | Description: Timeout in minutes for the build 51 | Type: Number 52 | Default: 5 53 | CodeBuildImage: 54 | Description: Image for the CodeBuild container that runs linting, test cases, build. Make sure the version matches your Lambda runtime. Ex, node:8.10 55 | Type: String 56 | Default: golang:1.10.3 57 | TestBuildPackageSpecPath: 58 | Description: Path to the buildspec used to test, build, package your lambda. relative to your codebase 59 | Type: String 60 | Default: aws/codebuild/nodejs-test-package.yaml 61 | 62 | PublishTimeout: 63 | Description: Timeout in minutes for deploying the lambda each stage 64 | Type: Number 65 | Default: 5 66 | LambdaPublishBuildSpecPath: 67 | Description: Path to the buildspec used set env vars, create version, and update alias. relative to your codebase 68 | Type: String 69 | Default: aws/codebuild/lambda-publish.yaml 70 | 71 | Metadata: 72 | AWS::CloudFormation::Interface: 73 | ParameterGroups: 74 | - Label: 75 | default: "Misc" 76 | Parameters: 77 | - ApprovalNotificationArn 78 | - GithubOrg 79 | - GithubOAuthToken 80 | - Label: 81 | default: "Lambda" 82 | Parameters: 83 | - LambdaName 84 | - LambdaRuntime 85 | - LambdaDesc 86 | - HandlerPath 87 | - Label: 88 | default: "Build, Test, Deploy" 89 | Parameters: 90 | - S3BucketForLambdaPackageZips 91 | - CodeBuildImage 92 | - TestBuildPackageSpecPath 93 | - BuildTimeout 94 | - Label: 95 | default: "Publish" 96 | Parameters: 97 | - LambdaPublishBuildSpecPath 98 | - PublishTimeout 99 | 100 | Resources: 101 | ArtifactStoreBucket: 102 | Type: AWS::S3::Bucket 103 | Properties: 104 | VersioningConfiguration: 105 | Status: Enabled 106 | 107 | #### CodeBuild Stuff 108 | CodeBuildRole: 109 | Type: AWS::IAM::Role 110 | Properties: 111 | AssumeRolePolicyDocument: 112 | Statement: 113 | - Action: ['sts:AssumeRole'] 114 | Effect: Allow 115 | Principal: 116 | Service: [codebuild.amazonaws.com] 117 | Version: '2012-10-17' 118 | Path: / 119 | Policies: 120 | - PolicyName: CodeBuild 121 | PolicyDocument: 122 | Version: '2012-10-17' 123 | Statement: 124 | - Action: 125 | - "logs:CreateLogGroup" 126 | - "logs:CreateLogStream" 127 | - "logs:PutLogEvents" 128 | Effect: Allow 129 | Resource: 130 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" 131 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*" 132 | - Action: 133 | - "s3:PutObject" 134 | - "s3:GetObject" 135 | - "s3:GetObjectVersion" 136 | Effect: Allow 137 | Resource: 138 | - "arn:aws:s3:::codepipeline-us-east-1-*" 139 | - Action: 140 | - 'iam:PassRole' 141 | - 'ssm:DescribeParameters' 142 | Effect: Allow 143 | Resource: '*' 144 | - Action: 145 | - 's3:*' 146 | Effect: Allow 147 | Resource: 148 | - !Sub 149 | - ${bucket}* 150 | - { bucket: !GetAtt ArtifactStoreBucket.Arn } 151 | - !Sub 152 | - arn:aws:s3:::${bucket}* 153 | - { bucket: !Ref S3BucketForLambdaPackageZips } 154 | - Action: #see https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-access.html 155 | - 'ssm:GetParameter*' 156 | Effect: Allow 157 | Resource: 158 | - !Sub "arn:aws:ssm:*:${AWS::AccountId}:parameter/*" 159 | - Action: #see https://docs.aws.amazon.com/kms/latest/developerguide/iam-policies.html 160 | - 'kms:Decrypt' 161 | Effect: Allow 162 | Resource: 163 | - !Sub "arn:aws:kms:*:${AWS::AccountId}:key/*" 164 | - Action: 165 | - lambda:Add* 166 | - lambda:Create* 167 | - lambda:Get* 168 | - lambda:List* 169 | - lambda:Update* 170 | Effect: Allow 171 | Resource: 172 | - !Sub 173 | - arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${Repo}--${Branch}--${LambdaName}* 174 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 175 | 176 | TestBuildPackageProject: 177 | Type: AWS::CodeBuild::Project 178 | Properties: 179 | Name: !Sub 180 | - ${StackName}-BuildTestPackageLambda 181 | - { StackName: !Ref 'AWS::StackName' } 182 | Artifacts: 183 | Type: CODEPIPELINE 184 | Environment: 185 | ComputeType: BUILD_GENERAL1_LARGE 186 | PrivilegedMode: true 187 | Type: LINUX_CONTAINER 188 | Image: !Ref CodeBuildImage 189 | EnvironmentVariables: 190 | - Name: AWS_ACCOUNT_ID 191 | Value: !Ref AWS::AccountId 192 | - Name: HANDLER_PATH 193 | Value: !Ref HandlerPath 194 | - Name: GITHUB_ORG 195 | Value: !Ref GithubOrg 196 | - Name: REPO 197 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 198 | - Name: BRANCH 199 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 200 | - Name: EYECATCHER 201 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 202 | - Name: LAMBDA_NAME 203 | Value: !Ref LambdaName 204 | - Name: FULL_LAMBDA_NAME 205 | Value: !Sub 206 | - ${Repo}--${Branch}--${LambdaName} 207 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 208 | - Name: S3_DIR_PATH_FOR_ZIPS 209 | Value: !Sub 210 | - s3://${S3Bucket}/${AWS::StackName}/lambdazips 211 | - { S3Bucket: !Ref S3BucketForLambdaPackageZips } 212 | ServiceRole: !Ref CodeBuildRole 213 | Source: 214 | Type: CODEPIPELINE 215 | BuildSpec: !Ref TestBuildPackageSpecPath 216 | TimeoutInMinutes: !Ref BuildTimeout 217 | 218 | LamPubProjTest: 219 | Type: AWS::CodeBuild::Project 220 | Properties: 221 | Name: !Sub 222 | - ${StackName}-LambdaPublish-Test 223 | - { StackName: !Ref 'AWS::StackName' } 224 | Artifacts: 225 | Type: CODEPIPELINE 226 | Environment: 227 | ComputeType: BUILD_GENERAL1_LARGE 228 | PrivilegedMode: true 229 | Type: LINUX_CONTAINER 230 | Image: 'aws/codebuild/ubuntu-base:14.04' 231 | EnvironmentVariables: 232 | - Name: AWS_ACCOUNT_ID 233 | Value: !Ref AWS::AccountId 234 | - Name: LAMBDA_RUNTIME 235 | Value: !Ref LambdaRuntime 236 | - Name: LAMBDA_DESC 237 | Value: !Ref LambdaRuntime 238 | - Name: HANDLER_PATH 239 | Value: !Ref HandlerPath 240 | - Name: STAGE 241 | Value: test 242 | - Name: REPO 243 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 244 | - Name: BRANCH 245 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 246 | - Name: EYECATCHER 247 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 248 | - Name: LAMBDA_NAME 249 | Value: !Ref LambdaName 250 | - Name: FULL_LAMBDA_NAME 251 | Value: !Sub 252 | - ${Repo}--${Branch}--${LambdaName} 253 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 254 | - Name: S3_BUCKET_CONTAINING_PACKAGES 255 | Value: !Ref S3BucketForLambdaPackageZips 256 | - Name: S3_PATH_TO_PACKAGES 257 | Value: !Sub ${AWS::StackName}/lambdazips 258 | ServiceRole: !Ref CodeBuildRole 259 | Source: 260 | Type: CODEPIPELINE 261 | BuildSpec: !Ref LambdaPublishBuildSpecPath 262 | TimeoutInMinutes: !Ref PublishTimeout 263 | 264 | LamPubProjStaging: 265 | Type: AWS::CodeBuild::Project 266 | Properties: 267 | Name: !Sub 268 | - ${StackName}-LambdaPublish-Staging 269 | - { StackName: !Ref 'AWS::StackName' } 270 | Artifacts: 271 | Type: CODEPIPELINE 272 | Environment: 273 | ComputeType: BUILD_GENERAL1_LARGE 274 | PrivilegedMode: true 275 | Type: LINUX_CONTAINER 276 | Image: 'aws/codebuild/ubuntu-base:14.04' 277 | EnvironmentVariables: 278 | - Name: AWS_ACCOUNT_ID 279 | Value: !Ref AWS::AccountId 280 | - Name: LAMBDA_RUNTIME 281 | Value: !Ref LambdaRuntime 282 | - Name: LAMBDA_DESC 283 | Value: !Ref LambdaRuntime 284 | - Name: HANDLER_PATH 285 | Value: !Ref HandlerPath 286 | - Name: STAGE 287 | Value: staging 288 | - Name: REPO 289 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 290 | - Name: BRANCH 291 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 292 | - Name: EYECATCHER 293 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 294 | - Name: LAMBDA_NAME 295 | Value: !Ref LambdaName 296 | - Name: FULL_LAMBDA_NAME 297 | Value: !Sub 298 | - ${Repo}--${Branch}--${LambdaName} 299 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 300 | - Name: S3_BUCKET_CONTAINING_PACKAGES 301 | Value: !Ref S3BucketForLambdaPackageZips 302 | - Name: S3_PATH_TO_PACKAGES 303 | Value: !Sub ${AWS::StackName}/lambdazips 304 | ServiceRole: !Ref CodeBuildRole 305 | Source: 306 | Type: CODEPIPELINE 307 | BuildSpec: !Ref LambdaPublishBuildSpecPath 308 | TimeoutInMinutes: !Ref PublishTimeout 309 | 310 | LamPubProjProd: 311 | Type: AWS::CodeBuild::Project 312 | Properties: 313 | Name: !Sub 314 | - ${StackName}-LambdaPublish-Prod 315 | - { StackName: !Ref 'AWS::StackName' } 316 | Artifacts: 317 | Type: CODEPIPELINE 318 | Environment: 319 | ComputeType: BUILD_GENERAL1_LARGE 320 | PrivilegedMode: true 321 | Type: LINUX_CONTAINER 322 | Image: 'aws/codebuild/ubuntu-base:14.04' 323 | EnvironmentVariables: 324 | - Name: AWS_ACCOUNT_ID 325 | Value: !Ref AWS::AccountId 326 | - Name: LAMBDA_RUNTIME 327 | Value: !Ref LambdaRuntime 328 | - Name: LAMBDA_DESC 329 | Value: !Ref LambdaRuntime 330 | - Name: HANDLER_PATH 331 | Value: !Ref HandlerPath 332 | - Name: STAGE 333 | Value: prod 334 | - Name: REPO 335 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 336 | - Name: BRANCH 337 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 338 | - Name: EYECATCHER 339 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 340 | - Name: LAMBDA_NAME 341 | Value: !Ref LambdaName 342 | - Name: FULL_LAMBDA_NAME 343 | Value: !Sub 344 | - ${Repo}--${Branch}--${LambdaName} 345 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 346 | - Name: S3_BUCKET_CONTAINING_PACKAGES 347 | Value: !Ref S3BucketForLambdaPackageZips 348 | - Name: S3_PATH_TO_PACKAGES 349 | Value: !Sub ${AWS::StackName}/lambdazips 350 | ServiceRole: !Ref CodeBuildRole 351 | Source: 352 | Type: CODEPIPELINE 353 | BuildSpec: !Ref LambdaPublishBuildSpecPath 354 | TimeoutInMinutes: !Ref PublishTimeout 355 | #### END CodeBuild Stuff 356 | 357 | 358 | #### CodePipeline Stuff 359 | PipelineRole: 360 | Type: AWS::IAM::Role 361 | Properties: 362 | AssumeRolePolicyDocument: 363 | Statement: 364 | - Action: ['sts:AssumeRole'] 365 | Effect: Allow 366 | Principal: 367 | Service: [codepipeline.amazonaws.com] 368 | Version: '2012-10-17' 369 | Path: / 370 | Policies: 371 | - PolicyName: CodePipelineAccess 372 | PolicyDocument: 373 | Version: '2012-10-17' 374 | Statement: 375 | - Action: 376 | - 's3:*' 377 | Effect: Allow 378 | Resource: 379 | - !Sub 380 | - ${bucket}* 381 | - { bucket: !GetAtt ArtifactStoreBucket.Arn } 382 | - !Sub 383 | - arn:aws:s3:::${bucket}* 384 | - { bucket: !Ref S3BucketForLambdaPackageZips } 385 | - Action: 386 | - 'cloudformation:CreateStack' 387 | - 'cloudformation:DescribeStacks' 388 | - 'cloudformation:DeleteStack' 389 | - 'cloudformation:UpdateStack' 390 | - 'cloudformation:CreateChangeSet' 391 | - 'cloudformation:ExecuteChangeSet' 392 | - 'cloudformation:DeleteChangeSet' 393 | - 'cloudformation:DescribeChangeSet' 394 | - 'cloudformation:SetStackPolicy' 395 | - 'iam:PassRole' 396 | - 'sns:Publish' 397 | - 'codebuild:BatchGetBuilds' 398 | - 'codebuild:StartBuild' 399 | Effect: Allow 400 | Resource: '*' 401 | 402 | Pipeline: 403 | Type: AWS::CodePipeline::Pipeline 404 | Properties: 405 | ArtifactStore: 406 | Location: !Ref 'ArtifactStoreBucket' 407 | Type: S3 408 | DisableInboundStageTransitions: [] 409 | Name: !Ref AWS::StackName 410 | RoleArn: !GetAtt PipelineRole.Arn 411 | Stages: 412 | - Name: Source 413 | Actions: 414 | - Name: Source 415 | ActionTypeId: 416 | Category: Source 417 | Owner: ThirdParty 418 | Provider: GitHub 419 | Version: '1' 420 | OutputArtifacts: 421 | - Name: MyAppCode 422 | Configuration: 423 | Owner: !Ref GithubOrg 424 | Repo: !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] 425 | PollForSourceChanges: true 426 | Branch: !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] 427 | OAuthToken: !Ref GithubOAuthToken 428 | RunOrder: 1 429 | - Name: TestBuildPackage 430 | Actions: 431 | - Name: LintTestBuildPackage 432 | InputArtifacts: 433 | - Name: MyAppCode 434 | ActionTypeId: 435 | Category: Build 436 | Owner: AWS 437 | Provider: CodeBuild 438 | Version: '1' 439 | OutputArtifacts: 440 | - Name: MyAppBuild 441 | Configuration: 442 | ProjectName: !Ref TestBuildPackageProject 443 | RunOrder: 1 444 | - Name: PublishTest 445 | Actions: 446 | - Name: SetENVAndPublish 447 | InputArtifacts: 448 | - Name: MyAppCode 449 | ActionTypeId: 450 | Category: Build 451 | Owner: AWS 452 | Provider: CodeBuild 453 | Version: '1' 454 | Configuration: 455 | ProjectName: !Ref LamPubProjTest 456 | RunOrder: 1 457 | - Name: PromoteTest 458 | Actions: 459 | - Name: Approve 460 | ActionTypeId: 461 | Category: Approval 462 | Owner: AWS 463 | Provider: Manual 464 | Version: '1' 465 | Configuration: 466 | NotificationArn: !Ref ApprovalNotificationArn 467 | RunOrder: 1 468 | - Name: PublishStaging 469 | Actions: 470 | - Name: SetENVAndPublish 471 | InputArtifacts: 472 | - Name: MyAppCode 473 | ActionTypeId: 474 | Category: Build 475 | Owner: AWS 476 | Provider: CodeBuild 477 | Version: '1' 478 | Configuration: 479 | ProjectName: !Ref LamPubProjStaging 480 | RunOrder: 1 481 | - Name: ApproveStaging 482 | ActionTypeId: 483 | Category: Approval 484 | Owner: AWS 485 | Provider: Manual 486 | Version: '1' 487 | Configuration: 488 | NotificationArn: !Ref ApprovalNotificationArn 489 | RunOrder: 2 490 | - Name: PublishProd 491 | Actions: 492 | - Name: SetENVAndPublish 493 | InputArtifacts: 494 | - Name: MyAppCode 495 | ActionTypeId: 496 | Category: Build 497 | Owner: AWS 498 | Provider: CodeBuild 499 | Version: '1' 500 | Configuration: 501 | ProjectName: !Ref LamPubProjProd 502 | RunOrder: 1 503 | #### END CodePipeline Stuff 504 | 505 | Outputs: 506 | 507 | CodeBuildRole: 508 | Description: CodeBuildRole 509 | Value: !Ref CodeBuildRole 510 | 511 | S3ArtifactStore: 512 | Description: Artifact Store Bucket 513 | Value: !Ref ArtifactStoreBucket 514 | 515 | FullLambdaName: 516 | Description: Full lambda name 517 | Value: !Sub 518 | - ${Repo}--${Branch}--${LambdaName} 519 | - { LambdaName: !Ref LambdaName, Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] } 520 | -------------------------------------------------------------------------------- /pipelines/cicd/cloudformation-test-staging-prod.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | 4 | Description: > 5 | Continous Delievery: Naming convention:[repo]--[branch]--[service]--cicd This template 6 | builds an AWS CodePipeline pipeline that implements a continuous delivery release 7 | process for AWS CloudFormation stacks. Push change to github and it will build 8 | the code, then release it to test. Once approvded in test, will release in 9 | staging. Once approved in staging, it will create 10 | a changeset in prod. Approve the changeset and it will deploy in prod. 11 | 12 | Parameters: 13 | RelCloudFormationTemplatePath: 14 | Description: Path of the cloudformation template, relative to your codebase 15 | Type: String 16 | Default: aws/cloudformation/fargate-with-elb.yaml 17 | TestCloudFormationTemplateParameters: 18 | Description: Relative JSON config file that contains parameters passed to RelCloudFormationTemplatePath CloudFormation stack, for your test stage 19 | Type: String 20 | Default: aws/cloudformation/parameters/test--ecs-codepipeline-parameters.json 21 | StagingCloudFormationTemplateParameters: 22 | Description: Relative JSON config file that contains parameters passed to RelCloudFormationTemplatePath CloudFormation stack, for your staging stage 23 | Type: String 24 | Default: aws/cloudformation/parameters/staging--ecs-codepipeline-parameters.json 25 | ProdCloudFormationTemplateParameters: 26 | Description: Relative JSON config file that contains parameters passed to RelCloudFormationTemplatePath CloudFormation stack, for your prod stage 27 | Type: String 28 | Default: aws/cloudformation/parameters/prod--ecs-codepipeline-parameters.json 29 | BuildParameterOverrides: 30 | Default: '{"ContainerImage":{"Fn::GetParam":["MyAppBuild","build.json","CodeImage"]}}' 31 | Description: CloudFormation paramater overrides. build.json is exported from your codebuild/build-docker-image.yaml 32 | Type: String 33 | ApprovalNotificationArn: 34 | Description: SNS ARN to receive notifications for approvals 35 | Type: String 36 | Default: arn:aws:sns:us-east-1:1111111:code-promotion-approvals 37 | GithubOrg: 38 | Description: The GitHub organization 39 | Type: String 40 | GithubOAuthToken: 41 | Description: The GitHub Personal Access Token so CodePipeline can get the code. https://github.com/settings/tokens. Needs repo scope. 42 | Type: String 43 | NoEcho: true 44 | BuildTimeout: 45 | Description: Timeout in minutes for the build 46 | Type: Number 47 | Default: 5 48 | BuildTimeoutTest: 49 | Description: Timeout in minutes for the lint and tests 50 | Type: Number 51 | Default: 5 52 | CodeBuildImageTest: 53 | Description: Image for the CodeBuild container that runs linting and test cases. 54 | Type: String 55 | Default: golang:1.11.1 56 | CodeBuildImage: 57 | Description: Image for the CodeBuild container that builds the final app docker image. See https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html 58 | Type: String 59 | Default: aws/codebuild/docker:17.09.0 60 | CodeEntryPointFilePath: 61 | Description: The path to the root of your source code base, relative to the project root 62 | Type: String 63 | Default: cmd/appName/main.go 64 | RelDockerfilePath: 65 | Description: Path to the Dockerfile to use for the build, relative to your project root. ex build/Dockerfile. Defaults to project root. 66 | Type: String 67 | Default: Dockerfile 68 | LintTestBuildSpecPath: 69 | Description: Path to the buildspec used to run testcases, relative to your codebase 70 | Type: String 71 | Default: aws/codebuild/go-lint-test.yaml 72 | CodeBuildProjectBuildSpecPath: 73 | Description: Path to the buildspec used to build your app image, relative to your codebase 74 | Type: String 75 | Default: aws/codebuild/build-docker-image.yaml 76 | 77 | Metadata: 78 | AWS::CloudFormation::Interface: 79 | ParameterGroups: 80 | - Label: 81 | default: "Misc" 82 | Parameters: 83 | - ApprovalNotificationArn 84 | - GithubOrg 85 | - GithubOAuthToken 86 | - Label: 87 | default: "CloudFormation Template Settings" 88 | Parameters: 89 | - RelCloudFormationTemplatePath 90 | - Label: 91 | default: "CloudFormation template parameters" 92 | Parameters: 93 | - TestCloudFormationTemplateParameters 94 | - StagingCloudFormationTemplateParameters 95 | - ProdCloudFormationTemplateParameters 96 | - Label: 97 | default: "Test cases and linting" 98 | Parameters: 99 | - CodeBuildImageTest 100 | - LintTestBuildSpecPath 101 | - BuildTimeoutTest 102 | - Label: 103 | default: "Build" 104 | Parameters: 105 | - CodeBuildProjectBuildSpecPath 106 | - CodeEntryPointFilePath 107 | - RelDockerfilePath 108 | - BuildParameterOverrides 109 | - CodeBuildImage 110 | - BuildTimeout 111 | 112 | Resources: 113 | ArtifactStoreBucket: 114 | Type: AWS::S3::Bucket 115 | Properties: 116 | VersioningConfiguration: 117 | Status: Enabled 118 | 119 | CodeBuildRole: 120 | Type: AWS::IAM::Role 121 | Properties: 122 | AssumeRolePolicyDocument: 123 | Statement: 124 | - Action: ['sts:AssumeRole'] 125 | Effect: Allow 126 | Principal: 127 | Service: [codebuild.amazonaws.com] 128 | Version: '2012-10-17' 129 | Path: / 130 | Policies: 131 | - PolicyName: CodeBuild 132 | PolicyDocument: 133 | Version: '2012-10-17' 134 | Statement: 135 | - Action: 136 | - "logs:CreateLogGroup" 137 | - "logs:CreateLogStream" 138 | - "logs:PutLogEvents" 139 | Effect: Allow 140 | Resource: 141 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" 142 | - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*" 143 | - Action: 144 | - "s3:PutObject" 145 | - "s3:GetObject" 146 | - "s3:GetObjectVersion" 147 | Effect: Allow 148 | Resource: 149 | - "arn:aws:s3:::codepipeline-us-east-1-*" 150 | - Action: 151 | - 's3:*' 152 | - 'ecr:*' 153 | - 'ecs:*' 154 | Effect: Allow 155 | Resource: '*' 156 | - Action: 157 | - 'iam:PassRole' 158 | Effect: Allow 159 | Resource: '*' 160 | - Action: 161 | - 'ssm:Get*' 162 | Effect: Allow 163 | Resource: '*' 164 | 165 | CodeBuildProjectTestcases: 166 | Type: AWS::CodeBuild::Project 167 | Properties: 168 | Name: !Join ['--', ['testcases',!Ref 'AWS::StackName']] 169 | Artifacts: 170 | Type: CODEPIPELINE 171 | Environment: 172 | ComputeType: BUILD_GENERAL1_LARGE 173 | PrivilegedMode: false 174 | Type: LINUX_CONTAINER 175 | Image: !Ref CodeBuildImageTest 176 | EnvironmentVariables: 177 | - Name: CODE_ENTRY_POINT_FILE_PATH 178 | Value: !Ref CodeEntryPointFilePath 179 | - Name: AWS_DEFAULT_REGION 180 | Value: !Ref AWS::Region 181 | - Name: AWS_ACCOUNT_ID 182 | Value: !Ref AWS::AccountId 183 | - Name: IMAGE_REPO_NAME 184 | Value: !Join [ '/', [ !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] , !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] ] ] 185 | - Name: GITHUB_ORG 186 | Value: !Ref GithubOrg 187 | - Name: REPO 188 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 189 | - Name: BRANCH 190 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 191 | - Name: SERVICE 192 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 193 | ServiceRole: !Ref CodeBuildRole 194 | Source: 195 | Type: CODEPIPELINE 196 | BuildSpec: !Ref LintTestBuildSpecPath 197 | TimeoutInMinutes: !Ref BuildTimeoutTest 198 | 199 | CodeBuildProject: 200 | Type: AWS::CodeBuild::Project 201 | Properties: 202 | Name: !Ref 'AWS::StackName' 203 | Artifacts: 204 | Type: CODEPIPELINE 205 | Environment: 206 | ComputeType: BUILD_GENERAL1_LARGE 207 | PrivilegedMode: true 208 | Type: LINUX_CONTAINER 209 | Image: !Ref CodeBuildImage 210 | EnvironmentVariables: 211 | - Name: CODE_ENTRY_POINT_FILE_PATH 212 | Value: !Ref CodeEntryPointFilePath 213 | - Name: DOCKERFILE_PATH 214 | Value: !Ref RelDockerfilePath 215 | - Name: AWS_DEFAULT_REGION 216 | Value: !Ref AWS::Region 217 | - Name: AWS_ACCOUNT_ID 218 | Value: !Ref AWS::AccountId 219 | - Name: IMAGE_REPO_NAME 220 | Value: !Join [ '/', [ !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] , !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] ] ] 221 | - Name: GITHUB_ORG 222 | Value: !Ref GithubOrg 223 | - Name: REPO 224 | Value: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] 225 | - Name: BRANCH 226 | Value: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ] 227 | - Name: SERVICE 228 | Value: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] 229 | ServiceRole: !Ref CodeBuildRole 230 | Source: 231 | Type: CODEPIPELINE 232 | BuildSpec: !Ref CodeBuildProjectBuildSpecPath 233 | TimeoutInMinutes: !Ref BuildTimeout 234 | 235 | Pipeline: 236 | Type: AWS::CodePipeline::Pipeline 237 | Properties: 238 | ArtifactStore: 239 | Location: !Ref 'ArtifactStoreBucket' 240 | Type: S3 241 | DisableInboundStageTransitions: [] 242 | Name: !Ref AWS::StackName 243 | RoleArn: !GetAtt PipelineRole.Arn 244 | Stages: 245 | - Name: Source 246 | Actions: 247 | - Name: Source 248 | ActionTypeId: 249 | Category: Source 250 | Owner: ThirdParty 251 | Provider: GitHub 252 | Version: '1' 253 | OutputArtifacts: 254 | - Name: MyApp 255 | Configuration: 256 | Owner: !Ref GithubOrg 257 | Repo: !Select [ 0, !Split [ '--', !Ref 'AWS::StackName' ] ] 258 | PollForSourceChanges: true 259 | Branch: !Select [ 1, !Split [ '--', !Ref 'AWS::StackName' ] ] 260 | OAuthToken: !Ref GithubOAuthToken 261 | RunOrder: 1 262 | - Name: Testcases 263 | Actions: 264 | - Name: CodeBuild 265 | InputArtifacts: 266 | - Name: MyApp 267 | ActionTypeId: 268 | Category: Build 269 | Owner: AWS 270 | Provider: CodeBuild 271 | Version: '1' 272 | OutputArtifacts: 273 | - Name: MyAppTestcases 274 | Configuration: 275 | ProjectName: !Ref CodeBuildProjectTestcases 276 | RunOrder: 1 277 | - Name: Build 278 | Actions: 279 | - Name: CodeBuild 280 | InputArtifacts: 281 | - Name: MyApp 282 | ActionTypeId: 283 | Category: Build 284 | Owner: AWS 285 | Provider: CodeBuild 286 | Version: '1' 287 | OutputArtifacts: 288 | - Name: MyAppBuild 289 | Configuration: 290 | ProjectName: !Ref CodeBuildProject 291 | RunOrder: 1 292 | - Name: Test 293 | Actions: 294 | - Name: TestStack 295 | InputArtifacts: 296 | - Name: MyApp 297 | - Name: MyAppBuild 298 | ActionTypeId: 299 | Category: Deploy 300 | Owner: AWS 301 | Provider: CloudFormation 302 | Version: '1' 303 | RunOrder: 1 304 | Configuration: 305 | ActionMode: CHANGE_SET_REPLACE 306 | RoleArn: !GetAtt CFNRole.Arn 307 | Capabilities: CAPABILITY_NAMED_IAM 308 | StackName: !Sub 309 | - test--${Repo}--${Branch}-${Service}--genbycicd 310 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 311 | ChangeSetName: ChangeSet 312 | TemplatePath: !Sub "MyApp::${RelCloudFormationTemplatePath}" 313 | TemplateConfiguration: !Sub "MyApp::${TestCloudFormationTemplateParameters}" 314 | ParameterOverrides: !Ref BuildParameterOverrides 315 | - Name: ExecuteChangeSet 316 | ActionTypeId: 317 | Category: Deploy 318 | Owner: AWS 319 | Provider: CloudFormation 320 | Version: '1' 321 | Configuration: 322 | ActionMode: CHANGE_SET_EXECUTE 323 | ChangeSetName: ChangeSet 324 | RoleArn: !GetAtt CFNRole.Arn 325 | StackName: !Sub 326 | - test--${Repo}--${Branch}-${Service}--genbycicd 327 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 328 | RunOrder: 2 329 | - Name: PromoteTest 330 | Actions: 331 | - Name: Approve 332 | ActionTypeId: 333 | Category: Approval 334 | Owner: AWS 335 | Provider: Manual 336 | Version: '1' 337 | Configuration: 338 | NotificationArn: !Ref ApprovalNotificationArn 339 | RunOrder: 1 340 | - Name: Staging 341 | Actions: 342 | - Name: StagingStack 343 | InputArtifacts: 344 | - Name: MyApp 345 | - Name: MyAppBuild 346 | ActionTypeId: 347 | Category: Deploy 348 | Owner: AWS 349 | Provider: CloudFormation 350 | Version: '1' 351 | RunOrder: 1 352 | Configuration: 353 | ActionMode: CHANGE_SET_REPLACE 354 | RoleArn: !GetAtt CFNRole.Arn 355 | Capabilities: CAPABILITY_IAM 356 | StackName: !Sub 357 | - staging--${Repo}--${Branch}-${Service}--genbycicd 358 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 359 | ChangeSetName: ChangeSet 360 | TemplatePath: !Sub "MyApp::${RelCloudFormationTemplatePath}" 361 | TemplateConfiguration: !Sub "MyApp::${StagingCloudFormationTemplateParameters}" 362 | ParameterOverrides: !Ref BuildParameterOverrides 363 | - Name: ExecuteChangeSet 364 | ActionTypeId: 365 | Category: Deploy 366 | Owner: AWS 367 | Provider: CloudFormation 368 | Version: '1' 369 | Configuration: 370 | ActionMode: CHANGE_SET_EXECUTE 371 | ChangeSetName: ChangeSet 372 | RoleArn: !GetAtt CFNRole.Arn 373 | StackName: !Sub 374 | - staging--${Repo}--${Branch}-${Service}--genbycicd 375 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 376 | RunOrder: 2 377 | - Name: ApproveStaging 378 | ActionTypeId: 379 | Category: Approval 380 | Owner: AWS 381 | Provider: Manual 382 | Version: '1' 383 | Configuration: 384 | NotificationArn: !Ref ApprovalNotificationArn 385 | RunOrder: 3 386 | - Name: Prod 387 | Actions: 388 | - Name: CreateChangeSet 389 | ActionTypeId: 390 | Category: Deploy 391 | Owner: AWS 392 | Provider: CloudFormation 393 | Version: '1' 394 | InputArtifacts: 395 | - Name: MyApp 396 | - Name: MyAppBuild 397 | Configuration: 398 | ActionMode: CHANGE_SET_REPLACE 399 | RoleArn: !GetAtt CFNRole.Arn 400 | Capabilities: CAPABILITY_IAM 401 | StackName: !Sub 402 | - prod--${Repo}--${Branch}-${Service}--genbycicd 403 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 404 | ChangeSetName: ChangeSet 405 | TemplatePath: !Sub "MyApp::${RelCloudFormationTemplatePath}" 406 | TemplateConfiguration: !Sub "MyApp::${ProdCloudFormationTemplateParameters}" 407 | ParameterOverrides: !Ref BuildParameterOverrides 408 | RunOrder: 1 409 | - Name: ApproveChangeSet 410 | ActionTypeId: 411 | Category: Approval 412 | Owner: AWS 413 | Provider: Manual 414 | Version: '1' 415 | Configuration: 416 | NotificationArn: !Ref ApprovalNotificationArn 417 | CustomData: !Sub 'A new change set was created for the prod-${AWS::StackName} stack. Do you want to implement the changes?' 418 | RunOrder: 2 419 | - Name: ExecuteChangeSet 420 | ActionTypeId: 421 | Category: Deploy 422 | Owner: AWS 423 | Provider: CloudFormation 424 | Version: '1' 425 | Configuration: 426 | ActionMode: CHANGE_SET_EXECUTE 427 | ChangeSetName: ChangeSet 428 | RoleArn: !GetAtt CFNRole.Arn 429 | StackName: !Sub 430 | - prod--${Repo}--${Branch}-${Service}--genbycicd 431 | - { Repo: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ], Branch: !Select [ "1", !Split [ '--', !Ref 'AWS::StackName' ] ], Service: !Select [ "2", !Split [ '--', !Ref 'AWS::StackName' ] ] } 432 | RunOrder: 3 433 | - Name: DeleteStagingAndTest 434 | Actions: 435 | - Name: ApproveDeleteStagingAndTest 436 | ActionTypeId: 437 | Category: Approval 438 | Owner: AWS 439 | Provider: Manual 440 | Version: '1' 441 | Configuration: 442 | NotificationArn: !Ref ApprovalNotificationArn 443 | CustomData: !Sub 'Delete staging and test?' 444 | RunOrder: 1 445 | - Name: DeleteTest 446 | ActionTypeId: 447 | Category: Deploy 448 | Owner: AWS 449 | Provider: CloudFormation 450 | Version: '1' 451 | Configuration: 452 | ActionMode: DELETE_ONLY 453 | RoleArn: !GetAtt CFNRole.Arn 454 | StackName: !Join [ '--', [ 'test', !Ref 'AWS::StackName' ] ] 455 | RunOrder: 2 456 | - Name: DeleteStaging 457 | ActionTypeId: 458 | Category: Deploy 459 | Owner: AWS 460 | Provider: CloudFormation 461 | Version: '1' 462 | Configuration: 463 | ActionMode: DELETE_ONLY 464 | RoleArn: !GetAtt CFNRole.Arn 465 | StackName: !Join [ '--', [ 'staging', !Ref 'AWS::StackName' ] ] 466 | RunOrder: 3 467 | 468 | CFNRole: 469 | Type: AWS::IAM::Role 470 | Properties: 471 | AssumeRolePolicyDocument: 472 | Statement: 473 | - Action: ['sts:AssumeRole'] 474 | Effect: Allow 475 | Principal: 476 | Service: [cloudformation.amazonaws.com] 477 | Version: '2012-10-17' 478 | Path: / 479 | Policies: 480 | - PolicyName: CloudFormationRole 481 | PolicyDocument: 482 | Version: '2012-10-17' 483 | Statement: 484 | - Action: 485 | - '*' 486 | Effect: Allow 487 | Resource: '*' 488 | 489 | PipelineRole: 490 | Type: AWS::IAM::Role 491 | Properties: 492 | AssumeRolePolicyDocument: 493 | Statement: 494 | - Action: ['sts:AssumeRole'] 495 | Effect: Allow 496 | Principal: 497 | Service: [codepipeline.amazonaws.com] 498 | Version: '2012-10-17' 499 | Path: / 500 | Policies: 501 | - PolicyName: CodePipelineAccess 502 | PolicyDocument: 503 | Version: '2012-10-17' 504 | Statement: 505 | - Action: 506 | - 's3:*' 507 | - 'cloudformation:CreateStack' 508 | - 'cloudformation:DescribeStacks' 509 | - 'cloudformation:DeleteStack' 510 | - 'cloudformation:UpdateStack' 511 | - 'cloudformation:CreateChangeSet' 512 | - 'cloudformation:ExecuteChangeSet' 513 | - 'cloudformation:DeleteChangeSet' 514 | - 'cloudformation:DescribeChangeSet' 515 | - 'cloudformation:SetStackPolicy' 516 | - 'iam:PassRole' 517 | - 'sns:Publish' 518 | - 'codebuild:BatchGetBuilds' 519 | - 'codebuild:StartBuild' 520 | Effect: Allow 521 | Resource: '*' 522 | --------------------------------------------------------------------------------