├── 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 |
--------------------------------------------------------------------------------