├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── README.md ├── cdk ├── .npmignore ├── README.md ├── cdk-to-proton.sh ├── environment-templates │ ├── README.md │ ├── cdk-vpc-ecs-cluster │ │ └── v1 │ │ │ ├── README.md │ │ │ ├── infrastructure │ │ │ ├── bin │ │ │ │ └── vpc-ecs-cluster.ts │ │ │ ├── cdk-to-proton.sh │ │ │ ├── cdk.json │ │ │ ├── jest.config.js │ │ │ ├── lib │ │ │ │ └── vpc-ecs-cluster-stack.ts │ │ │ ├── manifest.yaml │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── proton-inputs.json │ │ │ └── tsconfig.json │ │ │ └── schema │ │ │ └── schema.yaml │ └── cdk-vpc-eks-cluster │ │ └── v1 │ │ ├── README.md │ │ ├── infrastructure │ │ ├── bin │ │ │ └── vpc-eks-cluster.ts │ │ ├── cdk-to-proton.sh │ │ ├── cdk.json │ │ ├── cdk_deploy.sh │ │ ├── jest.config.js │ │ ├── lib │ │ │ ├── constants.ts │ │ │ ├── pre-req-stack.ts │ │ │ └── vpc-eks-cluster-stack.ts │ │ ├── manifest.yaml │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── proton-inputs.json │ │ └── tsconfig.json │ │ └── schema │ │ └── schema.yaml ├── manifest.yaml └── service-templates │ ├── cdk-ecs-fargate-service-ci-pipeline │ └── v1 │ │ ├── .template-registration.yaml │ │ ├── Makefile │ │ ├── README.md │ │ ├── images │ │ ├── configureinstance.png │ │ ├── configureserviceinstancessync.png │ │ ├── devsvc.png │ │ ├── prodcreating.png │ │ ├── pullrequest.png │ │ ├── pullrequest1.png │ │ ├── selectervicetemplate.png │ │ ├── serviceinstance.png │ │ ├── servicewsourcecode.png │ │ └── syncconfiguration.png │ │ ├── instance_infrastructure │ │ ├── bin │ │ │ └── ecs-fargate-service.ts │ │ ├── cdk-to-proton.sh │ │ ├── cdk.json │ │ ├── jest.config.js │ │ ├── lib │ │ │ ├── ecs-fargate-service.ts │ │ │ └── load-balancer.ts │ │ ├── manifest.yaml │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── proton-inputs.json │ │ └── tsconfig.json │ │ ├── pipeline_infrastructure │ │ ├── .prettierignore │ │ ├── bin │ │ │ └── pipeline.ts │ │ ├── cdk-to-proton.sh │ │ ├── cdk.json │ │ ├── jest.config.js │ │ ├── lib │ │ │ └── container-image-pipeline.ts │ │ ├── manifest.yaml │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── proton-inputs.json │ │ ├── scripts │ │ │ └── ecr_policy_updater.py │ │ ├── test.js │ │ ├── test.ts │ │ └── tsconfig.json │ │ └── schema │ │ └── schema.yaml │ └── cdk-ecs-fargate-service │ └── v1 │ ├── .template-registration.yaml │ ├── README.md │ ├── instance_infrastructure │ ├── bin │ │ └── ecs-fargate-service.ts │ ├── cdk-to-proton.sh │ ├── cdk.json │ ├── jest.config.js │ ├── lib │ │ ├── ecs-fargate-service.ts │ │ └── load-balancer.ts │ ├── manifest.yaml │ ├── package-lock.json │ ├── package.json │ ├── proton-inputs.json │ └── tsconfig.json │ └── schema │ └── schema.yaml ├── pulumi ├── README.md ├── environment-templates │ └── vpc-ecs-cluster │ │ └── v1 │ │ ├── README.md │ │ ├── infrastructure │ │ ├── Pulumi.yaml │ │ ├── index.ts │ │ ├── manifest.yaml │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── proton-inputs.json │ │ ├── pulumi-to-proton-outputs.sh │ │ └── tsconfig.json │ │ └── schema │ │ └── schema.yaml ├── manifest.yaml ├── pulumi-to-proton-outputs.sh └── service-templates │ └── ecs-fargate-load-balanced-service │ └── v1 │ ├── .template-registration.yaml │ ├── README.md │ ├── instance_infrastructure │ ├── Pulumi.yaml │ ├── index.ts │ ├── manifest.yaml │ ├── package-lock.json │ ├── package.json │ ├── proton-inputs.json │ ├── pulumi-to-proton-outputs.sh │ └── tsconfig.json │ └── schema │ └── schema.yaml └── terraform ├── .gitignore ├── .pre-commit-config.yaml ├── .tool-versions ├── README.md ├── environment-templates ├── README.md └── tf-vpc-ecs-cluster │ └── v1 │ ├── Makefile │ ├── README.md │ ├── infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── s3-bucket │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── src │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── variables.tf │ ├── input.png │ └── schema │ └── schema.yaml └── service-templates ├── README.md ├── tf-ecs-fargate-lb-service-cicd-codepipeline └── v1 │ ├── .template-registration.yaml │ ├── Makefile │ ├── README.md │ ├── input.gif │ ├── input.png │ ├── instance_infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── tf-state.sh │ └── variables.tf │ ├── pipeline.png │ ├── pipeline_infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── tf-state.sh │ └── variables.tf │ └── schema │ └── schema.yaml ├── tf-ecs-fargate-lb-service-cicd-gh-actions └── v1 │ ├── .template-registration.yaml │ ├── Makefile │ ├── README.md │ ├── input.png │ ├── instance_infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── tf-state.sh │ └── variables.tf │ ├── pipeline_infrastructure │ ├── README.md │ ├── main.tf │ ├── manifest.yaml │ ├── oidc-identity-provider │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── outputs.tf │ ├── scripts │ │ ├── install-github-cli.sh │ │ ├── install-terraform.sh │ │ ├── oidc.sh │ │ ├── submit-pr.sh │ │ └── tf-state.sh │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── deploy-template.yml │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── variables.tf │ └── schema │ └── schema.yaml ├── tf-ecs-fargate-lb-service └── v1 │ ├── .template-registration.yaml │ ├── Makefile │ ├── README.md │ ├── input.png │ ├── instance_infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── variables.tf │ └── schema │ └── schema.yaml ├── tf-lambda-apigw-service-cicd-codepipeline └── v1 │ ├── .template-registration.yaml │ ├── Makefile │ ├── README.md │ ├── input.png │ ├── instance_infrastructure │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── function.js │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── variables.tf │ ├── pipeline_infrastructure │ ├── README.md │ ├── install-terraform.sh │ ├── main.tf │ ├── manifest.yaml │ ├── output.sh │ ├── outputs.tf │ ├── src │ │ ├── README.md │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── tf-state.sh │ └── variables.tf │ └── schema │ └── schema.yaml └── tf-lambda-apigw-service └── v1 ├── .template-registration.yaml ├── Makefile ├── README.md ├── input.png ├── instance_infrastructure ├── install-terraform.sh ├── main.tf ├── manifest.yaml ├── output.sh ├── outputs.tf ├── src │ ├── README.md │ ├── function.js │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── variables.tf └── schema └── schema.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.tar.gz 3 | .DS_Store 4 | cdk.out 5 | cdk.context.json 6 | *-outputs.json 7 | proton.yaml 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 4 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 5 | opensource-codeofconduct@amazon.com with any additional questions or comments. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proton Codebuild Provisioning Examples 2 | 3 | This repository contains sample IaC templates to demonstrate how to leverage Codebuild provisioning with AWS Proton. 4 | 5 | ## Layout 6 | 7 | Each directory is organized by IaC tool with sample environment and service templates within. 8 | For more information on how to leverage the IaC tool from our samples, navigate to its respective directory and follow the README for more information. 9 | 10 | ## Security 11 | 12 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 13 | 14 | ## License 15 | 16 | This library is licensed under the MIT-0 License. See the LICENSE file. 17 | -------------------------------------------------------------------------------- /cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /cdk/README.md: -------------------------------------------------------------------------------- 1 | # AWS Cloud Development Kit (CDK) Proton Examples 2 | 3 | This directory is home to sample AWS CDK templates to gain an understanding of how one can leverage the AWS CDK with AWS Proton via Codebuild provisioning. 4 | 5 | ### Requirements 6 | 7 | Install the AWS CDK CLI 8 | 9 | ``` 10 | npm i aws-cdk 11 | ``` 12 | 13 | #### Bootstrap 14 | 15 | Prior to launching CDK Stacks, a [bootstrap](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_bootstrap) needs to occur for your AWS account and region. 16 | 17 | #### Manifest 18 | 19 | - Gathering the outputs to send to AWS Proton post deployment. 20 | 21 | - If you want Proton to consume outputs from your stack, there is a very small [shell script](./cdk-to-proton.sh) that is included in this repo that will convert the outputs into the required JSON for Proton to consume. 22 | 23 | ```bash 24 | cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 25 | ``` 26 | 27 | - Post deployment 28 | 29 | - Proton needs to be notified when the deployment is complete. If the Codebuild job fails, Proton will surface that information. On success, the following command needs to be ran: 30 | 31 | ```bash 32 | aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 33 | ``` 34 | 35 | ### Examples 36 | 37 | In the root of the `./cdk` directory we have the following examples: 38 | 39 | 1. An example [manifest](./manifest.yaml) file that covers the requirements to have a successful deployment. 40 | 2. The output rendering [script](./cdk-to-proton.sh). 41 | 3. Example [environment](./environment-templates/) and [service](./service-templates/) templates under their respective directories. 42 | -------------------------------------------------------------------------------- /cdk/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/environment-templates/README.md: -------------------------------------------------------------------------------- 1 | # CDK Environment Examples for AWS Proton 2 | 3 | ### ECS Cluster example 4 | 5 | This example will deploy an ECS Cluster and a VPC 6 | 7 | [./cdk-vpc-ecs-cluster](cdk-vpc-ecs-cluster) 8 | 9 | ### EKS Cluster example 10 | 11 | This example will deploy an EKS Cluster, VPC, optional namespaces, and optional add-ons 12 | 13 | [./cdk-vpc-eks-cluster](cdk-vpc-eks-cluster) 14 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/README.md: -------------------------------------------------------------------------------- 1 | # VPC ECS Cluster Environment Template Bundle 2 | 3 | ### What this builds 4 | 5 | This template bundle will deploy the following: 6 | 7 | - A VPC 8 | - An ECS Cluster with optional configuration inputs (EC2 capacity, Container Insights, ECS Exec logging) 9 | - Works with the [vpc-ecs-cluster](../../../service-templates/vpc-ecs-cluster) environment template. 10 | 11 | ### Testing 12 | 13 | To deploy this locally you will need the `proton-inputs.json` file (example located in this directory). 14 | Modify the proton-inputs.json file to manipulate the build accordingly. 15 | 16 | ``` 17 | { 18 | "environment": { 19 | "name": "cdk-env-demo", 20 | "inputs": { 21 | "vpc_cidr_block": "10.0.0.0/16", 22 | "ec2_capacity": false, 23 | "ec2_instance_type": "t3.medium", 24 | "allow_ecs_exec": false, 25 | "enhanced_cluster_monitoring": false, 26 | "service_discovery_namespace": "proton.cdkdemo.svc" 27 | } 28 | } 29 | } 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/bin/vpc-ecs-cluster.ts: -------------------------------------------------------------------------------- 1 | import { App } from "aws-cdk-lib"; 2 | import { VpcEcsClusterStack } from "../lib/vpc-ecs-cluster-stack"; 3 | import input from "../proton-inputs.json"; 4 | 5 | const protonEnv = { 6 | account: process.env.CDK_DEFAULT_ACCOUNT, 7 | region: process.env.CDK_DEFAULT_REGION, 8 | }; 9 | 10 | const stackName = input.environment.name; 11 | 12 | const app = new App(); 13 | 14 | new VpcEcsClusterStack(app, "ProtonEnv", { 15 | env: protonEnv, 16 | stackName: stackName, 17 | }); 18 | 19 | app.synth(); 20 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/vpc-ecs-cluster.ts", 3 | "outputsFile": "proton-outputs.json", 4 | "requireApproval": "never", 5 | "watch": { 6 | "include": [ 7 | "**" 8 | ], 9 | "exclude": [ 10 | "README.md", 11 | "cdk*.json", 12 | "**/*.d.ts", 13 | "**/*.js", 14 | "tsconfig.json", 15 | "package*.json", 16 | "yarn.lock", 17 | "node_modules", 18 | "test" 19 | ] 20 | }, 21 | "context": { 22 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 23 | "@aws-cdk/core:stackRelativeExports": true, 24 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 25 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 26 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 27 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 28 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 29 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 30 | "@aws-cdk/core:checkSecretUsage": true, 31 | "@aws-cdk/aws-iam:minimizePolicies": true, 32 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 33 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 34 | "@aws-cdk/core:target-partitions": [ 35 | "aws", 36 | "aws-cn" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | # Run when create/update is triggered for environment or service 10 | # Install dependencies 11 | - npm install 12 | - npm run cdk -- deploy 13 | # Script to convert CFN outputs into outputs for Proton 14 | - chmod +x ./cdk-to-proton.sh 15 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 16 | # Notify AWS Proton of deployment status 17 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 18 | deprovision: 19 | # Install dependencies and destroy resources 20 | - npm install 21 | - npm run cdk -- destroy --force 22 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vpc-ecs-cluster", 3 | "version": "0.1.0", 4 | "bin": { 5 | "vpc-ecs-cluster": "bin/vpc-ecs-cluster.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "jest": "^27.5.1", 18 | "ts-jest": "^27.1.4", 19 | "aws-cdk": "2.63.2", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.63.2", 25 | "constructs": "^10.0.0", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "name": "cdk-ecs-env-demo", 4 | "inputs": { 5 | "vpc_cidr_block": "10.0.0.0/16", 6 | "ec2_capacity": false, 7 | "ec2_instance_type": "t3.medium", 8 | "allow_ecs_exec": true, 9 | "enhanced_cluster_monitoring": true, 10 | "service_discovery_namespace": "proton.cdkdemo.svc" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ], 25 | "resolveJsonModule": true, 26 | "esModuleInterop": true, 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "cdk.out" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-ecs-cluster/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | environment_input_type: "EnvironmentInputs" 5 | types: 6 | EnvironmentInputs: 7 | type: object 8 | description: "Input properties for my environment" 9 | properties: 10 | vpc_cidr_block: 11 | type: string 12 | title: "VPC CIDR block" 13 | description: "VPC CIDR block, or default if left blank" 14 | default: "10.0.0.0/16" 15 | ec2_capacity: 16 | type: boolean 17 | title: "Enable EC2 Capacity" 18 | description: "Enable EC2 capacity for the cluster" 19 | default: false 20 | ec2_instance_type: 21 | type: string 22 | title: "EC2 instance types for cluster (if enabled)" 23 | description: "If EC2 capacity enabled, instance type to be used" 24 | default: "t3.medium" 25 | allow_ecs_exec: 26 | type: boolean 27 | title: "Enable ECS Execute command logging for the cluster" 28 | description: "Enable ECS Exec on the cluster" 29 | default: false 30 | enhanced_cluster_monitoring: 31 | type: boolean 32 | title: "Enable Container Insights for this cluster" 33 | default: false 34 | description: "Enable Cloudwatch Container Insights for the cluster" 35 | service_discovery_namespace: 36 | type: string 37 | title: "Service discovery namespace domain" 38 | description: "Service discovery domain" 39 | required: 40 | - service_discovery_namespace 41 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/README.md: -------------------------------------------------------------------------------- 1 | # VPC EKS Cluster Environment Template Bundle 2 | 3 | ### What this builds 4 | 5 | This template bundle will deploy the following: 6 | 7 | - A VPC or import a VPC based on user input 8 | - An EKS Cluster with optional addons and namespace creation 9 | 10 | ### Limitations 11 | 12 | - Today this feature only supports deploying proton resources in the same account where Proton lives 13 | 14 | ### Testing 15 | 16 | To deploy this locally you will need the `proton-inputs.json` file (example located in this directory). 17 | Modify the proton-inputs.json file to manipulate the build accordingly. 18 | 19 | ``` 20 | { 21 | "environment": { 22 | "name": "cdk-eks-env-demo", 23 | "inputs": { 24 | "clusterName": "", 25 | "vpcCidr": "10.0.0.0/16", 26 | "k8Version": "1.23", 27 | "opaGateKeeper": true, 28 | "certManager": true, 29 | "karpenter": true, 30 | "lbController": true, 31 | "metricsServer": true, 32 | "kubeCost": true 33 | } 34 | } 35 | } 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/bin/vpc-eks-cluster.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from "aws-cdk-lib"; 2 | import ClusterConstruct from "../lib/vpc-eks-cluster-stack"; 3 | import { environmentInputs, stackName } from "../lib/constants"; 4 | import { PreReqs } from "../lib/pre-req-stack"; 5 | 6 | const app = new cdk.App(); 7 | const env = { 8 | account: process.env.CDK_DEFAULT_ACCOUNT!, 9 | region: process.env.CDK_DEFAULT_REGION || undefined, 10 | }; 11 | 12 | const appInputs = { env, ...environmentInputs, stackName: stackName }; 13 | 14 | // Build namespace pre-requisites like IAM role 15 | new PreReqs(app, `PreReqStack${stackName}`, { env }); 16 | // Build EKS Stack 17 | new ClusterConstruct(app, appInputs); 18 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##### 3 | # This script converts the cdk outputs into a format that is supported by AWS Proton 4 | ##### 5 | 6 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/vpc-eks-cluster.ts", 3 | "requireApproval": "never", 4 | "outputsFile": "proton-outputs.json", 5 | "watch": { 6 | "include": ["**"], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 25 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/core:checkSecretUsage": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/cdk_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | ### 6 | # Deploy IAM roles for namespaces in pre-req stack, then use the outputs for the EKS cluster stack 7 | ### 8 | 9 | env_name=$(cat proton-inputs.json | jq -r '.environment.name') 10 | echo "{}" | tee ./pre-req-outputs.json 11 | npm run cdk -- deploy --exclusively "PreReqStack${env_name}" --outputs-file ./pre-req-outputs.json 12 | npm run cdk -- deploy --exclusively "${env_name}-eks" --outputs-file ./proton-outputs.json -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import input from "../proton-inputs.json"; 2 | import { InstanceType } from "aws-cdk-lib/aws-ec2"; 3 | 4 | export const environmentInputs = input.environment.inputs; 5 | export const stackName = input.environment.name; 6 | export const BOTTLEROCKET_ON_DEMAND_INSTANCES: InstanceType[] = [ 7 | new InstanceType("t3.medium"), 8 | ]; 9 | export const BOTTLEROCKET_SPOT_INSTANCES: InstanceType[] = [ 10 | new InstanceType("t3.medium"), 11 | new InstanceType("t3.small"), 12 | new InstanceType("t3.large"), 13 | ]; 14 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/lib/pre-req-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib"; 2 | import { Construct } from "constructs"; 3 | import * as cdk from "aws-cdk-lib"; 4 | import * as iam from "aws-cdk-lib/aws-iam"; 5 | import input from "../proton-inputs.json"; 6 | 7 | export class PreReqs extends Stack { 8 | private nsMap: any[]; 9 | constructor(scope: Construct, id: string, props: StackProps) { 10 | super(scope, id, props); 11 | 12 | const envInputs: any = input.environment.inputs; 13 | this.nsMap = []; 14 | 15 | if (input.environment.inputs.hasOwnProperty("namespaces")) { 16 | const namespaces: string[] = envInputs.namespaces; 17 | let ns: string; 18 | 19 | for (ns of namespaces!) { 20 | // Note that the purpose here is to show how platform teams can create IAM roles for developers 21 | // as a part of the template bundle. IAM roles and policies should be scoped to least privilege. 22 | const iamRole = new iam.Role(this, `NSRole${ns}`, { 23 | assumedBy: new cdk.aws_iam.AccountPrincipal( 24 | cdk.Stack.of(this).account 25 | ), 26 | }); 27 | this.nsMap.push({ 28 | name: ns, 29 | roleArn: iamRole.roleArn, 30 | }); 31 | } 32 | } 33 | 34 | Object.entries(this.nsMap).forEach(([_, value]) => { 35 | new CfnOutput(this, value.name, { 36 | value: value.roleArn, 37 | }); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | - echo "PROTON INPUTS == $(cat proton-inputs.json)" 10 | - npm install 11 | - chmod +x ./cdk_deploy.sh ./cdk-to-proton.sh 12 | - ./cdk_deploy.sh 13 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 14 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 15 | deprovision: 16 | - npm install 17 | - echo "{}" | tee ./pre-req-outputs.json 18 | - npm run cdk -- destroy --force --all 19 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vpc-eks-cluster", 3 | "version": "0.1.0", 4 | "bin": { 5 | "vpc-eks-cluster": "bin/vpc-eks-cluster.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "cdk": "2.60.0", 18 | "jest": "^27.5.1", 19 | "ts-jest": "^27.1.4", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "@aws-quickstart/eks-blueprints": "^1.5.4", 25 | "@kubecost/kubecost-eks-blueprints-addon": "^0.1.2", 26 | "aws-cdk-lib": "2.60.0", 27 | "constructs": "^10.0.0", 28 | "source-map-support": "^0.5.21" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "name": "ekscdkbp", 4 | "inputs": { 5 | "clusterName": "", 6 | "k8Version": "1.23", 7 | "opaGateKeeper": true, 8 | "certManager": true, 9 | "karpenter": true, 10 | "lbController": true, 11 | "metricsServer": true, 12 | "kubeCost": true, 13 | "platformTeamRole": "arn:aws:iam::123456789123:role/Admin" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ], 25 | "resolveJsonModule": true, 26 | "esModuleInterop": true, 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "cdk.out" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cdk/environment-templates/cdk-vpc-eks-cluster/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | environment_input_type: "EnvironmentInputs" 5 | types: 6 | EnvironmentInputs: 7 | type: object 8 | description: "Input properties for eks environment." 9 | properties: 10 | clusterName: 11 | title: "Cluster name" 12 | description: "Cluster name or defaults to environment name" 13 | type: string 14 | k8Version: 15 | title: "Kubernetes version" 16 | type: string 17 | enum: ["1.21", "1.22", "1.23"] 18 | default: "1.23" 19 | opaGateKeeper: 20 | title: "Enable OPA GateKeeper Add-On" 21 | type: boolean 22 | default: false 23 | certManager: 24 | title: "Enable Cert Manager Add-On" 25 | type: boolean 26 | default: false 27 | karpenter: 28 | title: "Enable Karpenter Add-On" 29 | type: boolean 30 | default: false 31 | lbController: 32 | title: "Enable AWS Load Balancer Add-On" 33 | type: boolean 34 | default: false 35 | metricsServer: 36 | title: "Enable Metrics Server Add-On" 37 | type: boolean 38 | default: false 39 | kubeCost: 40 | title: "Enable Kubecost Add-On" 41 | type: boolean 42 | default: false 43 | namespaces: 44 | title: "Namespaces to create in cluster" 45 | description: Namespaces to create on the cluster 46 | type: array 47 | example: 48 | - namespace1 49 | - namespace2 50 | items: 51 | type: string 52 | platformTeamRole: 53 | title: "Platform team IAM role ARN" 54 | description: "The Administrator IAM Role for cluster and console access" 55 | type: string 56 | required: 57 | - platformTeamRole 58 | -------------------------------------------------------------------------------- /cdk/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | # Run when create/update is triggered for environment or service 10 | # Install dependencies 11 | - npm install 12 | - cdk deploy --require-approval never 13 | # Script to convert CFN outputs into outputs for Proton 14 | - chmod +x ./cdk-to-proton.sh 15 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 16 | # Notify AWS Proton of deployment status 17 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 18 | deprovision: 19 | # Install dependencies and destroy resources 20 | - npm install 21 | - cdk destroy --force 22 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - cdk-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := cdk-ecs-fargate-pipeline-ci-service 2 | key := cdk-ecs-fargate-pipeline-ci-service.tar.gz 3 | version := 1 4 | minor_version := 0 5 | service_name := cdk-ecs-fargate-pipeline-ci-service 6 | 7 | all: help 8 | 9 | .PHONY: help 10 | help: Makefile 11 | @echo 12 | @echo " Choose a make command to run" 13 | @echo 14 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 15 | @echo 16 | 17 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 18 | .PHONY: template 19 | template: 20 | if [[ -z "${bucket}" ]]; then echo "S3 Bucket argument is required \"bucket=s3-bucket-name\""; exit 1; fi 21 | 22 | aws proton create-service-template \ 23 | --name ${template} \ 24 | --display-name "Load Balanced ECS Fargate Service w/ CI Pipeline" \ 25 | --description "Load Balanced ECS Fargate Service w/CI Pipeline" 26 | 27 | tar --exclude="node_modules" --exclude="cdk.out" --exclude="cdk.context.json" -zcvf \ 28 | ${key} schema instance_infrastructure pipeline_infrastructure 29 | aws s3 cp ${key} s3://${bucket}/${key} 30 | rm ${key} 31 | 32 | aws proton create-service-template-version \ 33 | --template-name ${template} \ 34 | --description "registered from CLI" \ 35 | --compatible-environment-templates="majorVersion=1,templateName=cdk-vpc-ecs-cluster" \ 36 | --source s3="{bucket=${bucket},key=${key}}" \ 37 | --major-version ${version} 38 | 39 | aws proton wait service-template-version-registered \ 40 | --template-name ${template} --major-version ${version} --minor-version ${minor_version} 41 | 42 | aws proton update-service-template-version \ 43 | --template-name ${template} --major-version ${version} --minor-version ${minor_version} --status PUBLISHED 44 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/configureinstance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/configureinstance.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/configureserviceinstancessync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/configureserviceinstancessync.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/devsvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/devsvc.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/prodcreating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/prodcreating.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/pullrequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/pullrequest.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/pullrequest1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/pullrequest1.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/selectervicetemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/selectervicetemplate.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/serviceinstance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/serviceinstance.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/servicewsourcecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/servicewsourcecode.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/syncconfiguration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/images/syncconfiguration.png -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/bin/ecs-fargate-service.ts: -------------------------------------------------------------------------------- 1 | import { App } from "aws-cdk-lib"; 2 | import { EcsFargateServiceStack } from "../lib/ecs-fargate-service"; 3 | import input from "../proton-inputs.json"; 4 | 5 | const protonEnv = { 6 | account: process.env.CDK_DEFAULT_ACCOUNT, 7 | region: process.env.CDK_DEFAULT_REGION, 8 | }; 9 | 10 | const stackName = input.service_instance.name; 11 | 12 | const app = new App(); 13 | 14 | new EcsFargateServiceStack(app, "ProtonSvc", { 15 | env: protonEnv, 16 | stackName: stackName, 17 | }); 18 | 19 | app.synth(); 20 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/ecs-fargate-service.ts", 3 | "outputsFile": "proton-outputs.json", 4 | "watch": { 5 | "include": ["**"], 6 | "exclude": [ 7 | "README.md", 8 | "cdk*.json", 9 | "**/*.d.ts", 10 | "**/*.js", 11 | "tsconfig.json", 12 | "package*.json", 13 | "yarn.lock", 14 | "node_modules", 15 | "test" 16 | ] 17 | }, 18 | "context": { 19 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 20 | "@aws-cdk/core:stackRelativeExports": true, 21 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 22 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 23 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 25 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 26 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 27 | "@aws-cdk/core:checkSecretUsage": true, 28 | "@aws-cdk/aws-iam:minimizePolicies": true, 29 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 30 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 31 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/lib/load-balancer.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Duration, CfnOutput } from "aws-cdk-lib"; 2 | import { Construct } from "constructs"; 3 | import * as ec2 from "aws-cdk-lib/aws-ec2"; 4 | import * as lb from "aws-cdk-lib/aws-elasticloadbalancingv2"; 5 | 6 | export interface EcsAlbStackProps extends StackProps { 7 | stackName: string | undefined; 8 | vpc: ec2.IVpc; 9 | listenerPort: number; 10 | containerPort: number; 11 | public: boolean; 12 | } 13 | 14 | export class EcsAlbStack extends Construct { 15 | public lbSecGrp: ec2.SecurityGroup; 16 | public loadBalancer: lb.IApplicationLoadBalancer; 17 | public targetGroup: lb.ApplicationTargetGroup; 18 | public lbListener: lb.IApplicationListener; 19 | 20 | constructor(scope: Construct, id: string, props: EcsAlbStackProps) { 21 | super(scope, id); 22 | 23 | const lbSubnets = props.vpc.selectSubnets({ 24 | subnetType: props.public 25 | ? ec2.SubnetType.PUBLIC 26 | : ec2.SubnetType.PRIVATE_WITH_EGRESS, 27 | }); 28 | 29 | this.lbSecGrp = new ec2.SecurityGroup(this, "LbSecGrp", { 30 | vpc: props.vpc, 31 | allowAllOutbound: true, 32 | }); 33 | 34 | this.lbSecGrp.addIngressRule( 35 | ec2.Peer.anyIpv4(), 36 | ec2.Port.tcp(props.listenerPort) 37 | ); 38 | 39 | this.loadBalancer = new lb.ApplicationLoadBalancer(this, "ECSSvcALB", { 40 | vpc: props.vpc, 41 | internetFacing: props.public, 42 | vpcSubnets: lbSubnets, 43 | securityGroup: this.lbSecGrp, 44 | }); 45 | 46 | this.targetGroup = new lb.ApplicationTargetGroup(this, "LbTg", { 47 | vpc: props.vpc, 48 | deregistrationDelay: Duration.seconds(30), 49 | port: props.containerPort, 50 | targetType: lb.TargetType.IP, 51 | protocol: lb.ApplicationProtocol.HTTP, 52 | }); 53 | 54 | this.lbListener = this.loadBalancer.addListener("LbListener", { 55 | port: props.listenerPort, 56 | defaultTargetGroups: [this.targetGroup], 57 | protocol: lb.ApplicationProtocol.HTTP, 58 | }); 59 | 60 | new CfnOutput(this, "LBDNSName", { 61 | value: this.loadBalancer.loadBalancerDnsName, 62 | exportName: `LBDNSName-${props.stackName}`, 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | - cat proton-inputs.json 10 | # Run when create/update is triggered for environment or service 11 | # Install dependencies 12 | - npm install 13 | - npm run cdk -- deploy --require-approval never 14 | # Script to convert CFN outputs into outputs for Proton 15 | - chmod +x ./cdk-to-proton.sh 16 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 17 | # Notify AWS Proton of deployment status 18 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 19 | deprovision: 20 | # Install dependencies and destroy resources 21 | - npm install 22 | - npm run cdk -- destroy --force 23 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecs-fargate-service", 3 | "version": "0.1.0", 4 | "bin": { 5 | "ecs-fargate-service": "bin/ecs-fargate-service.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "jest": "^27.5.1", 18 | "ts-jest": "^27.1.4", 19 | "aws-cdk": "2.63.2", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.63.2", 25 | "constructs": "^10.0.0", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "account_id": "12345678910", 4 | "name": "cdk-ecs-cluster-env", 5 | "outputs": { 6 | "ECSClusterArn": "arn:aws:ecs:us-west-2:12345678910:cluster/cdk-ecs-cluster-env", 7 | "ECSClusterSecGrps": "[]", 8 | "VPCId": "vpc-0287059029960c111", 9 | "ECSClusterName": "cdk-ecs-cluster-env", 10 | "ECSClusterSDNamespace": "ecs-cdk-demo.dev" 11 | } 12 | }, 13 | "service": { 14 | "name": "cdk-svc-demo", 15 | "repository_connection_arn": "", 16 | "repository_id": "", 17 | "branch_name": "" 18 | }, 19 | "service_instance": { 20 | "name": "test", 21 | "inputs": { 22 | "port": 3000, 23 | "desired_count": 1, 24 | "task_size": "small", 25 | "image": "12345678910.dkr.ecr.ap-northeast-1.amazonaws.com/service-sync-demo-pipeline-ecrrepoc36dc9e6-8e6qjsxk550f", 26 | "imageTag": "latest", 27 | "load_balanced": true, 28 | "load_balanced_public": true, 29 | "service_discovery_name": "" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/instance_infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ], 25 | "resolveJsonModule": true, 26 | "esModuleInterop": true, 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "cdk.out" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/.prettierignore: -------------------------------------------------------------------------------- 1 | lib/container-image-pipeline.ts -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/bin/pipeline.ts: -------------------------------------------------------------------------------- 1 | import { App } from "aws-cdk-lib"; 2 | import { ContainerImageBuildPipeline } from "../lib/container-image-pipeline"; 3 | import input from "../proton-inputs.json"; 4 | 5 | const protonEnv = { 6 | account: process.env.CDK_DEFAULT_ACCOUNT, 7 | region: process.env.CDK_DEFAULT_REGION, 8 | }; 9 | 10 | const stackName = `${input.service.name}-pipeline`; 11 | 12 | const app = new App(); 13 | 14 | new ContainerImageBuildPipeline(app, "ProtonPipeline", { 15 | env: protonEnv, 16 | stackName: stackName, 17 | }); 18 | 19 | app.synth(); 20 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/pipeline.ts", 3 | "outputsFile": "proton-outputs.json", 4 | "watch": { 5 | "include": ["**"], 6 | "exclude": [ 7 | "README.md", 8 | "cdk*.json", 9 | "**/*.d.ts", 10 | "**/*.js", 11 | "tsconfig.json", 12 | "package*.json", 13 | "yarn.lock", 14 | "node_modules", 15 | "test" 16 | ] 17 | }, 18 | "context": { 19 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 20 | "@aws-cdk/core:stackRelativeExports": true, 21 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 22 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 23 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 25 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 26 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 27 | "@aws-cdk/core:checkSecretUsage": true, 28 | "@aws-cdk/aws-iam:minimizePolicies": true, 29 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 30 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 31 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | - cat proton-inputs.json 10 | # Run when create/update is triggered for environment or service 11 | # Install dependencies 12 | - npm install 13 | - npm run cdk -- deploy --require-approval never 14 | # Script to convert CFN outputs into outputs for Proton 15 | - chmod +x ./cdk-to-proton.sh 16 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 17 | # Notify AWS Proton of deployment status 18 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 19 | deprovision: 20 | # Install dependencies and destroy resources 21 | - npm install 22 | - npm run cdk -- destroy --force 23 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecs-fargate-service", 3 | "version": "0.1.0", 4 | "bin": { 5 | "ecs-fargate-service": "bin/ecs-fargate-service.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "jest": "^27.5.1", 18 | "ts-jest": "^27.1.4", 19 | "aws-cdk": "2.63.2", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.63.2", 25 | "constructs": "^10.0.0", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/scripts/ecr_policy_updater.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import boto3 4 | import json 5 | from os import getenv 6 | 7 | print("Updating ECR Repository Policy to reflect all environments for this service") 8 | 9 | proton = boto3.client("proton") 10 | 11 | service_name = getenv("SERVICE_NAME", "service-sync-ci-demo") 12 | proton_acct_id = getenv("PROTON_ACCT_ID", "1234") 13 | service_instances = proton.list_service_instances(serviceName=service_name) 14 | environment_names = list( 15 | set([x["environmentName"] for x in service_instances["serviceInstances"]]) 16 | ) 17 | account_ids = [ 18 | proton.get_environment(name=x)["environment"].get( 19 | "environmentAccountId", proton_acct_id 20 | ) 21 | for x in environment_names 22 | ] 23 | 24 | ecr_repo_name = getenv( 25 | "REPO_NAME", "service-sync-ci-demo-pipeline-ecrrepoc36dc9e6-kuo5hlmpkeyl" 26 | ) 27 | ecr = boto3.client("ecr") 28 | policy = ecr.get_repository_policy(repositoryName=ecr_repo_name) 29 | policy_text = json.loads(policy["policyText"]) 30 | 31 | print(f"Current Policy:\n=====\n{json.dumps(policy_text, indent=2)}\n=====") 32 | 33 | policy_list = list() 34 | for acct in account_ids: 35 | principal = f"arn:aws:iam::{acct}:root" 36 | policy_list.append(principal) 37 | 38 | policy_text["Statement"][0]["Principal"]["AWS"] = policy_list 39 | 40 | print(f"New Policy:\n=====\n{json.dumps(policy_text, indent=2)}\n=====") 41 | 42 | ecr.set_repository_policy( 43 | repositoryName=ecr_repo_name, policyText=json.dumps(policy_text) 44 | ) 45 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | var proton_inputs_json_1 = require("./proton-inputs.json"); 4 | var serviceOutputs = proton_inputs_json_1["default"].service; 5 | console.log(serviceOutputs); 6 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/test.ts: -------------------------------------------------------------------------------- 1 | import input from "./proton-inputs.json"; 2 | 3 | const serviceOutputs: { [index: string]: any } = input.service_instances; 4 | 5 | const result = [ 6 | ...new Set( 7 | serviceOutputs.map( 8 | (service: any) => 9 | `arn:aws:proton:us-west-2:${service.environment.account_id}:environment/${service.environment.name}*,` 10 | ) 11 | ), 12 | ]; 13 | 14 | console.log(typeof Array.from(result)); 15 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service-ci-pipeline/v1/pipeline_infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ], 25 | "resolveJsonModule": true, 26 | "esModuleInterop": true, 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "cdk.out" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - cdk-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/README.md: -------------------------------------------------------------------------------- 1 | # ECS Fargate Service Template Bundle 2 | 3 | ### What this builds 4 | 5 | This template bundle will deploy the following: 6 | 7 | - ECS task definition with Fargate as the compute 8 | - ECS service with desired task count and compute requirements defined in the schema 9 | - (Optional) Application Load Balancer that is attached to the ECS service 10 | - Works with the [vpc-ecs-cluster](../../../environment-templates/vpc-ecs-cluster) environment template. 11 | 12 | ### Testing 13 | 14 | To deploy this locally you will need the `proton-inputs.json` file (example located in this directory). 15 | Modify the proton-inputs.json file with the proper values from an existing environment. 16 | It's recommended that you deploy the (vpc-ecs-cluster)[../../../environment-templates/vpc-ecs-cluster] environment and use the values from there to fill in the proton-inputs.json. 17 | For the service map, you can leave or modify values as needed. 18 | 19 | ``` 20 | { 21 | "environment": { 22 | "account_id": "" 23 | "name": "cdk-env-demo", 24 | "outputs": { 25 | "ECSClusterArn": "", 26 | "ECSClusterSecGrps": "[]", 27 | "VPCId": "", 28 | "ECSClusterName": "", 29 | "ECSClusterSDNamespace": "" 30 | } 31 | }, 32 | "service": { 33 | "name": "cdk-svc-demo", 34 | "repository_connection_arn": "", 35 | "repository_id": "", 36 | "branch_name": "" 37 | }, 38 | "service_instance": { 39 | "name": "test", 40 | "inputs": { 41 | "port": 80, 42 | "desired_count": 1, 43 | "task_size": "x-small", 44 | "image": "public.ecr.aws/nginx/nginx:stable", 45 | "load_balanced": true, 46 | "load_balanced_public": true 47 | } 48 | } 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/bin/ecs-fargate-service.ts: -------------------------------------------------------------------------------- 1 | import { App } from "aws-cdk-lib"; 2 | import { EcsFargateServiceStack } from "../lib/ecs-fargate-service"; 3 | import input from "../proton-inputs.json"; 4 | 5 | const protonEnv = { 6 | account: process.env.CDK_DEFAULT_ACCOUNT, 7 | region: process.env.CDK_DEFAULT_REGION, 8 | }; 9 | 10 | const stackName = input.service_instance.name; 11 | 12 | const app = new App(); 13 | 14 | new EcsFargateServiceStack(app, "ProtonSvc", { 15 | env: protonEnv, 16 | stackName: stackName, 17 | }); 18 | 19 | app.synth(); 20 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/cdk-to-proton.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map_values(.value) | add | to_entries | map({key:.key, valueString:.value})' -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/ecs-fargate-service.ts", 3 | "outputsFile": "proton-outputs.json", 4 | "watch": { 5 | "include": ["**"], 6 | "exclude": [ 7 | "README.md", 8 | "cdk*.json", 9 | "**/*.d.ts", 10 | "**/*.js", 11 | "tsconfig.json", 12 | "package*.json", 13 | "yarn.lock", 14 | "node_modules", 15 | "test" 16 | ] 17 | }, 18 | "context": { 19 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 20 | "@aws-cdk/core:stackRelativeExports": true, 21 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 22 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 23 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 25 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 26 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 27 | "@aws-cdk/core:checkSecretUsage": true, 28 | "@aws-cdk/aws-iam:minimizePolicies": true, 29 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 30 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 31 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/lib/load-balancer.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Duration, CfnOutput } from "aws-cdk-lib"; 2 | import { Construct } from "constructs"; 3 | import * as ec2 from "aws-cdk-lib/aws-ec2"; 4 | import * as lb from "aws-cdk-lib/aws-elasticloadbalancingv2"; 5 | 6 | export interface EcsAlbStackProps extends StackProps { 7 | stackName: string | undefined; 8 | vpc: ec2.IVpc; 9 | listenerPort: number; 10 | containerPort: number; 11 | public: boolean; 12 | } 13 | 14 | export class EcsAlbStack extends Construct { 15 | public lbSecGrp: ec2.SecurityGroup; 16 | public loadBalancer: lb.IApplicationLoadBalancer; 17 | public targetGroup: lb.ApplicationTargetGroup; 18 | public lbListener: lb.IApplicationListener; 19 | 20 | constructor(scope: Construct, id: string, props: EcsAlbStackProps) { 21 | super(scope, id); 22 | 23 | const lbSubnets = props.vpc.selectSubnets({ 24 | subnetType: props.public 25 | ? ec2.SubnetType.PUBLIC 26 | : ec2.SubnetType.PRIVATE_WITH_EGRESS, 27 | }); 28 | 29 | this.lbSecGrp = new ec2.SecurityGroup(this, "LbSecGrp", { 30 | vpc: props.vpc, 31 | allowAllOutbound: true, 32 | }); 33 | 34 | this.lbSecGrp.addIngressRule( 35 | ec2.Peer.anyIpv4(), 36 | ec2.Port.tcp(props.listenerPort) 37 | ); 38 | 39 | this.loadBalancer = new lb.ApplicationLoadBalancer(this, "ECSSvcALB", { 40 | vpc: props.vpc, 41 | internetFacing: props.public, 42 | vpcSubnets: lbSubnets, 43 | securityGroup: this.lbSecGrp, 44 | }); 45 | 46 | this.targetGroup = new lb.ApplicationTargetGroup(this, "LbTg", { 47 | vpc: props.vpc, 48 | deregistrationDelay: Duration.seconds(30), 49 | port: props.containerPort, 50 | targetType: lb.TargetType.IP, 51 | protocol: lb.ApplicationProtocol.HTTP, 52 | }); 53 | 54 | this.lbListener = this.loadBalancer.addListener("LbListener", { 55 | port: props.listenerPort, 56 | defaultTargetGroups: [this.targetGroup], 57 | protocol: lb.ApplicationProtocol.HTTP, 58 | }); 59 | 60 | new CfnOutput(this, "LBDNSName", { 61 | value: this.loadBalancer.loadBalancerDnsName, 62 | exportName: `LBDNSName-${props.stackName}`, 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | provision: 9 | # Run when create/update is triggered for environment or service 10 | # Install dependencies 11 | - npm install 12 | - npm run cdk -- deploy --require-approval never 13 | # Script to convert CFN outputs into outputs for Proton 14 | - chmod +x ./cdk-to-proton.sh 15 | - cat proton-outputs.json | ./cdk-to-proton.sh > outputs.json 16 | # Notify AWS Proton of deployment status 17 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 18 | deprovision: 19 | # Install dependencies and destroy resources 20 | - npm install 21 | - npm run cdk -- destroy --force 22 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecs-fargate-service", 3 | "version": "0.1.0", 4 | "bin": { 5 | "ecs-fargate-service": "bin/ecs-fargate-service.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "jest": "^27.5.1", 18 | "ts-jest": "^27.1.4", 19 | "aws-cdk": "2.63.2", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.63.2", 25 | "constructs": "^10.0.0", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "account_id": "12345678910", 4 | "name": "cdk-ecs-cluster-env", 5 | "outputs": { 6 | "ECSClusterArn": "arn:aws:ecs:us-west-2:12345678910:cluster/cdk-ecs-cluster-env", 7 | "ECSClusterSecGrps": "[]", 8 | "VPCId": "vpc-0287059029960c111", 9 | "ECSClusterName": "cdk-ecs-cluster-env", 10 | "ECSClusterSDNamespace": "ecs-cdk-demo.dev" 11 | } 12 | }, 13 | "service": { 14 | "name": "cdk-svc-demo", 15 | "repository_connection_arn": "", 16 | "repository_id": "", 17 | "branch_name": "" 18 | }, 19 | "service_instance": { 20 | "name": "test", 21 | "inputs": { 22 | "port": 3000, 23 | "desired_count": 1, 24 | "task_size": "small", 25 | "image": "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50", 26 | "load_balanced": true, 27 | "load_balanced_public": true, 28 | "service_discovery_name": "" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/instance_infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ], 25 | "resolveJsonModule": true, 26 | "esModuleInterop": true, 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "cdk.out" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /cdk/service-templates/cdk-ecs-fargate-service/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | service_input_type: "ECSFargateSvcInputs" 5 | types: 6 | ECSFargateSvcInputs: 7 | type: object 8 | description: "Input properties for a loadbalanced Fargate service" 9 | properties: 10 | port: 11 | title: "Container port" 12 | type: number 13 | description: "The container port to route traffic to" 14 | default: 80 15 | minimum: 0 16 | maximum: 65535 17 | desired_count: 18 | title: "Desired count" 19 | type: number 20 | description: "The default number of Fargate tasks you want running" 21 | default: 1 22 | minimum: 1 23 | task_size: 24 | title: "Task size" 25 | type: string 26 | description: "The size of the task you want to run" 27 | enum: ["x-small", "small", "medium", "large", "x-large"] 28 | default: "x-small" 29 | image: 30 | title: "Container image URI" 31 | type: string 32 | description: "The name/url of the container image" 33 | default: "public.ecr.aws/nginx/nginx:stable" 34 | minLength: 1 35 | maxLength: 200 36 | service_discovery_name: 37 | title: "Service discovery name" 38 | type: string 39 | description: "The name of the service to register in service discovery" 40 | minLength: 3 41 | maxLength: 24 42 | load_balanced: 43 | title: "Enable Application Load Balancing" 44 | type: boolean 45 | description: "If enabled, Application Load Balancer will get created and point to the default port of the container" 46 | default: false 47 | load_balanced_public: 48 | title: "Public Facing LB" 49 | type: boolean 50 | description: "This determines where the load balancer is placed in the network - internet facing or private to the vpc" 51 | default: public 52 | env_vars: 53 | title: "Environment variables" 54 | description: "Example: ENV_VAR_1=VALUE" 55 | type: array 56 | example: 57 | - "ENV_VAR1=TEST1" 58 | - "ENV_VAR2=TEST2" 59 | items: 60 | type: string 61 | -------------------------------------------------------------------------------- /pulumi/README.md: -------------------------------------------------------------------------------- 1 | # Pulumi Proton Examples 2 | 3 | This directory is home to sample Pulumi templates to gain an understanding of how one can leverage Pulumi with AWS Proton via Codebuild provisioning. 4 | 5 | ### Requirements 6 | 7 | #### Personal Access Token 8 | 9 | In order to interact with Pulumi API's, you will need to create a [Personal Access Token](https://www.pulumi.com/docs/intro/pulumi-service/accounts/#access-tokens) and store it in [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html). 10 | Codebuild will reference this secret which is defined in the manifest file. More information on this in the manifest section below. 11 | 12 | #### Manifest 13 | 14 | - Environment variables 15 | - To avoid duplication, set the pulumi organization and pulumi namespace as environment variables in the manifest. 16 | - In order for the Pulumi CLI to connect back to Pulumi, you will need to reference the secret from Secrets Manager. 17 | ```yaml 18 | env: 19 | variables: 20 | PULUMI_ORG: adamjkeller 21 | PULUMI_NAMESPACE: proton-environment 22 | secrets_manager: 23 | PULUMI_ACCESS_TOKEN: "proton/pulumi-access-token:Secret" 24 | ``` 25 | - Gathering the outputs to send to AWS Proton post deployment. 26 | - If you want Proton to consume outputs from your stack, there is a very small [shell script](./pulumi-to-proton-outputs.sh) that is included in this repo that will convert the outputs into the required JSON for Proton to consume. 27 | ```yaml 28 | - pulumi stack output --json | ./pulumi-to-proton-outputs.sh > outputs.json 29 | ``` 30 | - Post deployment 31 | 32 | - Proton needs to be notified when the deployment is complete. If the Codebuild job fails, Proton will surface that information. On success, the following command needs to be ran: 33 | 34 | ```bash 35 | aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 36 | ``` 37 | 38 | ### Examples 39 | 40 | In the root of the `./pulumi` directory we have the following examples: 41 | 42 | 1. An example [manifest](./manifest.yaml) file that covers the requirements to have a successful deployment. 43 | 2. The output rendering [script](./pulumi-to-proton-outputs.sh). 44 | 3. Example [environment](./environment-templates/) and [service](./service-templates/) templates under their respective directories. 45 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/README.md: -------------------------------------------------------------------------------- 1 | # VPC ECS Cluster Environment 2 | 3 | ### Description 4 | 5 | This template will create a vpc with defaults set by Pulumi within the library. 6 | Additionally it will create an ECS cluster with the option to turn on enhanced monitoring via Container Insights. 7 | 8 | ### IaC 9 | 10 | This template is built using Pulumi and their Typescript libraries. 11 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: proton-environment 2 | description: Stacks created by AWS Proton for environment template "vpc-ecs-cluster" 3 | runtime: nodejs 4 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/index.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import input from "./proton-inputs.json"; 3 | 4 | const envName = input.environment.name; 5 | const envInputs = input.environment.inputs; 6 | 7 | // https://www.pulumi.com/docs/guides/crosswalk/aws/vpc/ 8 | const vpc = new awsx.ec2.Vpc(envName, {}); 9 | 10 | // https://www.pulumi.com/docs/guides/crosswalk/aws/ecs/ 11 | const ecsCluster = new awsx.ecs.Cluster(envName, { 12 | vpc: vpc, 13 | settings: [ 14 | { 15 | name: "containerInsights", 16 | value: envInputs.enhanced_cluster_monitoring ? "enabled" : "disabled", 17 | }, 18 | ], 19 | }); 20 | 21 | export const vpcId = vpc.id; 22 | export const privateSubnetIds = vpc.privateSubnetIds; 23 | export const publicSubnetIds = vpc.publicSubnetIds; 24 | export const ecsClusterName = ecsCluster.cluster.name; 25 | export const ecsClusterId = ecsCluster.cluster.id; 26 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proton-demo", 3 | "main": "index.ts", 4 | "devDependencies": { 5 | "@types/node": "^14" 6 | }, 7 | "dependencies": { 8 | "@pulumi/pulumi": "^3.0.0", 9 | "@pulumi/aws": "^5.0.0", 10 | "@pulumi/awsx": "^0.40.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "name": "localTestEnv", 4 | "inputs": { 5 | "vpc_cidr": "10.0.0.0/16", 6 | "enhanced_cluster_monitoring": true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/pulumi-to-proton-outputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map({key:.key, valueString:.value|tostring})' -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "outDir": "bin", 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "resolveJsonModule": true, 15 | "esModuleInterop": true 16 | }, 17 | "files": ["index.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /pulumi/environment-templates/vpc-ecs-cluster/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | environment_input_type: "VpcEcsClusterEnv" 5 | types: 6 | VpcEcsClusterEnv: 7 | type: object 8 | description: "Input properties for my environment" 9 | properties: 10 | enhanced_cluster_monitoring: 11 | title: "Enable enhanced cluster monitoring via Container Insights" 12 | type: boolean 13 | default: false 14 | description: "Enable Cloudwatch Container Insights for the cluster" 15 | -------------------------------------------------------------------------------- /pulumi/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/amazonlinux2-x86_64-standard:4.0 6 | runtimes: 7 | nodejs: 16 8 | env: 9 | variables: 10 | PULUMI_ORG: 11 | PULUMI_PROJECT: proton- 12 | secrets_manager: 13 | # Create secret in Secrets Manager following this format 14 | PULUMI_ACCESS_TOKEN: "proton/pulumi-access-token:Secret" 15 | provision: 16 | # Steps to be run when an environment is deployed or updated 17 | # Download Pulumi binary and install dependencies 18 | - curl -fsSL https://get.pulumi.com | sh 19 | - export PATH=$PATH:$HOME/.pulumi/bin 20 | - npm install 21 | # Setting stack name from the name of the environment and setting up pulumi config 22 | - export STACK_NAME=$(cat proton-inputs.json | jq -r '.environment.name') 23 | - pulumi stack select $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME || pulumi stack init $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME 24 | - pulumi config set aws:region $AWS_DEFAULT_REGION 25 | - pulumi config set org $PULUMI_ORG 26 | # Deploying stack 27 | - pulumi up --yes --stack $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME 28 | # Running script to convert pulumi outputs for Proton to injest and update Proton on deployment status 29 | - chmod +x ./pulumi-to-proton-outputs.sh 30 | - pulumi stack output --json | ./pulumi-to-proton-outputs.sh > outputs.json 31 | - aws proton notify-resource-deployment-status-change --resource-arn $RESOURCE_ARN --outputs file://./outputs.json 32 | deprovision: 33 | # Steps to be ran when an environment is deleted 34 | # Download Pulumi binary and install dependencies 35 | - curl -fsSL https://get.pulumi.com | sh 36 | - export PATH=$PATH:$HOME/.pulumi/bin 37 | - npm install 38 | # Setting stack name from the name of the environment and setting up pulumi config 39 | - export STACK_NAME=$(cat proton-inputs.json | jq -r '.environment.name') 40 | - pulumi stack select $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME || pulumi stack init $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME 41 | - pulumi config set aws:region $AWS_DEFAULT_REGION 42 | - pulumi config set org $PULUMI_ORG 43 | # Destroying Stack 44 | - pulumi destroy -s $PULUMI_ORG/$PULUMI_PROJECT/$STACK_NAME 45 | - pulumi stack rm proton-vpc-ecs-cluster 46 | -------------------------------------------------------------------------------- /pulumi/pulumi-to-proton-outputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map({key:.key, valueString:.value|tostring})' -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environment: 2 | - vpc-ecs-cluster:1 -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/README.md: -------------------------------------------------------------------------------- 1 | # ECS Fargate Load Balanced Service 2 | 3 | ### Description 4 | 5 | This template will create a load balanced ECS service. 6 | 7 | ### IaC 8 | 9 | This template is built using Pulumi and their Typescript libraries. 10 | -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/instance_infrastructure/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: proton-service 2 | description: Stacks created by AWS Proton for service template "ecs-fargate-load-balanced-service" 3 | runtime: nodejs 4 | -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/instance_infrastructure/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proton-demo", 3 | "main": "index.ts", 4 | "devDependencies": { 5 | "@types/node": "^14" 6 | }, 7 | "dependencies": { 8 | "@pulumi/pulumi": "^3.0.0", 9 | "@pulumi/aws": "^5.0.0", 10 | "@pulumi/awsx": "^0.40.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/instance_infrastructure/proton-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "account_id": "696393176379", 4 | "name": "environment-pulumi-cb-demo-1", 5 | "outputs": { 6 | "publicSubnetIds": "[\"subnet-0f013308e69b083ea\",\"subnet-0d3d53f1639d48659\"]", 7 | "ecsClusterId": "arn:aws:ecs:us-east-1:696393176379:cluster/pulumi-example-5-10cad46", 8 | "ecsClusterName": "pulumi-example-5-10cad46", 9 | "vpcId": "vpc-0bb1f48265f448d11", 10 | "privateSubnetIds": "[\"subnet-0c69b7af3150d1af7\",\"subnet-04a81ed754720cb5e\"]" 11 | } 12 | }, 13 | "service": { 14 | "name": "pulumi-service-demo", 15 | "repository_connection_arn": "", 16 | "repository_id": "", 17 | "branch_name": "" 18 | }, 19 | "service_instance": { 20 | "name": "pulumi-dev", 21 | "inputs": { 22 | "containerPort": 80, 23 | "desiredCount": 1, 24 | "container_image_uri": "public.ecr.aws/nginx/nginx:stable" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/instance_infrastructure/pulumi-to-proton-outputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jq 'to_entries | map({key:.key, valueString:.value|tostring})' -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/instance_infrastructure/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "outDir": "bin", 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "resolveJsonModule": true, 15 | "esModuleInterop": true 16 | }, 17 | "files": ["index.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /pulumi/service-templates/ecs-fargate-load-balanced-service/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | service_input_type: "PulumiDemoServiceInput" 5 | types: 6 | PulumiDemoServiceInput: 7 | type: object 8 | description: "Input properties for a load balanced Fargate service launched via Pulumi" 9 | properties: 10 | containerPort: 11 | title: "Container Port" 12 | type: number 13 | description: "The port to route traffic to" 14 | default: 80 15 | minimum: 0 16 | maximum: 65535 17 | desiredCount: 18 | title: "Desired count of running tasks" 19 | type: number 20 | description: "How many containers to run for this Fargate service" 21 | default: 1 22 | minimum: 0 23 | maximum: 10 24 | container_image_uri: 25 | title: "Container Image URI" 26 | type: string 27 | description: "The name/url of the container image" 28 | default: "public.ecr.aws/nginx/nginx:stable" 29 | minLength: 1 30 | maxLength: 200 31 | -------------------------------------------------------------------------------- /terraform/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | .terraform.lock.hcl 3 | terraform.tfvars.json 4 | -------------------------------------------------------------------------------- /terraform/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: ^terraform/ 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.3.0 5 | hooks: 6 | - id: trailing-whitespace 7 | - id: check-merge-conflict 8 | - id: detect-private-key 9 | - id: detect-aws-credentials 10 | args: ["--allow-missing-credentials"] 11 | 12 | - repo: https://github.com/antonbabenko/pre-commit-terraform 13 | rev: v1.76.0 14 | hooks: 15 | - id: terraform_fmt 16 | - id: terraform_docs 17 | args: 18 | - "--args=--lockfile=false" 19 | - id: terraform_tflint 20 | # ignore until `pipeline_endpoint` is supported 21 | exclude: ./service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/ 22 | args: 23 | - "--args=--only=terraform_deprecated_interpolation" 24 | - "--args=--only=terraform_deprecated_index" 25 | - "--args=--only=terraform_unused_declarations" 26 | - "--args=--only=terraform_comment_syntax" 27 | - "--args=--only=terraform_documented_outputs" 28 | - "--args=--only=terraform_documented_variables" 29 | - "--args=--only=terraform_typed_variables" 30 | - "--args=--only=terraform_module_pinned_source" 31 | - "--args=--only=terraform_naming_convention" 32 | - "--args=--only=terraform_standard_module_structure" 33 | - "--args=--only=terraform_workspace_remote" 34 | -------------------------------------------------------------------------------- /terraform/.tool-versions: -------------------------------------------------------------------------------- 1 | terraform 1.3.4 -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Proton Examples 2 | 3 | This directory is home to sample Terraform templates to gain an understanding of how one can leverage Terraform with AWS Proton via Codebuild provisioning. 4 | 5 | Please see the [environment](./environment-templates/README.md) and [service](./service-templates/README.md) templates under their respective directories. 6 | 7 | Follow the [README](./environment-templates/tf-vpc-ecs-cluster/v1/README.md) [files](./service-templates/tf-ecs-fargate-lb-service/v1/README.md) in the template directories for instructions on how to register them with AWS Proton. 8 | 9 | 10 | ## Contributing 11 | 12 | See [CONTRIBUTING](CONTRIBUTING.md) for more information. 13 | 14 | These samples are configured to use the [pre-commit](https://pre-commit.com/) tool for running various checks before committing changes. Please install the tool and run `pre-commit install` so that the automated checks are run before committing. You can also run `pre-commit run --all-files` to run the checks on-demand. 15 | 16 | The following additional tools need to be installed 17 | 18 | - [terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) 19 | - [tflint](https://github.com/terraform-linters/tflint) 20 | - [terraform-docs](https://terraform-docs.io/) 21 | -------------------------------------------------------------------------------- /terraform/environment-templates/README.md: -------------------------------------------------------------------------------- 1 | ## Example Terraform Proton Environment Templates 2 | 3 | - [vpc-ecs-cluster](./tf-vpc-ecs-cluster/v1/README.md) - provisions a VPC network, an ECS cluster, and an S3 bucket for storing Terraform state 4 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-vpc-ecs-cluster 2 | key := proton-environment-template.tar.gz 3 | 4 | all: help 5 | 6 | .PHONY: help 7 | help: Makefile 8 | @echo 9 | @echo " Choose a make command to run" 10 | @echo 11 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 12 | @echo 13 | 14 | ## template: create an environment template and register a version, ex. `make template bucket=my-bucket version=1` 15 | .PHONY: template 16 | template: 17 | aws proton create-environment-template \ 18 | --name ${template} \ 19 | --display-name "Environment for ECS Fargate" \ 20 | --description "Environment for provisioning ECS Fargate services" 21 | 22 | tar -zcvf ${key} schema infrastructure 23 | aws s3 cp ${key} s3://${bucket}/${key} 24 | rm ${key} 25 | 26 | aws proton create-environment-template-version \ 27 | --template-name ${template} \ 28 | --description "registered from CLI" \ 29 | --source s3="{bucket=${bucket},key=${key}}" 30 | 31 | aws proton wait environment-template-version-registered \ 32 | --template-name ${template} --major-version ${version} --minor-version 0 33 | 34 | aws proton update-environment-template-version \ 35 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template creates a VPC with two public and private subnets across two availability zones. The VPC includes an Internet Gateway and a managed NAT Gateway in each public subnet as well as VPC Route Tables that allow for communication between the public and private subnets. 4 | 5 | It also deploys an ECS cluster to group your fargate tasks and services. An ECS task execution role is created to allow tasks to pull container images from an ECR private repository, and send container logs to CloudWatch Logs. 6 | 7 | Note that this template creates its own S3 bucket to be used for storing Terraform remote state. The bucket is outputted so that associated Proton service templates can use it as well. In this way, the IaC state for each environment instance along with its related services are all stored in a single bucket. 8 | 9 | 10 | ## Register Template in AWS Proton 11 | 12 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 13 | 14 | Note that you can also setup [Template Sync](https://docs.aws.amazon.com/proton/latest/userguide/ag-template-sync-configs.html) so that each commit is automatically registered in Proton. 15 | 16 | ```sh 17 | cd terraform/environment-templates/tf-vpc-ecs-cluster 18 | make template bucket=my-bucket version=1 19 | ``` 20 | 21 | ## Input Parameters 22 | 23 | ![input](./input.png) 24 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:environment" = var.environment.name 19 | } 20 | } 21 | } 22 | 23 | module "fargate_env" { 24 | source = "./src" 25 | 26 | name = var.environment.name 27 | vpc_cidr = var.environment.inputs.vpc_cidr 28 | private_subnet_one_cidr = var.environment.inputs.private_subnet_one_cidr 29 | private_subnet_two_cidr = var.environment.inputs.private_subnet_two_cidr 30 | public_subnet_one_cidr = var.environment.inputs.public_subnet_one_cidr 31 | public_subnet_two_cidr = var.environment.inputs.public_subnet_two_cidr 32 | } 33 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_name" { 2 | description = "cluster name" 3 | value = module.fargate_env.cluster_name 4 | } 5 | 6 | output "cluster_arn" { 7 | description = "cluster arn" 8 | value = module.fargate_env.cluster_arn 9 | } 10 | 11 | output "service_taskdef_execution_role" { 12 | description = "task execution role" 13 | value = module.fargate_env.service_taskdef_execution_role 14 | } 15 | 16 | output "vpc_id" { 17 | description = "vpc id" 18 | value = module.fargate_env.vpc_id 19 | } 20 | 21 | output "public_subnet_one_id" { 22 | description = "public subnet one" 23 | value = module.fargate_env.public_subnet_one_id 24 | } 25 | 26 | output "public_subnet_two_id" { 27 | description = "public subnet two" 28 | value = module.fargate_env.public_subnet_two_id 29 | } 30 | 31 | output "private_subnet_one_id" { 32 | description = "private subnet one" 33 | value = module.fargate_env.private_subnet_one_id 34 | } 35 | 36 | output "private_subnet_two_id" { 37 | description = "private subnet two" 38 | value = module.fargate_env.private_subnet_two_id 39 | } 40 | 41 | output "default_security_group_id" { 42 | description = "Default security group for VPC" 43 | value = module.fargate_env.default_security_group_id 44 | } 45 | 46 | output "tf_state_bucket" { 47 | description = "Terraform state bucket name" 48 | value = var.tf_state_bucket 49 | } 50 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/s3-bucket/README.md: -------------------------------------------------------------------------------- 1 | # s3-bucket 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | | Name | Version | 14 | |------|---------| 15 | | [aws](#provider\_aws) | ~> 4.0 | 16 | 17 | ## Modules 18 | 19 | No modules. 20 | 21 | ## Resources 22 | 23 | | Name | Type | 24 | |------|------| 25 | | [aws_s3_bucket.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | 26 | | [aws_s3_bucket_public_access_block.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | 27 | | [aws_s3_bucket_server_side_encryption_configuration.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | 28 | | [aws_s3_bucket_versioning.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | 29 | 30 | ## Inputs 31 | 32 | | Name | Description | Type | Default | Required | 33 | |------|-------------|------|---------|:--------:| 34 | | [name](#input\_name) | the bucket name | `string` | n/a | yes | 35 | 36 | ## Outputs 37 | 38 | | Name | Description | 39 | |------|-------------| 40 | | [s3\_bucket](#output\_s3\_bucket) | the s3 bucket that was created | 41 | 42 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/s3-bucket/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | resource "aws_s3_bucket" "main" { 13 | bucket = var.name 14 | force_destroy = false 15 | } 16 | 17 | resource "aws_s3_bucket_versioning" "main" { 18 | bucket = aws_s3_bucket.main.id 19 | versioning_configuration { 20 | status = "Enabled" 21 | } 22 | } 23 | 24 | resource "aws_s3_bucket_server_side_encryption_configuration" "main" { 25 | bucket = aws_s3_bucket.main.id 26 | rule { 27 | apply_server_side_encryption_by_default { 28 | sse_algorithm = "AES256" 29 | } 30 | } 31 | } 32 | 33 | resource "aws_s3_bucket_public_access_block" "main" { 34 | bucket = aws_s3_bucket.main.id 35 | block_public_acls = true 36 | block_public_policy = true 37 | ignore_public_acls = true 38 | restrict_public_buckets = true 39 | } 40 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/s3-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket" { 2 | description = "the s3 bucket that was created" 3 | value = aws_s3_bucket.main.bucket 4 | } 5 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/s3-bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "the bucket name" 3 | type = string 4 | } 5 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/src/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_availability_zones" "available" { 2 | state = "available" 3 | } 4 | 5 | module "vpc" { 6 | source = "terraform-aws-modules/vpc/aws" 7 | 8 | name = var.name 9 | cidr = var.vpc_cidr 10 | azs = [data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1]] 11 | private_subnets = [var.private_subnet_one_cidr, var.private_subnet_two_cidr] 12 | public_subnets = [var.public_subnet_one_cidr, var.public_subnet_two_cidr] 13 | enable_nat_gateway = true 14 | enable_vpn_gateway = true 15 | enable_dns_hostnames = true 16 | enable_dns_support = true 17 | } 18 | 19 | resource "aws_ecs_cluster" "main" { 20 | name = var.name 21 | 22 | setting { 23 | name = "containerInsights" 24 | value = "enabled" 25 | } 26 | } 27 | 28 | resource "aws_ecs_cluster_capacity_providers" "main" { 29 | cluster_name = aws_ecs_cluster.main.name 30 | capacity_providers = ["FARGATE"] 31 | 32 | default_capacity_provider_strategy { 33 | capacity_provider = "FARGATE" 34 | } 35 | } 36 | 37 | resource "aws_iam_role" "main" { 38 | name = var.name 39 | assume_role_policy = data.aws_iam_policy_document.ecs_task_execution_role_policy.json 40 | } 41 | 42 | data "aws_iam_policy_document" "ecs_task_execution_role_policy" { 43 | statement { 44 | actions = ["sts:AssumeRole"] 45 | effect = "Allow" 46 | 47 | principals { 48 | type = "Service" 49 | identifiers = ["ecs-tasks.amazonaws.com"] 50 | } 51 | } 52 | } 53 | 54 | resource "aws_iam_role_policy_attachment" "main" { 55 | role = aws_iam_role.main.name 56 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 57 | } 58 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_name" { 2 | description = "cluster name" 3 | value = aws_ecs_cluster.main.name 4 | } 5 | 6 | output "cluster_arn" { 7 | description = "cluster arn" 8 | value = aws_ecs_cluster.main.arn 9 | } 10 | 11 | output "service_taskdef_execution_role" { 12 | description = "task execution role" 13 | value = aws_iam_role.main.arn 14 | } 15 | 16 | output "vpc_id" { 17 | description = "vpc id" 18 | value = module.vpc.vpc_id 19 | } 20 | 21 | output "public_subnet_one_id" { 22 | description = "public subnet one" 23 | value = module.vpc.public_subnets[0] 24 | } 25 | 26 | output "public_subnet_two_id" { 27 | description = "public subnet two" 28 | value = module.vpc.public_subnets[1] 29 | } 30 | 31 | output "private_subnet_one_id" { 32 | description = "private subnet one" 33 | value = module.vpc.private_subnets[0] 34 | } 35 | 36 | output "private_subnet_two_id" { 37 | description = "private subnet two" 38 | value = module.vpc.private_subnets[1] 39 | } 40 | 41 | output "default_security_group_id" { 42 | description = "Default security group for VPC" 43 | value = module.vpc.default_security_group_id 44 | } 45 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "The name of this environment" 3 | type = string 4 | } 5 | 6 | variable "vpc_cidr" { 7 | description = "The CIDR range for the VPC" 8 | type = string 9 | default = "10.0.0.0/16" 10 | } 11 | 12 | variable "private_subnet_one_cidr" { 13 | description = "The CIDR range for private subnet one" 14 | type = string 15 | default = "10.0.128.0/18" 16 | } 17 | 18 | variable "private_subnet_two_cidr" { 19 | description = "The CIDR range for private subnet two" 20 | type = string 21 | default = "10.0.192.0/18" 22 | } 23 | 24 | variable "public_subnet_one_cidr" { 25 | description = "The CIDR range for public subnet one" 26 | type = string 27 | default = "10.0.0.0/18" 28 | } 29 | 30 | variable "public_subnet_two_cidr" { 31 | description = "The CIDR range for public subnet two" 32 | type = string 33 | default = "10.0.64.0/18" 34 | } 35 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | variable "environment" { 3 | description = "The Proton Environment" 4 | type = object({ 5 | name = string 6 | inputs = map(string) 7 | }) 8 | default = null 9 | } 10 | 11 | variable "region" { 12 | description = "aws region" 13 | type = string 14 | default = "us-east-1" 15 | } 16 | 17 | variable "tf_state_bucket" { 18 | description = "Terraform state bucket name. This is merely a passthrough so we can conveniently output it back to proton" 19 | type = string 20 | } 21 | -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/environment-templates/tf-vpc-ecs-cluster/v1/input.png -------------------------------------------------------------------------------- /terraform/environment-templates/tf-vpc-ecs-cluster/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | environment_input_type: "PublicEnvironmentInput" 5 | types: 6 | PublicEnvironmentInput: 7 | type: object 8 | description: "Input properties for my environment" 9 | properties: 10 | vpc_cidr: 11 | title: VPC CIDR 12 | type: string 13 | description: "The CIDR range for your VPC" 14 | default: 10.0.0.0/16 15 | pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|18|24)) 16 | public_subnet_one_cidr: 17 | title: Public Subnet 1 CIDR 18 | type: string 19 | description: "The CIDR range for public subnet one" 20 | default: 10.0.0.0/18 21 | pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|18|24)) 22 | public_subnet_two_cidr: 23 | title: Public Subnet 2 CIDR 24 | type: string 25 | description: "The CIDR range for public subnet two" 26 | default: 10.0.64.0/18 27 | pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|18|24)) 28 | private_subnet_one_cidr: 29 | title: Private Subnet 1 CIDR 30 | type: string 31 | description: "The CIDR range for private subnet one" 32 | default: 10.0.128.0/18 33 | pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|18|24)) 34 | private_subnet_two_cidr: 35 | title: Private Subnet 2 CIDR 36 | type: string 37 | description: "The CIDR range for private subnet two" 38 | default: 10.0.192.0/18 39 | pattern: ([0-9]{1,3}\.){3}[0-9]{1,3}($|/(16|18|24)) 40 | -------------------------------------------------------------------------------- /terraform/service-templates/README.md: -------------------------------------------------------------------------------- 1 | ## Example Terraform Proton Service Templates 2 | 3 | - [ECS Fargate Web Service](./tf-ecs-fargate-lb-service/v1/README.md) - provisions a stack for running HTTP applications, which includes an ALB, an ECS Fargate service, and ancillary services 4 | 5 | - [ECS Fargate Web Service with CodePipeline](./tf-ecs-fargate-lb-service-cicd-codepipeline/v1/README.md) - same as [ECS Fargate Web Service](./tf-ecs-fargate-lb-service/v1/README.md) but adds a CI/CD pipeline implemented with AWS CodePipeline 6 | 7 | - [ECS Fargate Web Service with GitHub Actions](./tf-ecs-fargate-lb-service-cicd-gh-actions/v1/README.md) - same as [ECS Fargate Web Service](./tf-ecs-fargate-lb-service/v1/README.md) but adds a CI/CD pipeline implemented with GitHub Actions 8 | 9 | - [Lambda Web Service](./tf-lambda-apigw-service/v1/README.md) - provisions a web API stack using API Gateway HTTP API and Lambda 10 | 11 | - [Lambda Web Service with CodePipeline](./tf-lambda-apigw-service-cicd-codepipeline/v1/README.md) - same as [Lambda Web Service](./tf-lambda-apigw-service/v1/README.md) but adds a CI/CD pipeline implemented with AWS CodePipeline 12 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environment: 2 | - tf-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-ecs-fargate-load-balanced-service-cicd-codepipeline 2 | key := proton-service-template.tar.gz 3 | description := "Load Balanced ECS Fargate Service with a CodePipeline CI/CD Pipeline" 4 | 5 | all: help 6 | 7 | .PHONY: help 8 | help: Makefile 9 | @echo 10 | @echo " Choose a make command to run" 11 | @echo 12 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 13 | @echo 14 | 15 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 16 | .PHONY: template 17 | template: 18 | aws proton create-service-template \ 19 | --name ${template} \ 20 | --display-name ${description} \ 21 | --description ${description} 22 | 23 | tar -zcvf ${key} schema instance_infrastructure pipeline_infrastructure 24 | aws s3 cp ${key} s3://${bucket}/${key} 25 | rm ${key} 26 | 27 | aws proton create-service-template-version \ 28 | --template-name ${template} \ 29 | --description "registered from CLI" \ 30 | --compatible-environment-templates="majorVersion=1,templateName=tf-vpc-ecs-cluster" \ 31 | --source s3="{bucket=${bucket},key=${key}}" 32 | 33 | aws proton wait service-template-version-registered \ 34 | --template-name ${template} --major-version ${version} --minor-version 0 35 | 36 | aws proton update-service-template-version \ 37 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED 38 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template is the same as the [tf-ecs-fargate-lb-service](../../tf-ecs-fargate-lb-service/) but adds infrastructure to enable CI/CD using AWS CodePipeline. The template outputs the following resources: 4 | 5 | - Private ECR repository 6 | - CodeBuild Project 7 | - CodePipeline 8 | 9 | The idea with this template is that once you deploy it, it will setup a CodePipeline that will automatically: 10 | 11 | - build your code into a container image (requires a Dockerfile) 12 | - push your image to ECR 13 | - optionally deploy the new image to whichever service instances you've configured 14 | 15 | This template is compatible with the [tf-vpc-ecs-cluster](../../environment-templates/tf-vpc-ecs-cluster) template. 16 | 17 | 18 | ## Register Template in AWS Proton 19 | 20 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 21 | 22 | ```sh 23 | cd terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline 24 | make template bucket=my-bucket version=1 25 | ``` 26 | 27 | ### Input Parameters 28 | 29 | ![input](./input.png) 30 | ![pipeline](./pipeline.png) 31 | 32 | ### Demo 33 | 34 | Here's a quick demo of what this looks like. 35 | 36 | ![gif](./input.gif) 37 | 38 | 39 | ## Security 40 | 41 | See [CONTRIBUTING](../../CONTRIBUTING.md#security-issue-notifications) for more information. 42 | 43 | ## License 44 | 45 | This library is licensed under the MIT-0 License. See the [LICENSE](../../LICENSE) file. 46 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/input.gif -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/input.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [load\_balanced\_fargate\_svc](#module\_load\_balanced\_fargate\_svc) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [environment](#input\_environment) | proton environment |
object({
name = string
account_id = string
outputs = map(string)
})
| n/a | yes | 30 | | [region](#input\_region) | aws region | `string` | `"us-east-1"` | no | 31 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 32 | | [service\_instance](#input\_service\_instance) | proton service instance |
object({
name = string
inputs = map(string)
})
| n/a | yes | 33 | 34 | ## Outputs 35 | 36 | | Name | Description | 37 | |------|-------------| 38 | | [endpoint](#output\_endpoint) | the provisioned endpoint | 39 | 40 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:environment" = var.environment.name, 19 | "proton:service" = var.service.name, 20 | "proton:service_instance" = var.service_instance.name, 21 | } 22 | } 23 | } 24 | 25 | module "load_balanced_fargate_svc" { 26 | source = "./src" 27 | 28 | # name based on proton input 29 | name = "${var.environment.name}-${var.service.name}-${var.service_instance.name}" 30 | 31 | # environment output 32 | vpc_id = var.environment.outputs.vpc_id 33 | public_subnet_one_id = var.environment.outputs.public_subnet_one_id 34 | public_subnet_two_id = var.environment.outputs.public_subnet_two_id 35 | private_subnet_one_id = var.environment.outputs.private_subnet_one_id 36 | private_subnet_two_id = var.environment.outputs.private_subnet_two_id 37 | cluster_name = var.environment.outputs.cluster_name 38 | task_execution_role = var.environment.outputs.service_taskdef_execution_role 39 | 40 | # service input 41 | container_port = var.service_instance.inputs.port 42 | task_size = var.service_instance.inputs.task_size 43 | image = var.service_instance.inputs.image 44 | desired_count = var.service_instance.inputs.desired_count 45 | subnet_type = var.service_instance.inputs.subnet_type 46 | health_check_path = var.service_instance.inputs.health_check_path 47 | } 48 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | project_properties: 9 | # this setting will throw an error in case the account's 10 | # CodeBuild "Concurrently running builds" quota is too low 11 | ConcurrentBuildLimit: 2 12 | env: 13 | variables: 14 | TF_VERSION: 1.4.2 15 | 16 | provision: 17 | # build tf state based on proton input 18 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 19 | 20 | # install terraform cli 21 | - echo "Installing Terraform CLI ${TF_VERSION}" 22 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 23 | 24 | # provision vpc/ecs resources, storing state in the environment s3 bucket 25 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 26 | - terraform apply -var-file=proton-inputs.json -auto-approve 27 | 28 | # pass terraform output to proton 29 | - chmod +x ./output.sh && ./output.sh 30 | 31 | deprovision: 32 | # build tf state based on proton input 33 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 34 | 35 | # install terraform cli 36 | - echo "Installing Terraform CLI ${TF_VERSION}" 37 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 38 | 39 | # destroy environment 40 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 41 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 42 | - terraform destroy -var-file=proton-inputs.json -auto-approve 43 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = module.load_balanced_fargate_svc.endpoint 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/src/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" {} 2 | 3 | data "aws_caller_identity" "current" {} 4 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | name_truncated = substr(var.name, 0, 29) 4 | lb_port = 80 5 | lb_protocol = "HTTP" 6 | } 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = "http://${aws_alb.main.dns_name}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/tf-state.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | export IN=$(cat proton-inputs.json) && echo ${IN} 4 | export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 5 | export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 6 | export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 7 | export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 8 | export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 9 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/instance_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "environment" { 4 | description = "proton environment" 5 | type = object({ 6 | name = string 7 | account_id = string 8 | outputs = map(string) 9 | }) 10 | } 11 | 12 | variable "service" { 13 | description = "proton service" 14 | type = object({ 15 | name = string 16 | repository_id = string 17 | repository_connection_arn = string 18 | branch_name = string 19 | }) 20 | } 21 | 22 | variable "service_instance" { 23 | description = "proton service instance" 24 | type = object({ 25 | name = string 26 | inputs = map(string) 27 | }) 28 | } 29 | 30 | variable "region" { 31 | description = "aws region" 32 | type = string 33 | default = "us-east-1" 34 | } 35 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [cicd\_pipeline](#module\_cicd\_pipeline) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [pipeline](#input\_pipeline) | the proton pipeline |
object({
inputs = any
})
| n/a | yes | 30 | | [region](#input\_region) | aws region | `string` | `"us-east-1"` | no | 31 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 32 | | [service\_instances](#input\_service\_instances) | the service instances |
list(
object({
name = string
inputs = any
outputs = any
environment = object({
account_id = string
name = string
outputs = any
})
})
)
| n/a | yes | 33 | 34 | ## Outputs 35 | 36 | | Name | Description | 37 | |------|-------------| 38 | | [PipelineEndpoint](#output\_PipelineEndpoint) | A link to the generated CodePipeline | 39 | 40 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:service" = var.service.name, 19 | "proton:pipeline" = var.service.name 20 | } 21 | } 22 | } 23 | 24 | module "cicd_pipeline" { 25 | source = "./src" 26 | 27 | # name based on proton input 28 | name = var.service.name 29 | 30 | # service input 31 | service_name = var.service.name 32 | repository_connection_arn = var.service.repository_connection_arn 33 | repository_id = var.service.repository_id 34 | branch_name = var.service.branch_name 35 | 36 | # generated pipeline should only deploy to instances specified by user 37 | service_instances = tolist([for i in var.service_instances : i 38 | if contains(var.pipeline.inputs.instances_to_deploy, i.name)]) 39 | 40 | # pipeline input 41 | service_source_dir = var.pipeline.inputs.service_dir 42 | dockerfile = var.pipeline.inputs.dockerfile 43 | unit_test_command = var.pipeline.inputs.unit_test_command 44 | } 45 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | project_properties: 9 | # this setting will throw an error in case the account's 10 | # CodeBuild "Concurrently running builds" quota is too low 11 | ConcurrentBuildLimit: 2 12 | env: 13 | variables: 14 | TF_VERSION: 1.4.2 15 | 16 | provision: 17 | # build tf state based on proton input 18 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 19 | 20 | # install terraform cli 21 | - echo "Installing Terraform CLI ${TF_VERSION}" 22 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 23 | 24 | # provision, storing state in an s3 bucket 25 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 26 | - terraform apply -var-file=proton-inputs.json -auto-approve 27 | 28 | # pass terraform output to proton 29 | - chmod +x ./output.sh && ./output.sh 30 | 31 | deprovision: 32 | # build tf state based on proton input 33 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 34 | 35 | # install terraform cli 36 | - echo "Installing Terraform CLI ${TF_VERSION}" 37 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 38 | 39 | # provision, storing state in an s3 bucket 40 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 41 | - terraform destroy -var-file=proton-inputs.json -auto-approve 42 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | # note that proton currently requires the `PipelineEndpoint` 2 | # output in order to work with the pipeline run console 3 | output "PipelineEndpoint" { 4 | description = "A link to the generated CodePipeline" 5 | value = module.cicd_pipeline.pipeline_endpoint 6 | } 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | region = data.aws_region.current.id 4 | } -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "pipeline_endpoint" { 2 | description = "A link to the generated CodePipeline" 3 | value = "https://${local.region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${aws_codepipeline.pipeline.id}/view?region=${local.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "namespace used for all resources" 3 | type = string 4 | } 5 | 6 | variable "service_name" { 7 | description = "name of the associated service" 8 | type = string 9 | } 10 | 11 | variable "service_source_dir" { 12 | description = "Source directory for the service" 13 | type = string 14 | } 15 | 16 | variable "unit_test_command" { 17 | description = "the command to run for unit tests" 18 | type = string 19 | } 20 | 21 | variable "dockerfile" { 22 | description = "the location of the Dockerfile" 23 | type = string 24 | } 25 | 26 | variable "repository_connection_arn" { 27 | description = "repository connection arn" 28 | type = string 29 | } 30 | 31 | variable "repository_id" { 32 | description = "repository_id" 33 | type = string 34 | } 35 | 36 | variable "branch_name" { 37 | description = "the name of the git branch" 38 | type = string 39 | } 40 | 41 | variable "service_instances" { 42 | description = "list of service instances to deploy to. typically just one" 43 | type = list( 44 | object({ 45 | name = string 46 | inputs = any 47 | outputs = any 48 | environment = object({ 49 | account_id = string 50 | name = string 51 | outputs = any 52 | }) 53 | }) 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/tf-state.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export IN=$(cat proton-inputs.json) && echo ${IN} 3 | export TF_STATE_BUCKET=$(echo $IN | jq '.service_instances[0].environment.outputs.tf_state_bucket' -r) 4 | export PROTON_SVC=$(echo ${IN} | jq '.service.name' -r) 5 | export KEY=${PROTON_SVC}.pipeline.tfstate 6 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-codepipeline/v1/pipeline_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "pipeline" { 4 | description = "the proton pipeline" 5 | type = object({ 6 | inputs = any 7 | }) 8 | } 9 | 10 | variable "service" { 11 | description = "proton service" 12 | type = object({ 13 | name = string 14 | repository_id = string 15 | repository_connection_arn = string 16 | branch_name = string 17 | }) 18 | } 19 | 20 | variable "service_instances" { 21 | description = "the service instances" 22 | type = list( 23 | object({ 24 | name = string 25 | inputs = any 26 | outputs = any 27 | environment = object({ 28 | account_id = string 29 | name = string 30 | outputs = any 31 | }) 32 | }) 33 | ) 34 | } 35 | 36 | variable "region" { 37 | description = "aws region" 38 | type = string 39 | default = "us-east-1" 40 | } 41 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - tf-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-ecs-fargate-lb-service-cicd-gh-actions 2 | key := proton-service-template.tar.gz 3 | description := "Load Balanced ECS Fargate Service with a GitHub Actions CI/CD Pipeline" 4 | 5 | all: help 6 | 7 | .PHONY: help 8 | help: Makefile 9 | @echo 10 | @echo " Choose a make command to run" 11 | @echo 12 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 13 | @echo 14 | 15 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 16 | .PHONY: template 17 | template: 18 | aws proton create-service-template \ 19 | --name ${template} \ 20 | --display-name ${description} \ 21 | --description ${description} 22 | 23 | tar -zcvf ${key} schema instance_infrastructure pipeline_infrastructure 24 | aws s3 cp ${key} s3://${bucket}/${key} 25 | rm ${key} 26 | 27 | aws proton create-service-template-version \ 28 | --template-name ${template} \ 29 | --description "registered from CLI" \ 30 | --compatible-environment-templates="majorVersion=1,templateName=tf-vpc-ecs-cluster" \ 31 | --source s3="{bucket=${bucket},key=${key}}" 32 | 33 | aws proton wait service-template-version-registered \ 34 | --template-name ${template} --major-version ${version} --minor-version 0 35 | 36 | aws proton update-service-template-version \ 37 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED 38 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template is the same as the [tf-ecs-fargate-lb-service](../../tf-ecs-fargate-lb-service/) but adds infrastructure to enable CI/CD. The template outputs the following resources: 4 | 5 | - Private ECR repository 6 | - AWS Identity Provider and IAM Role that GitHub Actions can assume with access to ECR, Proton, and S3 7 | - Generated GitHub Actions workflow file (`.github/workflows.deploy.yml`) 8 | - a GitHub pull request with the generated workflow 9 | 10 | The idea with this template is that once you deploy it, your app repo will get a PR with a CI/CD pipeline that, when merged, will: 11 | 12 | - build your code into a container image (requies a Dockerfile) 13 | - push your image to ECR 14 | - update your Proton service to deploy the image to the ECS Fargate service instances 15 | 16 | 17 | This template is compatible with the [tf-vpc-ecs-cluster](../../environment-templates/tf-vpc-ecs-cluster/README.md) template. 18 | 19 | 20 | ## Register Template in AWS Proton 21 | 22 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 23 | 24 | ```sh 25 | cd terraform/service-templates/tf-ecs-fargate-lb-service-cicd-github-actions 26 | make template bucket=my-bucket version=1 27 | ``` 28 | 29 | ### Input Parameters 30 | 31 | ![input](./input.png) 32 | 33 | 34 | ## Security 35 | 36 | See [CONTRIBUTING](../../CONTRIBUTING.md#security-issue-notifications) for more information. 37 | 38 | ## License 39 | 40 | This library is licensed under the MIT-0 License. See the [LICENSE](../../LICENSE) file. 41 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/input.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [load\_balanced\_fargate\_svc](#module\_load\_balanced\_fargate\_svc) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [environment](#input\_environment) | proton environment |
object({
name = string
account_id = string
outputs = map(string)
})
| n/a | yes | 30 | | [region](#input\_region) | aws region | `string` | `"us-east-1"` | no | 31 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 32 | | [service\_instance](#input\_service\_instance) | proton service instance |
object({
name = string
inputs = map(string)
})
| n/a | yes | 33 | 34 | ## Outputs 35 | 36 | | Name | Description | 37 | |------|-------------| 38 | | [endpoint](#output\_endpoint) | the provisioned endpoint | 39 | 40 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:environment" = var.environment.name, 19 | "proton:service" = var.service.name, 20 | "proton:service_instance" = var.service_instance.name, 21 | } 22 | } 23 | } 24 | 25 | module "load_balanced_fargate_svc" { 26 | source = "./src" 27 | 28 | # name based on proton input 29 | name = "${var.environment.name}-${var.service.name}-${var.service_instance.name}" 30 | 31 | # environment output 32 | vpc_id = var.environment.outputs.vpc_id 33 | public_subnet_one_id = var.environment.outputs.public_subnet_one_id 34 | public_subnet_two_id = var.environment.outputs.public_subnet_two_id 35 | private_subnet_one_id = var.environment.outputs.private_subnet_one_id 36 | private_subnet_two_id = var.environment.outputs.private_subnet_two_id 37 | cluster_name = var.environment.outputs.cluster_name 38 | task_execution_role = var.environment.outputs.service_taskdef_execution_role 39 | 40 | # service input 41 | container_port = var.service_instance.inputs.port 42 | task_size = var.service_instance.inputs.task_size 43 | image = var.service_instance.inputs.image 44 | desired_count = var.service_instance.inputs.desired_count 45 | subnet_type = var.service_instance.inputs.subnet_type 46 | health_check_path = var.service_instance.inputs.health_check_path 47 | } 48 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | env: 9 | variables: 10 | TF_VERSION: 1.4.2 11 | 12 | provision: 13 | # build tf state based on proton input 14 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 15 | 16 | # install terraform cli 17 | - echo "Installing Terraform CLI ${TF_VERSION}" 18 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 19 | 20 | # provision vpc/ecs resources, storing state in the environment s3 bucket 21 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 22 | - terraform apply -var-file=proton-inputs.json -auto-approve 23 | 24 | # pass terraform output to proton 25 | - chmod +x ./output.sh && ./output.sh 26 | 27 | deprovision: 28 | # build tf state based on proton input 29 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 30 | 31 | # install terraform cli 32 | - echo "Installing Terraform CLI ${TF_VERSION}" 33 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 34 | 35 | # destroy environment 36 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 37 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 38 | - terraform destroy -var-file=proton-inputs.json -auto-approve 39 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --status IN_PROGRESS --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = module.load_balanced_fargate_svc.endpoint 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/src/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" {} 2 | 3 | data "aws_caller_identity" "current" {} 4 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | name_truncated = substr(var.name, 0, 29) 4 | lb_port = 80 5 | lb_protocol = "HTTP" 6 | } 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = "http://${aws_alb.main.dns_name}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/tf-state.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | export IN=$(cat proton-inputs.json) && echo ${IN} 4 | export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 5 | export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 6 | export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 7 | export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 8 | export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 9 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/instance_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "environment" { 4 | description = "proton environment" 5 | type = object({ 6 | name = string 7 | account_id = string 8 | outputs = map(string) 9 | }) 10 | } 11 | 12 | variable "service" { 13 | description = "proton service" 14 | type = object({ 15 | name = string 16 | repository_id = string 17 | repository_connection_arn = string 18 | branch_name = string 19 | }) 20 | } 21 | 22 | variable "service_instance" { 23 | description = "proton service instance" 24 | type = object({ 25 | name = string 26 | inputs = map(string) 27 | }) 28 | } 29 | 30 | variable "region" { 31 | description = "aws region" 32 | type = string 33 | default = "us-east-1" 34 | } 35 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [cicd\_pipeline](#module\_cicd\_pipeline) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [pipeline](#input\_pipeline) | the proton pipeline |
object({
inputs = any
})
| n/a | yes | 30 | | [region](#input\_region) | aws region | `string` | `"us-east-1"` | no | 31 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 32 | | [service\_instances](#input\_service\_instances) | the service instances |
list(
object({
name = string
inputs = any
outputs = any
environment = object({
account_id = string
name = string
outputs = any
})
})
)
| `null` | no | 33 | 34 | ## Outputs 35 | 36 | No outputs. 37 | 38 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:service" = var.service.name, 19 | "proton:pipeline" = var.service.name 20 | } 21 | } 22 | } 23 | 24 | module "cicd_pipeline" { 25 | source = "./src" 26 | 27 | proton_service = var.service.name 28 | proton_service_instances = var.service_instances 29 | repository_id = var.service.repository_id 30 | branch_name = var.service.branch_name 31 | docker_path = var.pipeline.inputs.docker_path 32 | } 33 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | env: 9 | variables: 10 | TF_VERSION: 1.4.2 11 | 12 | provision: 13 | # make scripts executable 14 | - find ./scripts -type f -iname "*.sh" -exec chmod +x {} \; 15 | 16 | # install terraform cli 17 | - echo "Installing Terraform CLI ${TF_VERSION}" 18 | - ./scripts/install-terraform.sh ${TF_VERSION} 19 | 20 | # get tf remote state bucket/key 21 | - export IN=$(cat proton-inputs.json) && echo ${IN} 22 | - . ./scripts/tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 23 | 24 | # if not already exists, create github oidc provider 25 | - account=$(aws sts get-caller-identity --query Account --output text) 26 | - oidc=arn:aws:iam::${account}:oidc-provider/token.actions.githubusercontent.com 27 | - aws iam get-open-id-connect-provider --open-id-connect-provider-arn ${oidc} || ./scripts/oidc.sh 28 | 29 | # provision, storing state in an s3 bucket 30 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 31 | - terraform apply -var-file=proton-inputs.json -auto-approve 32 | 33 | # install github cli 34 | - ./scripts/install-github-cli.sh 35 | 36 | # submit a PR to the app repo with the GitHub Actions CI/CD workflow 37 | - export REPO=$(echo ${IN} | jq '.service.repository_id' -r) 38 | - export SECRET=$(echo ${IN} | jq '.pipeline.inputs.github_token_secret' -r) 39 | - ./scripts/submit-pr.sh "${REPO}" "${SECRET}" 40 | 41 | deprovision: 42 | # make scripts executable 43 | - find ./scripts -type f -iname "*.sh" -exec chmod +x {} \; 44 | 45 | # install terraform cli 46 | - echo "Installing Terraform CLI ${TF_VERSION}" 47 | - ./scripts/install-terraform.sh ${TF_VERSION} 48 | 49 | # get tf remote state bucket/key 50 | - export IN=$(cat proton-inputs.json) && echo ${IN} 51 | - . ./scripts/tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 52 | 53 | # destroy environment 54 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 55 | - terraform destroy -var-file=proton-inputs.json -auto-approve 56 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/README.md: -------------------------------------------------------------------------------- 1 | Creates a GitHub OIDC Identity Provider 2 | 3 | 4 | ## Requirements 5 | 6 | No requirements. 7 | 8 | ## Providers 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [aws](#provider\_aws) | n/a | 13 | | [tls](#provider\_tls) | n/a | 14 | 15 | ## Modules 16 | 17 | No modules. 18 | 19 | ## Resources 20 | 21 | | Name | Type | 22 | |------|------| 23 | | [aws_iam_openid_connect_provider.github](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | 24 | | [tls_certificate.github_actions_oidc_provider](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/certificate) | data source | 25 | 26 | ## Inputs 27 | 28 | No inputs. 29 | 30 | ## Outputs 31 | 32 | No outputs. 33 | 34 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/main.tf: -------------------------------------------------------------------------------- 1 | data "tls_certificate" "github_actions_oidc_provider" { 2 | url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration" 3 | } 4 | 5 | resource "aws_iam_openid_connect_provider" "github" { 6 | url = "https://token.actions.githubusercontent.com" 7 | client_id_list = ["sts.amazonaws.com"] 8 | thumbprint_list = [data.tls_certificate.github_actions_oidc_provider.certificates[0].sha1_fingerprint] 9 | } 10 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/outputs.tf -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/variables.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/oidc-identity-provider/variables.tf -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/outputs.tf -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/scripts/install-github-cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | type -p curl >/dev/null || sudo apt install curl -y 4 | curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ 5 | && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ 6 | && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ 7 | && sudo apt update \ 8 | && sudo apt install gh -y 9 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/scripts/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/scripts/oidc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd ./oidc-identity-provider 5 | terraform init 6 | terraform apply -auto-approve 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/scripts/submit-pr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | REPO=$1 5 | USER=$(echo $REPO | cut -d "/" -f 1) 6 | SECRET=$2 7 | 8 | # set gh token to value from secrets manager 9 | echo "fetching ${SECRET}" 10 | export GITHUB_TOKEN=$(aws secretsmanager get-secret-value --secret-id ${SECRET} | jq '.SecretString' -r) 11 | 12 | # clone repo 13 | GH_ENDPOINT=https://${USER}:$GITHUB_TOKEN@github.com/${REPO}.git 14 | git clone ${GH_ENDPOINT} ${REPO} 15 | cd ${REPO} 16 | 17 | # commit deploy.yml 18 | mkdir -p .github/workflows 19 | cp ../../deploy.yml ./.github/workflows/deploy.yml 20 | git checkout -b cicd 21 | git add . 22 | git config --global user.name "github-actions[bot]" 23 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 24 | git commit -m "Proton generated GitHub Actions CI/CD pipeline" 25 | 26 | # push commit to remote 27 | echo "" 28 | echo "pushing branch" 29 | git remote set-url origin ${GH_ENDPOINT} 30 | git push ${GH_ENDPOINT} 31 | git fetch origin 32 | 33 | # create pull request 34 | echo "" 35 | echo "creating PR" 36 | gh repo set-default ${REPO} 37 | pr=$(gh pr create --fill | tail -1) 38 | 39 | # output PR url 40 | aws proton notify-resource-deployment-status-change \ 41 | --resource-arn ${RESOURCE_ARN} \ 42 | --status IN_PROGRESS \ 43 | --outputs "key=workflow,valueString=${pr}" 44 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/scripts/tf-state.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | export TF_STATE_BUCKET=$(echo $IN | jq '.service_instances[0].environment.outputs.tf_state_bucket' -r) 4 | export PROTON_SVC=$(echo ${IN} | jq '.service.name' -r) 5 | export KEY=${PROTON_SVC}.pipeline.tfstate 6 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | data "aws_region" "current" {} 4 | 5 | data "aws_iam_openid_connect_provider" "current" { 6 | url = "https://token.actions.githubusercontent.com" 7 | } 8 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/deploy-template.yml: -------------------------------------------------------------------------------- 1 | ################################################# 2 | ## Generated by an AWS Proton service template 3 | ################################################# 4 | 5 | name: Deploy using Proton 6 | on: 7 | push: 8 | branches: [ "${branch_name}" ] 9 | 10 | env: 11 | AWS_REGION: ${region} 12 | ECR_REPOSITORY: ${ecr_repo} 13 | PROTON_SERVICE: ${proton_service} 14 | 15 | permissions: 16 | contents: read 17 | id-token: write 18 | 19 | jobs: 20 | deploy: 21 | name: Deploy 22 | runs-on: ubuntu-latest 23 | environment: production 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Assume AWS IAM Role 30 | uses: aws-actions/configure-aws-credentials@v1-node16 31 | with: 32 | aws-region: ${region} 33 | role-to-assume: ${role} 34 | 35 | - name: Login to Amazon ECR 36 | id: login-ecr 37 | uses: aws-actions/amazon-ecr-login@v1 38 | 39 | - name: Build, tag, and push image to Amazon ECR 40 | id: build 41 | env: 42 | ECR_REGISTRY: $${{ steps.login-ecr.outputs.registry }} 43 | IMAGE_TAG: $${{ github.sha }} 44 | run: | 45 | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG ${docker_path} 46 | docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG 47 | echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT 48 | 49 | - name: Generate new Proton service spec for deployment 50 | run: | 51 | aws proton get-service --name $${PROTON_SERVICE} | jq -r .service.spec > spec.yml 52 | 53 | # replace newly built image in yaml and deploy it 54 | wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && chmod +x /usr/local/bin/yq 55 | yq ".instances[0].spec.image = \"$${{ steps.build.outputs.image }}\"" spec.yml > $${HOME}/service.yml 56 | 57 | %{ for instance in proton_service_instances ~} 58 | - name: Deploy container image to ${instance.name} using Proton pipeline 59 | run: | 60 | aws proton update-service-instance \ 61 | --service-name $${PROTON_SERVICE} \ 62 | --name ${instance.name} \ 63 | --deployment-type CURRENT_VERSION \ 64 | --spec file://$${HOME}/service.yml 65 | 66 | aws proton wait service-instance-deployed \ 67 | --service-name $${PROTON_SERVICE} \ 68 | --name ${instance.name} 69 | 70 | %{ endfor ~} 71 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | github_org = split("/", var.repository_id)[0] 3 | github_repo = split("/", var.repository_id)[1] 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/outputs.tf -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "proton_service" { 2 | description = "the name of the proton service" 3 | type = string 4 | } 5 | 6 | variable "proton_service_instances" { 7 | description = "the service instances" 8 | type = list( 9 | object({ 10 | name = string 11 | inputs = any 12 | outputs = any 13 | environment = object({ 14 | account_id = string 15 | name = string 16 | outputs = any 17 | }) 18 | }) 19 | ) 20 | default = null 21 | } 22 | 23 | variable "repository_id" { 24 | description = "the proton pipeline connected git repo" 25 | type = string 26 | } 27 | 28 | variable "branch_name" { 29 | description = "the repo branch to deploy" 30 | type = string 31 | } 32 | 33 | variable "docker_path" { 34 | description = "Docker build context is the set of files located in the specified PATH" 35 | type = string 36 | } 37 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/pipeline_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "pipeline" { 4 | description = "the proton pipeline" 5 | type = object({ 6 | inputs = any 7 | }) 8 | } 9 | 10 | variable "service" { 11 | description = "proton service" 12 | type = object({ 13 | name = string 14 | repository_id = string 15 | repository_connection_arn = string 16 | branch_name = string 17 | }) 18 | } 19 | 20 | variable "service_instances" { 21 | description = "the service instances" 22 | type = list( 23 | object({ 24 | name = string 25 | inputs = any 26 | outputs = any 27 | environment = object({ 28 | account_id = string 29 | name = string 30 | outputs = any 31 | }) 32 | }) 33 | ) 34 | default = null 35 | } 36 | 37 | variable "region" { 38 | description = "aws region" 39 | type = string 40 | default = "us-east-1" 41 | } 42 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service-cicd-gh-actions/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | service_input_type: ServiceInputs 5 | pipeline_input_type: PipelineInputs 6 | 7 | types: 8 | ServiceInputs: 9 | type: object 10 | description: "Input properties for a loadbalanced Fargate service" 11 | properties: 12 | port: 13 | type: number 14 | title: "Container Port" 15 | description: "The port the container listens on" 16 | default: 8080 17 | minimum: 0 18 | maximum: 65535 19 | health_check_path: 20 | title: Health Check Path 21 | description: The health check path 22 | type: string 23 | default: /health 24 | desired_count: 25 | type: number 26 | title: "Desired Task Count" 27 | description: "The default number of Fargate tasks you want running" 28 | default: 1 29 | minimum: 1 30 | task_size: 31 | type: string 32 | title: "Task Size (CPU/Memory)" 33 | description: "The size of the task you want to run" 34 | enum: ["x-small", "small", "medium", "large", "x-large"] 35 | default: "x-small" 36 | subnet_type: 37 | type: string 38 | title: "Subnet location" 39 | description: "Which subnet to run tasks in" 40 | enum: ["public", "private"] 41 | default: "private" 42 | image: 43 | type: string 44 | title: "Container Image" 45 | description: "The container image to deploy" 46 | default: "public.ecr.aws/aws-containers/proton-demo-image:1e5ca1d" 47 | minLength: 1 48 | maxLength: 200 49 | 50 | PipelineInputs: 51 | type: object 52 | description: "Pipeline input properties" 53 | properties: 54 | github_token_secret: 55 | type: string 56 | title: GitHub PAT Secret Name 57 | description: "Name of Secrets Manager Secret that stores the GitHub PAT" 58 | docker_path: 59 | title: "Docker Path" 60 | description: "Docker build context is the set of files located in the specified PATH (Default is .)" 61 | type: string 62 | default: "." 63 | minLength: 1 64 | maxLength: 300 65 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - tf-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-ecs-fargate-lb-service 2 | key := proton-service-template.tar.gz 3 | 4 | all: help 5 | 6 | .PHONY: help 7 | help: Makefile 8 | @echo 9 | @echo " Choose a make command to run" 10 | @echo 11 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 12 | @echo 13 | 14 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 15 | .PHONY: template 16 | template: 17 | aws proton create-service-template \ 18 | --name ${template} \ 19 | --display-name "Load Balanced ECS Fargate Service" \ 20 | --description "Load Balanced ECS Fargate Service" \ 21 | --pipeline-provisioning CUSTOMER_MANAGED 22 | 23 | tar -zcvf ${key} schema instance_infrastructure 24 | aws s3 cp ${key} s3://${bucket}/${key} 25 | rm ${key} 26 | 27 | aws proton create-service-template-version \ 28 | --template-name ${template} \ 29 | --description "registered from CLI" \ 30 | --compatible-environment-templates="majorVersion=1,templateName=tf-vpc-ecs-cluster" \ 31 | --source s3="{bucket=${bucket},key=${key}}" 32 | 33 | aws proton wait service-template-version-registered \ 34 | --template-name ${template} --major-version ${version} --minor-version 0 35 | 36 | aws proton update-service-template-version \ 37 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED 38 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template is compatible with the [vpc-ecs-cluster](../../environment-templates/vpc-ecs-cluster/README.md) template. It creates an ECS service running on a Fargate cluster fronted by a public facing load balancer. The service can be configured to run in a public subnet with direct internet access or in a private subnet without direct internet access. The networking configuration of can be selected using the subnet_type parameter. Performanced based (CPU) horizontal auto-scaling is enabled by default. Other service properties like port number, desired task count, task size (cpu/memory units), and container image can be specified through the service input parameters. 4 | 5 | ## Register Template in AWS Proton 6 | 7 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 8 | 9 | ```sh 10 | cd terraform/service-templates/tf-ecs-fargate-load-balanced-service 11 | make template bucket=my-bucket version=1 12 | ``` 13 | 14 | ### Input Parameters 15 | 16 | ![input](./input.png) 17 | 18 | ## Security 19 | 20 | See [CONTRIBUTING](../../CONTRIBUTING.md#security-issue-notifications) for more information. 21 | 22 | ## License 23 | 24 | This library is licensed under the MIT-0 License. See the [LICENSE](../../LICENSE) file. 25 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-ecs-fargate-lb-service/v1/input.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 4.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [load\_balanced\_fargate\_svc](#module\_load\_balanced\_fargate\_svc) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [environment](#input\_environment) | proton environment |
object({
name = string
account_id = string
outputs = map(string)
})
| n/a | yes | 30 | | [region](#input\_region) | aws region | `string` | `"us-east-1"` | no | 31 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 32 | | [service\_instance](#input\_service\_instance) | proton service instance |
object({
name = string
inputs = map(string)
})
| n/a | yes | 33 | 34 | ## Outputs 35 | 36 | | Name | Description | 37 | |------|-------------| 38 | | [endpoint](#output\_endpoint) | the provisioned endpoint | 39 | 40 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | default_tags { 17 | tags = { 18 | "proton:environment" = var.environment.name, 19 | "proton:service" = var.service.name, 20 | "proton:service_instance" = var.service_instance.name, 21 | } 22 | } 23 | } 24 | 25 | module "load_balanced_fargate_svc" { 26 | source = "./src" 27 | 28 | # name based on proton input 29 | name = "${var.environment.name}-${var.service.name}-${var.service_instance.name}" 30 | 31 | # environment output 32 | vpc_id = var.environment.outputs.vpc_id 33 | public_subnet_one_id = var.environment.outputs.public_subnet_one_id 34 | public_subnet_two_id = var.environment.outputs.public_subnet_two_id 35 | private_subnet_one_id = var.environment.outputs.private_subnet_one_id 36 | private_subnet_two_id = var.environment.outputs.private_subnet_two_id 37 | cluster_name = var.environment.outputs.cluster_name 38 | task_execution_role = var.environment.outputs.service_taskdef_execution_role 39 | 40 | # service input 41 | container_port = var.service_instance.inputs.port 42 | task_size = var.service_instance.inputs.task_size 43 | image = var.service_instance.inputs.image 44 | desired_count = var.service_instance.inputs.desired_count 45 | subnet_type = var.service_instance.inputs.subnet_type 46 | health_check_path = var.service_instance.inputs.health_check_path 47 | } 48 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | env: 9 | variables: 10 | TF_VERSION: 1.3.9 11 | 12 | provision: 13 | # get proton input parameters 14 | - export IN=$(cat proton-inputs.json) && echo ${IN} 15 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 16 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 17 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 18 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 19 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 20 | 21 | # install terraform cli 22 | - echo "Installing Terraform CLI ${TF_VERSION}" 23 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 24 | 25 | # provision vpc/ecs resources, storing state in the environment s3 bucket 26 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 27 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 28 | - terraform apply -var-file=proton-inputs.json -auto-approve 29 | 30 | # pass terraform output to proton 31 | - chmod +x ./output.sh && ./output.sh 32 | 33 | deprovision: 34 | # get proton input parameters 35 | - export IN=$(cat proton-inputs.json) && echo ${IN} 36 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 37 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 38 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 39 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 40 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 41 | 42 | # install terraform cli 43 | - echo "Installing Terraform CLI ${TF_VERSION}" 44 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 45 | 46 | # destroy environment 47 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 48 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 49 | - terraform destroy -var-file=proton-inputs.json -auto-approve 50 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = module.load_balanced_fargate_svc.endpoint 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/src/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" {} 2 | 3 | data "aws_caller_identity" "current" {} 4 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | name_truncated = replace(substr(var.name, 0, 29), "-", "") 4 | lb_port = 80 5 | lb_protocol = "HTTP" 6 | } 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "the provisioned endpoint" 3 | value = "http://${aws_alb.main.dns_name}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/instance_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "environment" { 4 | description = "proton environment" 5 | type = object({ 6 | name = string 7 | account_id = string 8 | outputs = map(string) 9 | }) 10 | } 11 | 12 | variable "service" { 13 | description = "proton service" 14 | type = object({ 15 | name = string 16 | repository_id = string 17 | repository_connection_arn = string 18 | branch_name = string 19 | }) 20 | } 21 | 22 | variable "service_instance" { 23 | description = "proton service instance" 24 | type = object({ 25 | name = string 26 | inputs = map(string) 27 | }) 28 | } 29 | 30 | variable "region" { 31 | description = "aws region" 32 | type = string 33 | default = "us-east-1" 34 | } 35 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-ecs-fargate-lb-service/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | service_input_type: "LoadBalancedFargateServiceInput" 5 | types: 6 | LoadBalancedFargateServiceInput: 7 | type: object 8 | description: "Input properties for a loadbalanced Fargate service" 9 | properties: 10 | port: 11 | type: number 12 | title: "Container Port" 13 | description: "The port the container listens on" 14 | default: 8080 15 | minimum: 0 16 | maximum: 65535 17 | health_check_path: 18 | title: Health Check Path 19 | description: The health check path 20 | type: string 21 | default: /health 22 | desired_count: 23 | type: number 24 | title: "Desired Task Count" 25 | description: "The default number of Fargate tasks you want running" 26 | default: 1 27 | minimum: 1 28 | task_size: 29 | type: string 30 | title: "Task Size (CPU/Memory)" 31 | description: "The size of the task you want to run" 32 | enum: ["x-small", "small", "medium", "large", "x-large"] 33 | default: "x-small" 34 | subnet_type: 35 | type: string 36 | title: "Subnet location" 37 | description: "Which subnet to run tasks in" 38 | enum: ["public", "private"] 39 | default: "private" 40 | image: 41 | type: string 42 | title: "Container Image" 43 | description: "The container image to deploy" 44 | default: "public.ecr.aws/aws-containers/proton-demo-image:1e5ca1d" 45 | minLength: 1 46 | maxLength: 200 47 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - tf-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-lambda-apigw-service-cicd-codepipeline 2 | key := proton-service-template.tar.gz 3 | display := "Lambda Web Service with CodePipeline" 4 | description := "Lambda API Gateway HTTP API with a CodePipeline CI/CD Pipeline" 5 | 6 | all: help 7 | 8 | .PHONY: help 9 | help: Makefile 10 | @echo 11 | @echo " Choose a make command to run" 12 | @echo 13 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 14 | @echo 15 | 16 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 17 | .PHONY: template 18 | template: 19 | aws proton create-service-template \ 20 | --name ${template} \ 21 | --display-name ${display} \ 22 | --description ${description} 23 | 24 | tar -zcvf ${key} schema instance_infrastructure pipeline_infrastructure 25 | aws s3 cp ${key} s3://${bucket}/${key} 26 | rm ${key} 27 | 28 | aws proton create-service-template-version \ 29 | --template-name ${template} \ 30 | --description "registered from CLI" \ 31 | --compatible-environment-templates="majorVersion=1,templateName=tf-vpc-ecs-cluster" \ 32 | --source s3="{bucket=${bucket},key=${key}}" 33 | 34 | aws proton wait service-template-version-registered \ 35 | --template-name ${template} --major-version ${version} --minor-version 0 36 | 37 | aws proton update-service-template-version \ 38 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED 39 | 40 | ## delete: deletes proton template 41 | .PHONY: delete 42 | delete: 43 | aws proton list-service-template-versions \ 44 | --template-name ${template} | jq '.templateVersions[] | {majorVersion, minorVersion}' | jq '"--major-version " + .majorVersion + " --minor-version " + .minorVersion' -r | xargs -n 4 aws proton delete-service-template-version --template-name ${template} $1 45 | aws proton delete-service-template --name ${template} 46 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template creates a web API stack using API Gateway HTTP API and Lambda. It's compatible with the [vpc-ecs-cluster](../../environment-templates/vpc-ecs-cluster/README.md) template. 4 | 5 | By default, the lambda function will not be connected to a VPC. If you want the lambda to be able to access other resources inside a VPC, you can set the `VPC Access` input property to `true` and then select the `subnet type`. The actual subnet and security group values will come from the associated proton environment. 6 | 7 | The template will deploy a default function (written in node.js) regardless of what you set `lambda_runtime` and `lambda_handler` to. To deploy your app, upload your deployment package zip file to an S3 bucket, and update the service by specifying the `s3_bucket` and `s3_key` input fields. 8 | 9 | 10 | ## Register Template in AWS Proton 11 | 12 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 13 | 14 | ```sh 15 | cd terraform/service-templates/tf-lambda-apigw-service/v1 16 | make template bucket=my-bucket version=1 17 | ``` 18 | 19 | ### Input Parameters 20 | 21 | ![input](./input.png) 22 | 23 | ## Security 24 | 25 | See [CONTRIBUTING](../../CONTRIBUTING.md#security-issue-notifications) for more information. 26 | 27 | ## License 28 | 29 | This library is licensed under the MIT-0 License. See the [LICENSE](../../LICENSE) file. 30 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/input.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version 12 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | default_tags { 16 | tags = { 17 | "proton:environment" = var.environment.name 18 | "proton:service" = var.service.name, 19 | "proton:service_instance" = var.service_instance.name, 20 | } 21 | } 22 | } 23 | 24 | 25 | module "tf_lambda_apigw_service" { 26 | source = "./src" 27 | 28 | name = "${var.service.name}-${var.service_instance.name}" 29 | lambda_handler = var.service_instance.inputs.lambda_handler 30 | lambda_runtime = var.service_instance.inputs.lambda_runtime 31 | memory_size = var.service_instance.inputs.memory_size 32 | s3_bucket = var.service_instance.inputs.s3_bucket 33 | s3_key = var.service_instance.inputs.s3_key 34 | timeout = var.service_instance.inputs.timeout 35 | vpc_access = var.service_instance.inputs.vpc_access 36 | 37 | # get vpc config from environment output 38 | vpc_security_group_ids = [var.environment.outputs.default_security_group_id] 39 | vpc_subnet_ids = (var.service_instance.inputs.subnet_type == "private" ? 40 | [var.environment.outputs.private_subnet_one_id, var.environment.outputs.private_subnet_two_id] : 41 | [var.environment.outputs.public_subnet_one_id, var.environment.outputs.public_subnet_two_id]) 42 | } 43 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | env: 9 | variables: 10 | TF_VERSION: 1.4.5 11 | 12 | provision: 13 | # get proton input parameters 14 | - export IN=$(cat proton-inputs.json) && echo ${IN} 15 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 16 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 17 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 18 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 19 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 20 | 21 | # install terraform cli 22 | - echo "Installing Terraform CLI ${TF_VERSION}" 23 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 24 | 25 | # provision vpc/ecs resources, storing state in the environment s3 bucket 26 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 27 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 28 | - terraform apply -var-file=proton-inputs.json -auto-approve 29 | 30 | # pass terraform output to proton 31 | - chmod +x ./output.sh && ./output.sh 32 | 33 | deprovision: 34 | # get proton input parameters 35 | - export IN=$(cat proton-inputs.json) && echo ${IN} 36 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 37 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 38 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 39 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 40 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 41 | 42 | # install terraform cli 43 | - echo "Installing Terraform CLI ${TF_VERSION}" 44 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 45 | 46 | # destroy environment 47 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 48 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 49 | - terraform destroy -var-file=proton-inputs.json -auto-approve 50 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --status IN_PROGRESS --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "The default endpoint for the HTTP API" 3 | value = module.tf_lambda_apigw_service.endpoint 4 | } 5 | 6 | output "lambda_runtime" { 7 | description = "the lambda runtime passed in. needed for the service pipeline" 8 | value = var.service_instance.inputs.lambda_runtime 9 | } 10 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/src/function.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event, context) => { 2 | try { 3 | // Log event and context object to CloudWatch Logs 4 | console.log("Event: ", JSON.stringify(event, null, 2)); 5 | console.log("Context: ", JSON.stringify(context, null, 2)); 6 | // Create event object to return to caller 7 | const eventObj = { 8 | functionName: context.functionName, 9 | rawPath: event.rawPath, 10 | }; 11 | const response = { 12 | statusCode: 200, 13 | body: JSON.stringify(eventObj, null, 2), 14 | }; 15 | return response; 16 | } catch (error) { 17 | console.error(error); 18 | throw new Error(error); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "The default endpoint for the HTTP API" 3 | value = aws_apigatewayv2_stage.lambda.invoke_url 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "the name of the service" 3 | type = string 4 | } 5 | 6 | variable "lambda_runtime" { 7 | description = "The Lambda runtime" 8 | type = string 9 | default = "nodejs18.x" 10 | } 11 | 12 | variable "s3_bucket" { 13 | description = "The S3 bucket location containing the function's deployment package" 14 | type = string 15 | default = "" 16 | } 17 | 18 | variable "s3_key" { 19 | description = "The S3 key of an object containing the function's deployment package" 20 | type = string 21 | default = "" 22 | } 23 | 24 | variable "lambda_handler" { 25 | description = "The function entrypoint in your code" 26 | type = string 27 | default = "" 28 | } 29 | 30 | variable "memory_size" { 31 | description = "Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128." 32 | type = number 33 | default = 128 34 | } 35 | 36 | variable "timeout" { 37 | description = "The amount of time your Lambda Function has to run in seconds. Defaults to 3." 38 | type = number 39 | default = 3 40 | } 41 | 42 | variable "vpc_access" { 43 | description = "whether or not vpc access is enabled" 44 | type = bool 45 | default = false 46 | } 47 | 48 | variable "vpc_subnet_ids" { 49 | description = "A list of subnet IDs associated with the Lambda function" 50 | type = list(string) 51 | default = [] 52 | } 53 | 54 | variable "vpc_security_group_ids" { 55 | description = "A list of security group IDs associated with the Lambda function" 56 | type = list(string) 57 | default = [] 58 | } 59 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/instance_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "environment" { 4 | description = "proton environment" 5 | type = object({ 6 | name = string 7 | account_id = string 8 | outputs = map(string) 9 | }) 10 | } 11 | 12 | variable "service" { 13 | description = "proton service" 14 | type = object({ 15 | name = string 16 | repository_id = string 17 | repository_connection_arn = string 18 | branch_name = string 19 | }) 20 | } 21 | 22 | variable "service_instance" { 23 | description = "proton service instance" 24 | type = object({ 25 | name = string 26 | inputs = any 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/README.md: -------------------------------------------------------------------------------- 1 | This is a Terraform module that is provisioned by AWS Proton 2 | 3 | 4 | ## Requirements 5 | 6 | | Name | Version | 7 | |------|---------| 8 | | [terraform](#requirement\_terraform) | >= 1.0 | 9 | | [aws](#requirement\_aws) | ~> 5.0 | 10 | 11 | ## Providers 12 | 13 | No providers. 14 | 15 | ## Modules 16 | 17 | | Name | Source | Version | 18 | |------|--------|---------| 19 | | [cicd\_pipeline](#module\_cicd\_pipeline) | ./src | n/a | 20 | 21 | ## Resources 22 | 23 | No resources. 24 | 25 | ## Inputs 26 | 27 | | Name | Description | Type | Default | Required | 28 | |------|-------------|------|---------|:--------:| 29 | | [pipeline](#input\_pipeline) | the proton pipeline |
object({
inputs = any
})
| n/a | yes | 30 | | [service](#input\_service) | proton service |
object({
name = string
repository_id = string
repository_connection_arn = string
branch_name = string
})
| n/a | yes | 31 | | [service\_instances](#input\_service\_instances) | the service instances |
list(
object({
name = string
inputs = any
outputs = any
environment = object({
account_id = string
name = string
outputs = any
})
})
)
| n/a | yes | 32 | 33 | ## Outputs 34 | 35 | | Name | Description | 36 | |------|-------------| 37 | | [PipelineEndpoint](#output\_PipelineEndpoint) | A link to the generated CodePipeline | 38 | 39 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | default_tags { 16 | tags = { 17 | "proton:service" = var.service.name, 18 | "proton:pipeline" = var.service.name, 19 | } 20 | } 21 | } 22 | 23 | module "cicd_pipeline" { 24 | source = "./src" 25 | 26 | # name based on proton input 27 | name = var.service.name 28 | 29 | # service input 30 | service_name = var.service.name 31 | repository_connection_arn = var.service.repository_connection_arn 32 | repository_id = var.service.repository_id 33 | branch_name = var.service.branch_name 34 | 35 | # service instances 36 | service_instances = var.service_instances 37 | 38 | # pipeline input 39 | code_dir = var.pipeline.inputs.code_dir 40 | packaging_command = var.pipeline.inputs.packaging_command 41 | unit_test_command = var.pipeline.inputs.unit_test_command 42 | } 43 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | project_properties: 9 | # this setting will throw an error in case the account's 10 | # CodeBuild "Concurrently running builds" quota is too low 11 | ConcurrentBuildLimit: 2 12 | env: 13 | variables: 14 | TF_VERSION: 1.4.2 15 | 16 | provision: 17 | # build tf state based on proton input 18 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 19 | 20 | # install terraform cli 21 | - echo "Installing Terraform CLI ${TF_VERSION}" 22 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 23 | 24 | # provision, storing state in an s3 bucket 25 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 26 | - terraform apply -var-file=proton-inputs.json -auto-approve 27 | 28 | # pass terraform output to proton 29 | - chmod +x ./output.sh && ./output.sh 30 | 31 | deprovision: 32 | # build tf state based on proton input 33 | - . ./tf-state.sh && echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 34 | 35 | # install terraform cli 36 | - echo "Installing Terraform CLI ${TF_VERSION}" 37 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 38 | 39 | # provision, storing state in an s3 bucket 40 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 41 | - terraform destroy -var-file=proton-inputs.json -auto-approve 42 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | # note that proton currently requires the `PipelineEndpoint` 2 | # output in order to work with the pipeline run console 3 | output "PipelineEndpoint" { 4 | description = "A link to the generated CodePipeline" 5 | value = module.cicd_pipeline.pipeline_endpoint 6 | } 7 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/src/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | region = data.aws_region.current.id 4 | } -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "pipeline_endpoint" { 2 | description = "A link to the generated CodePipeline" 3 | value = "https://${local.region}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${aws_codepipeline.pipeline.id}/view?region=${local.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "namespace used for all resources" 3 | type = string 4 | } 5 | 6 | variable "service_name" { 7 | description = "name of the associated service" 8 | type = string 9 | } 10 | 11 | variable "code_dir" { 12 | description = "Source directory for the service" 13 | type = string 14 | } 15 | 16 | variable "unit_test_command" { 17 | description = "the command to run for unit tests" 18 | type = string 19 | } 20 | 21 | variable "packaging_command" { 22 | description = "The commands which packages your code into a file called function.zip" 23 | type = string 24 | } 25 | 26 | variable "repository_connection_arn" { 27 | description = "repository connection arn" 28 | type = string 29 | } 30 | 31 | variable "repository_id" { 32 | description = "repository_id" 33 | type = string 34 | } 35 | 36 | variable "branch_name" { 37 | description = "the name of the git branch" 38 | type = string 39 | } 40 | 41 | variable "service_instances" { 42 | description = "list of service instances to deploy to. typically just one" 43 | type = list( 44 | object({ 45 | name = string 46 | inputs = any 47 | outputs = any 48 | environment = object({ 49 | account_id = string 50 | name = string 51 | outputs = any 52 | }) 53 | }) 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/tf-state.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export IN=$(cat proton-inputs.json) && echo ${IN} 3 | export TF_STATE_BUCKET=$(echo $IN | jq '.service_instances[0].environment.outputs.tf_state_bucket' -r) 4 | export PROTON_SVC=$(echo ${IN} | jq '.service.name' -r) 5 | export KEY=${PROTON_SVC}.pipeline.tfstate 6 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service-cicd-codepipeline/v1/pipeline_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "pipeline" { 4 | description = "the proton pipeline" 5 | type = object({ 6 | inputs = any 7 | }) 8 | } 9 | 10 | variable "service" { 11 | description = "proton service" 12 | type = object({ 13 | name = string 14 | repository_id = string 15 | repository_connection_arn = string 16 | branch_name = string 17 | }) 18 | } 19 | 20 | variable "service_instances" { 21 | description = "the service instances" 22 | type = list( 23 | object({ 24 | name = string 25 | inputs = any 26 | outputs = any 27 | environment = object({ 28 | account_id = string 29 | name = string 30 | outputs = any 31 | }) 32 | }) 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/.template-registration.yaml: -------------------------------------------------------------------------------- 1 | compatible_environments: 2 | - tf-vpc-ecs-cluster:1 3 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/Makefile: -------------------------------------------------------------------------------- 1 | template := tf-lambda-apigw-service 2 | key := proton-service-template.tar.gz 3 | display := "Lambda Web Service" 4 | description := "Lambda API Gateway HTTP API" 5 | 6 | all: help 7 | 8 | .PHONY: help 9 | help: Makefile 10 | @echo 11 | @echo " Choose a make command to run" 12 | @echo 13 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 14 | @echo 15 | 16 | ## template: create a service template and register a version, ex. `make template bucket=my-bucket version=1` 17 | .PHONY: template 18 | template: 19 | aws proton create-service-template \ 20 | --name ${template} \ 21 | --display-name ${display} \ 22 | --description ${description} \ 23 | --pipeline-provisioning CUSTOMER_MANAGED 24 | 25 | tar -zcvf ${key} schema instance_infrastructure 26 | aws s3 cp ${key} s3://${bucket}/${key} 27 | rm ${key} 28 | 29 | aws proton create-service-template-version \ 30 | --template-name ${template} \ 31 | --description "registered from CLI" \ 32 | --compatible-environment-templates="majorVersion=1,templateName=tf-vpc-ecs-cluster" \ 33 | --source s3="{bucket=${bucket},key=${key}}" 34 | 35 | aws proton wait service-template-version-registered \ 36 | --template-name ${template} --major-version ${version} --minor-version 0 37 | 38 | aws proton update-service-template-version \ 39 | --template-name ${template} --major-version ${version} --minor-version 0 --status PUBLISHED 40 | 41 | ## delete: deletes proton template 42 | .PHONY: delete 43 | delete: 44 | aws proton list-service-template-versions \ 45 | --template-name ${template} | jq '.templateVersions[] | {majorVersion, minorVersion}' | jq '"--major-version " + .majorVersion + " --minor-version " + .minorVersion' -r | xargs -n 4 aws proton delete-service-template-version --template-name ${template} $1 46 | aws proton delete-service-template --name ${template} 47 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | This template creates a web API stack using API Gateway HTTP API and Lambda. It's compatible with the [vpc-ecs-cluster](../../environment-templates/vpc-ecs-cluster/README.md) template. 4 | 5 | By default, the lambda function will not be connected to a VPC. If you want the lambda to be able to access other resources inside a VPC, you can set the `VPC Access` input property to `true` and then select the `subnet type`. The actual subnet and security group values will come from the associated proton environment. 6 | 7 | The template will deploy a default function (written in node.js) regardless of what you set `lambda_runtime` and `lambda_handler` to. To deploy your app, upload your deployment package zip file to an S3 bucket, and update the service by specifying the `s3_bucket` and `s3_key` input fields. 8 | 9 | 10 | ## Register Template in AWS Proton 11 | 12 | To register this template in AWS Proton, you can either use the GUI console, or you can run the `make template` command specifying an S3 bucket used to store the template bundle and the version you'd like to register. 13 | 14 | ```sh 15 | cd terraform/service-templates/tf-lambda-apigw-service/v1 16 | make template bucket=my-bucket version=1 17 | ``` 18 | 19 | ### Input Parameters 20 | 21 | ![input](./input.png) 22 | 23 | ## Security 24 | 25 | See [CONTRIBUTING](../../CONTRIBUTING.md#security-issue-notifications) for more information. 26 | 27 | ## License 28 | 29 | This library is licensed under the MIT-0 License. See the [LICENSE](../../LICENSE) file. 30 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-containers/proton-codebuild-provisioning-examples/0abc6b80f93b6595b624efd9910387d9d7d8b4ee/terraform/service-templates/tf-lambda-apigw-service/v1/input.png -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/install-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip && \ 5 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS && \ 6 | curl https://keybase.io/hashicorp/pgp_keys.asc | gpg --import && \ 7 | curl -Os https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_SHA256SUMS.sig && \ 8 | gpg --verify terraform_${TF_VERSION}_SHA256SUMS.sig terraform_${TF_VERSION}_SHA256SUMS && \ 9 | shasum -a 256 -c terraform_${TF_VERSION}_SHA256SUMS 2>&1 | grep "${TF_VERSION}_linux_amd64.zip:\sOK" && \ 10 | unzip -o terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin && \ 11 | terraform --version 12 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | default_tags { 16 | tags = { 17 | "proton:environment" = var.environment.name 18 | "proton:service" = var.service.name, 19 | "proton:service_instance" = var.service_instance.name, 20 | } 21 | } 22 | } 23 | 24 | 25 | module "tf_lambda_apigw_service" { 26 | source = "./src" 27 | 28 | name = "${var.service.name}-${var.service_instance.name}" 29 | lambda_handler = var.service_instance.inputs.lambda_handler 30 | lambda_runtime = var.service_instance.inputs.lambda_runtime 31 | memory_size = var.service_instance.inputs.memory_size 32 | s3_bucket = var.service_instance.inputs.s3_bucket 33 | s3_key = var.service_instance.inputs.s3_key 34 | timeout = var.service_instance.inputs.timeout 35 | vpc_access = var.service_instance.inputs.vpc_access 36 | 37 | # get vpc config from environment output 38 | vpc_security_group_ids = [var.environment.outputs.default_security_group_id] 39 | vpc_subnet_ids = (var.service_instance.inputs.subnet_type == "private" ? 40 | [var.environment.outputs.private_subnet_one_id, var.environment.outputs.private_subnet_two_id] : 41 | [var.environment.outputs.public_subnet_one_id, var.environment.outputs.public_subnet_two_id]) 42 | } 43 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/manifest.yaml: -------------------------------------------------------------------------------- 1 | infrastructure: 2 | templates: 3 | - rendering_engine: codebuild 4 | settings: 5 | image: aws/codebuild/standard:6.0 6 | runtimes: 7 | golang: 1.18 8 | env: 9 | variables: 10 | TF_VERSION: 1.4.5 11 | 12 | provision: 13 | # get proton input parameters 14 | - export IN=$(cat proton-inputs.json) && echo ${IN} 15 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 16 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 17 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 18 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 19 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 20 | 21 | # install terraform cli 22 | - echo "Installing Terraform CLI ${TF_VERSION}" 23 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 24 | 25 | # provision vpc/ecs resources, storing state in the environment s3 bucket 26 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 27 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 28 | - terraform apply -var-file=proton-inputs.json -auto-approve 29 | 30 | # pass terraform output to proton 31 | - chmod +x ./output.sh && ./output.sh 32 | 33 | deprovision: 34 | # get proton input parameters 35 | - export IN=$(cat proton-inputs.json) && echo ${IN} 36 | - export PROTON_ENV=$(echo $IN | jq '.environment.name' -r) 37 | - export PROTON_SVC=$(echo $IN | jq '.service.name' -r) 38 | - export PROTON_SVC_INSTANCE=$(echo $IN | jq '.service_instance.name' -r) 39 | - export TF_STATE_BUCKET=$(echo $IN | jq '.environment.outputs.tf_state_bucket' -r) 40 | - export KEY=${PROTON_SVC}.${PROTON_SVC_INSTANCE}.tfstate 41 | 42 | # install terraform cli 43 | - echo "Installing Terraform CLI ${TF_VERSION}" 44 | - chmod +x ./install-terraform.sh && ./install-terraform.sh ${TF_VERSION} 45 | 46 | # destroy environment 47 | - echo "terraform remote state = s3://${TF_STATE_BUCKET}/${KEY}" 48 | - terraform init -backend-config="bucket=${TF_STATE_BUCKET}" -backend-config="key=${KEY}" 49 | - terraform destroy -var-file=proton-inputs.json -auto-approve 50 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | terraform output -json | jq 'to_entries | map({key:.key, valueString:.value.value})' > output.json 4 | aws proton notify-resource-deployment-status-change --resource-arn ${RESOURCE_ARN} --status IN_PROGRESS --outputs file://./output.json 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "The default endpoint for the HTTP API" 3 | value = module.tf_lambda_apigw_service.endpoint 4 | } 5 | 6 | output "lambda_runtime" { 7 | description = "the lambda runtime passed in. needed for the service pipeline" 8 | value = var.service_instance.inputs.lambda_runtime 9 | } 10 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/src/function.js: -------------------------------------------------------------------------------- 1 | exports.handler = async (event, context) => { 2 | try { 3 | // Log event and context object to CloudWatch Logs 4 | console.log("Event: ", JSON.stringify(event, null, 2)); 5 | console.log("Context: ", JSON.stringify(context, null, 2)); 6 | // Create event object to return to caller 7 | const eventObj = { 8 | functionName: context.functionName, 9 | rawPath: event.rawPath, 10 | }; 11 | const response = { 12 | statusCode: 200, 13 | body: JSON.stringify(eventObj, null, 2), 14 | }; 15 | return response; 16 | } catch (error) { 17 | console.error(error); 18 | throw new Error(error); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/src/outputs.tf: -------------------------------------------------------------------------------- 1 | output "endpoint" { 2 | description = "The default endpoint for the HTTP API" 3 | value = aws_apigatewayv2_stage.lambda.invoke_url 4 | } 5 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/src/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "the name of the service" 3 | type = string 4 | } 5 | 6 | variable "lambda_runtime" { 7 | description = "The Lambda runtime" 8 | type = string 9 | default = "nodejs18.x" 10 | } 11 | 12 | variable "s3_bucket" { 13 | description = "The S3 bucket location containing the function's deployment package" 14 | type = string 15 | default = "" 16 | } 17 | 18 | variable "s3_key" { 19 | description = "The S3 key of an object containing the function's deployment package" 20 | type = string 21 | default = "" 22 | } 23 | 24 | variable "lambda_handler" { 25 | description = "The function entrypoint in your code" 26 | type = string 27 | default = "" 28 | } 29 | 30 | variable "memory_size" { 31 | description = "Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128." 32 | type = number 33 | default = 128 34 | } 35 | 36 | variable "timeout" { 37 | description = "The amount of time your Lambda Function has to run in seconds. Defaults to 3." 38 | type = number 39 | default = 3 40 | } 41 | 42 | variable "vpc_access" { 43 | description = "whether or not vpc access is enabled" 44 | type = bool 45 | default = false 46 | } 47 | 48 | variable "vpc_subnet_ids" { 49 | description = "A list of subnet IDs associated with the Lambda function" 50 | type = list(string) 51 | default = [] 52 | } 53 | 54 | variable "vpc_security_group_ids" { 55 | description = "A list of security group IDs associated with the Lambda function" 56 | type = list(string) 57 | default = [] 58 | } 59 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/instance_infrastructure/variables.tf: -------------------------------------------------------------------------------- 1 | # required by proton 2 | 3 | variable "environment" { 4 | description = "proton environment" 5 | type = object({ 6 | name = string 7 | account_id = string 8 | outputs = map(string) 9 | }) 10 | } 11 | 12 | variable "service" { 13 | description = "proton service" 14 | type = object({ 15 | name = string 16 | repository_id = string 17 | repository_connection_arn = string 18 | branch_name = string 19 | }) 20 | } 21 | 22 | variable "service_instance" { 23 | description = "proton service instance" 24 | type = object({ 25 | name = string 26 | inputs = any 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /terraform/service-templates/tf-lambda-apigw-service/v1/schema/schema.yaml: -------------------------------------------------------------------------------- 1 | schema: 2 | format: 3 | openapi: "3.0.0" 4 | service_input_type: service 5 | 6 | types: 7 | service: 8 | type: object 9 | description: Service input properties 10 | properties: 11 | lambda_handler: 12 | title: "Lambda Handler" 13 | description: "The function entrypoint in your code" 14 | type: string 15 | default: "" 16 | 17 | lambda_runtime: 18 | title: "Lambda runtime" 19 | description: "The Lambda runtime" 20 | type: string 21 | enum: 22 | [ 23 | "nodejs18.x", 24 | "python3.10", 25 | "ruby3.2", 26 | "java17", 27 | "go1.x", 28 | "dotnet6", 29 | ] 30 | default: nodejs18.x 31 | 32 | memory_size: 33 | title: Lambda Memory 34 | description: "Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128." 35 | type: number 36 | default: 128 37 | 38 | s3_bucket: 39 | title: Lambda source code bucket 40 | description: "The S3 bucket location containing the function's deployment package" 41 | type: string 42 | default: "" 43 | 44 | s3_key: 45 | title: Lambda source code key 46 | description: "The S3 key of an object containing the function's deployment package" 47 | type: string 48 | default: "" 49 | 50 | timeout: 51 | title: Lambda Timeout 52 | description: "The amount of time your Lambda Function has to run in seconds. Defaults to 3." 53 | type: number 54 | default: 3 55 | 56 | vpc_access: 57 | title: VPC Access 58 | description: "Whether or not this lambda should be run in a VPC" 59 | type: boolean 60 | default: false 61 | 62 | subnet_type: 63 | title: "Subnet type" 64 | description: "If VPC access is enabled, Which VPC set of subnets would you like to run your function in" 65 | type: string 66 | enum: ["public", "private"] 67 | default: "private" 68 | --------------------------------------------------------------------------------