├── db └── password.txt ├── backend ├── requirements.txt ├── Dockerfile └── hello.py ├── frontend ├── Dockerfile └── conf ├── docker-compose.prod.rds.yaml ├── README.md ├── docker-compose.yaml ├── operations ├── buildspec.yaml ├── deployspec.yaml └── code-pipeline-cloudformation.yaml ├── .github └── workflows │ └── docker-hub.yaml ├── docker-compose.prod.migrate.yaml └── docker-compose.prod.scaling.yaml /db/password.txt: -------------------------------------------------------------------------------- 1 | db-78n9n -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.1.0 2 | mysql-connector==2.2.9 3 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.13-alpine 2 | COPY ./conf /etc/nginx/conf.d/default.conf -------------------------------------------------------------------------------- /frontend/conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 3000; 3 | server_name localhost; 4 | location / { 5 | proxy_pass http://backend:5000; 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | WORKDIR /code 3 | COPY ./ /code/ 4 | 5 | RUN ls -ltr /code 6 | RUN pip install -r requirements.txt 7 | ENV FLASK_APP hello.py 8 | CMD flask run --host=0.0.0.0 9 | -------------------------------------------------------------------------------- /docker-compose.prod.rds.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | backend: 3 | environment: 4 | - MY_SQL_DATABASE=workshop 5 | - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password 6 | - MY_SQL_HOST=${RDS_ENDPOINT} 7 | - MY_SQL_USER=admin 8 | - DB_TYPE=rds 9 | 10 | secrets: 11 | db-password: 12 | name: ${RDS_DB_SECRET_ARN} 13 | external: true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose ECS Sample 2 | 3 | This repository is the codebase used during the docker build talk on 02/04/2021. Entire talk is available at https://www.youtube.com/watch?v=2W_AQhWTmRw 4 | For step by step instructions on how to use this sample application go through https://docker.awsworkshop.io/ 5 | 6 | ### Provide Feedback 7 | 8 | * Regarding this repository - https://github.com/anshrma/docker-compose-ecs-sample/issues 9 | 10 | * Regarding Docker Compose - https://github.com/docker/roadmap/issues 11 | 12 | ## Reach me 13 | 14 | * Links to LinkedIn, Twitter, Email at https://github.com/anshrma 15 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | image: mysql:8.0.19 4 | command: '--default-authentication-plugin=mysql_native_password' 5 | restart: always 6 | secrets: 7 | - db-password 8 | volumes: 9 | - db-data:/var/lib/mysql 10 | networks: 11 | - backnet 12 | environment: 13 | - MYSQL_DATABASE=example 14 | - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password 15 | backend: 16 | build: 17 | context: ./backend 18 | restart: always 19 | secrets: 20 | - db-password 21 | # ports: 22 | # - 5000:5000 23 | networks: 24 | - backnet 25 | - frontnet 26 | depends_on: 27 | - db 28 | environment: 29 | - DB_TYPE=docker 30 | frontend: 31 | build: 32 | context: ./frontend 33 | restart: always 34 | ports: 35 | - 3000:3000 36 | networks: 37 | - frontnet 38 | depends_on: 39 | - backend 40 | volumes: 41 | db-data: 42 | secrets: 43 | db-password: 44 | # This is mounted to /run/secrets/ onto the container using it 45 | file: db/password.txt 46 | networks: 47 | backnet: 48 | frontnet: 49 | -------------------------------------------------------------------------------- /operations/buildspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | runtime-versions: 5 | python: 3.8 6 | commands: 7 | - echo "Performing manual install of compose cli" 8 | - curl -L -o docker-linux-amd64.tar.gz https://github.com/docker/compose-cli/releases/download/v1.0.10/docker-linux-amd64.tar.gz 9 | - tar xzf docker-linux-amd64.tar.gz 10 | - chmod +x docker/docker 11 | - ls -ltr 12 | - docker/docker compose --help 13 | - which docker 14 | - ln -s $(which docker) /usr/local/bin/com.docker.cli 15 | pre_build: 16 | commands: 17 | - echo Logging in to Docker Hub... 18 | - docker login --username $DOCKERHUB_USERNAME --password $DOCKERHUB_PASSWORD 19 | 20 | build: 21 | commands: 22 | - echo Build started on `date` 23 | - docker/docker compose build 24 | - echo "Tagging Docker image for Docker Hub" 25 | - docker images 26 | - docker tag src_backend:latest ${DOCKERHUB_USERNAME}/docker-compose-ecs-sample_backend:${CODEBUILD_RESOLVED_SOURCE_VERSION} 27 | - docker tag src_frontend:latest ${DOCKERHUB_USERNAME}/docker-compose-ecs-sample_frontend:${CODEBUILD_RESOLVED_SOURCE_VERSION} 28 | - docker push ${DOCKERHUB_USERNAME}/docker-compose-ecs-sample_backend:${CODEBUILD_RESOLVED_SOURCE_VERSION} 29 | - docker push ${DOCKERHUB_USERNAME}/docker-compose-ecs-sample_frontend:${CODEBUILD_RESOLVED_SOURCE_VERSION} 30 | 31 | post_build: 32 | commands: 33 | - echo "build successful" -------------------------------------------------------------------------------- /operations/deployspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | runtime-versions: 5 | python: 3.8 6 | commands: 7 | - echo "Performing manual install of compose cli" 8 | - curl -L -o docker-linux-amd64.tar.gz https://github.com/docker/compose-cli/releases/download/v1.0.10/docker-linux-amd64.tar.gz 9 | - tar xzf docker-linux-amd64.tar.gz 10 | - chmod +x docker/docker 11 | - ls -ltr 12 | - docker/docker compose --help 13 | - which docker 14 | - ln -s $(which docker) /usr/local/bin/com.docker.cli 15 | pre_build: 16 | commands: 17 | - echo Logging in to Docker Hub... 18 | - docker login --username $DOCKERHUB_USERNAME --password $DOCKERHUB_PASSWORD 19 | - STS_RESPONSE=$(curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) 20 | - export AWS_ACCESS_KEY_ID=$(echo $STS_RESPONSE | jq .AccessKeyId | tr -d \") 21 | - export AWS_SECRET_ACCESS_KEY=$(echo $STS_RESPONSE | jq .SecretAccessKey | tr -d \") 22 | - export AWS_SESSION_TOKEN=$(echo $STS_RESPONSE | jq .Token | tr -d \") 23 | - echo "Create Docker ECS context" 24 | - docker/docker context create ecs ecs-workshop --from-env 25 | - aws sts get-caller-identity 26 | - echo "Change context to use ECS context" 27 | - docker/docker context use ecs-workshop 28 | 29 | build: 30 | commands: 31 | - DOCKER_HUB_ID=${DOCKERHUB_USERNAME} DOCKER_PULL_SECRETS_MANAGER=${DOCKER_PULL_SECRETS_MANAGER} docker/docker compose -f docker-compose.yaml -f docker-compose.prod.migrate.yaml -p ${PROJECT_NAME} up 32 | 33 | post_build: 34 | commands: 35 | - echo "deploy successful" -------------------------------------------------------------------------------- /.github/workflows/docker-hub.yaml: -------------------------------------------------------------------------------- 1 | name: Docker Compose CI CD to DockerHub 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | docker-build-push-backend: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - 14 | name: Checkout 15 | uses: actions/checkout@v2 16 | - 17 | name: Set up QEMU 18 | uses: docker/setup-qemu-action@v1 19 | - 20 | name: Set up Docker Buildx 21 | uses: docker/setup-buildx-action@v1 22 | - 23 | name: Login to DockerHub 24 | uses: docker/login-action@v1 25 | with: 26 | username: ${{ secrets.DOCKERHUB_USERNAME }} 27 | password: ${{ secrets.DOCKERHUB_TOKEN }} 28 | - 29 | name: Build and push 30 | uses: docker/build-push-action@v2 31 | with: 32 | file: ./backend/Dockerfile 33 | push: true 34 | tags: ${{ secrets.DOCKERHUB_USERNAME }}/backend:${{ github.sha }} 35 | docker-build-push-frontend: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - 39 | name: Checkout 40 | uses: actions/checkout@v2 41 | - 42 | name: Set up QEMU 43 | uses: docker/setup-qemu-action@v1 44 | - 45 | name: Set up Docker Buildx 46 | uses: docker/setup-buildx-action@v1 47 | - 48 | name: Login to DockerHub 49 | uses: docker/login-action@v1 50 | with: 51 | username: ${{ secrets.DOCKERHUB_USERNAME }} 52 | password: ${{ secrets.DOCKERHUB_TOKEN }} 53 | - 54 | name: Build and push 55 | uses: docker/build-push-action@v2 56 | with: 57 | file: ./frontend/Dockerfile 58 | push: true 59 | tags: ${{ secrets.DOCKERHUB_USERNAME }}/frontend:${{ github.sha }} -------------------------------------------------------------------------------- /docker-compose.prod.migrate.yaml: -------------------------------------------------------------------------------- 1 | # x-aws-vpc: "vpc-FILL_ME" 2 | # x-aws-logs_retention: 30 3 | # x-aws-cloudformation: 4 | # Resources: 5 | # LoadBalancer: 6 | # Properties: 7 | # #Scheme: internal 8 | # Subnets: 9 | # - subnet-FILL_ME 10 | # - subnet-FILL_ME 11 | # # Certificates: 12 | # # - CertificateArn: "arn:aws:acm:certificate/123abc" 13 | # # Protocol: HTTPS 14 | # FrontendService: 15 | # Properties: 16 | # NetworkConfiguration: 17 | # AwsvpcConfiguration: 18 | # Subnets: 19 | # - subnet-FILL_ME 20 | # - subnet-FILL_ME 21 | 22 | services: 23 | frontend: 24 | image: ${DOCKER_HUB_ID}/docker-compose-ecs-sample_frontend:latest 25 | x-aws-pull_credentials: ${DOCKER_PULL_SECRETS_MANAGER} 26 | backend: 27 | image: ${DOCKER_HUB_ID}/docker-compose-ecs-sample_backend:latest 28 | x-aws-pull_credentials: ${DOCKER_PULL_SECRETS_MANAGER} 29 | logging: 30 | options: 31 | mode: non-blocking # Default is blocking 32 | max-buffer-size: 5m # Default is 1min 33 | # This enables task level IAM role for the service 34 | # x-aws-role: 35 | # Version: '2012-10-17' 36 | # Statement: 37 | # - Effect: Allow 38 | # Action: sqs:* 39 | # Resource: FILL_ME_ARN_OF_SQS 40 | # secrets: 41 | # db-password: 42 | # name: ${DOCKER_PULL_SECRETS_MANAGER} 43 | # external: true 44 | 45 | 46 | #Above overrides mysecrets 47 | # $ cat creds.json 48 | # { 49 | # "username":"Some secret name", 50 | # "password":"some secret credential" 51 | # } 52 | 53 | # Now create it using following 54 | # $ docker secret create pullcred /path/to/creds.json --context GIVE_ECS_CONTEXT_NAME 55 | # arn:aws:secretsmanager:eu-west-3:xxx:secret:pullcred -------------------------------------------------------------------------------- /docker-compose.prod.scaling.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | backend: 3 | cap_add: 4 | - SYS_PTRACE 5 | deploy: 6 | replicas: 2 7 | # resources: 8 | # limits: 9 | # memory: 2Gb 10 | # cpus: '0.5' 11 | x-aws-autoscaling: 12 | min: 1 13 | max: 10 #required 14 | cpu: 75 15 | 16 | x-aws-cloudformation: 17 | Transform: AWS::SecretsManager-2020-07-23 18 | Outputs: 19 | MySQL: 20 | Description: RDSInstance Endpoint 21 | Value: 22 | Fn::GetAtt : 23 | - MySQL 24 | - Endpoint.Address 25 | Resources: 26 | DBSecurityGroup: 27 | Type: AWS::EC2::SecurityGroup 28 | Properties: 29 | SecurityGroupIngress: 30 | - IpProtocol: "-1" 31 | CidrIp : 172.31.0.0/16 32 | GroupDescription: "Ingress Rule for DBSecurity" 33 | RDSInstanceSecret: 34 | Type: AWS::SecretsManager::Secret 35 | Properties: 36 | Description: 'This is the secret for my RDS instance' 37 | Name: RDSInstanceSecret 38 | GenerateSecretString: 39 | SecretStringTemplate: '{"username": "admin"}' 40 | GenerateStringKey: 'password' 41 | PasswordLength: 16 42 | ExcludeCharacters: '"@/\' 43 | MySQL: 44 | Type: AWS::RDS::DBInstance 45 | DependsOn: RDSInstanceSecret 46 | Properties: 47 | VPCSecurityGroups: 48 | - Fn::GetAtt : 49 | - DBSecurityGroup 50 | - GroupId 51 | DBName: workshop 52 | DBInstanceClass: db.t2.micro 53 | AllocatedStorage: 20 54 | Engine: MySQL 55 | EngineVersion: 8.0.23 56 | MasterUsername: '{{resolve:secretsmanager:RDSInstanceSecret:SecretString:username}}' 57 | MasterUserPassword: '{{resolve:secretsmanager:RDSInstanceSecret:SecretString:password}}' 58 | -------------------------------------------------------------------------------- /backend/hello.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask 3 | import mysql.connector 4 | import uuid 5 | import os 6 | import json 7 | 8 | class DBManager: 9 | def __init__(self): 10 | db_type = os.environ.get('DB_TYPE') 11 | password_file = os.environ.get('MY_SQL_PASSWORD_FILE','/run/secrets/db-password') 12 | pf = open(password_file, 'r') 13 | password = None 14 | 15 | if db_type == 'rds': 16 | credentials = json.load(pf) 17 | password = credentials["password"] 18 | else: 19 | password = pf.read() 20 | 21 | database = os.environ.get('MY_SQL_DATABASE','example') 22 | host = os.environ.get('MY_SQL_HOST',"db") 23 | user = os.environ.get('MY_SQL_USER',"root") 24 | self.connection = mysql.connector.connect( 25 | user=user, 26 | password=password, 27 | host=host, # name of the mysql service as set in the docker-compose file 28 | database=database, 29 | auth_plugin='mysql_native_password' 30 | ) 31 | pf.close() 32 | self.cursor = self.connection.cursor() 33 | 34 | def populate_db(self): 35 | 36 | self.cursor.execute('DROP TABLE IF EXISTS blog') 37 | self.cursor.execute('CREATE TABLE blog (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(256))') 38 | self.cursor.execute("insert into blog values(NULL,'initial load')") 39 | 40 | self.connection.commit() 41 | 42 | def insert_records(self,id,name): 43 | data = (id,str(name)) 44 | query = ( 45 | "INSERT INTO blog(id,title)" 46 | "VALUES (%s, %s)" 47 | ) 48 | self.cursor.execute(query,data) 49 | self.connection.commit() 50 | 51 | def query_titles(self): 52 | self.cursor.execute('SELECT title FROM blog') 53 | rec = [] 54 | for c in self.cursor: 55 | rec.append(c[0]) 56 | return rec 57 | 58 | 59 | server = Flask(__name__) 60 | conn = None 61 | 62 | @server.route('/') 63 | def listName(): 64 | global conn 65 | if not conn: 66 | conn = DBManager() 67 | conn.populate_db() 68 | #conn.insert_records() 69 | rec = conn.query_titles() 70 | 71 | response = '' 72 | for c in rec: 73 | response = response + '
' + c + '
' 74 | return response 75 | 76 | @server.route('/add//') 77 | def addName(id,name): 78 | global conn 79 | if not conn: 80 | conn = DBManager(password_file='/run/secrets/db-password') 81 | conn.insert_records(id,name) 82 | rec = conn.query_titles() 83 | 84 | response = '' 85 | for c in rec: 86 | response = response + '
' + c + '
' 87 | return response 88 | 89 | if __name__ == '__main__': 90 | server.run() 91 | -------------------------------------------------------------------------------- /operations/code-pipeline-cloudformation.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | Description: This template will launch the CI/CD Pipeline for the Docker-AWS Workshop. 3 | 4 | Parameters: 5 | ProjectName: 6 | Type: String 7 | Default: docker-compose-ecs-sample 8 | GitHubOwner: 9 | Type: String 10 | AllowedPattern: '[A-Za-z0-9-]+' 11 | Default: anshrma 12 | GitHubRepository: 13 | Type: String 14 | AllowedPattern: '[A-Za-z0-9-]+' 15 | Default: docker-compose-ecs-sample 16 | GitHubBranch: 17 | Type: String 18 | AllowedPattern: '[A-Za-z0-9-]+' 19 | Default: main 20 | DockerPullSecretsManagerArn: 21 | Type: String 22 | Description : ARN for Secrets Manager Stored Secret 23 | 24 | 25 | Resources: 26 | CodeBuildServiceRole: 27 | Type: 'AWS::IAM::Role' 28 | Properties: 29 | AssumeRolePolicyDocument: 30 | Version: 2012-10-17 31 | Statement: 32 | - Effect: Allow 33 | Principal: 34 | Service: 35 | - codebuild.amazonaws.com 36 | Action: 37 | - 'sts:AssumeRole' 38 | Policies: 39 | - PolicyName: root 40 | PolicyDocument: 41 | Version: 2012-10-17 42 | Statement: 43 | - Sid: CloudWatchLogsPolicy 44 | Effect: Allow 45 | Action: 46 | - 'logs:CreateLogGroup' 47 | - 'logs:CreateLogStream' 48 | - 'logs:PutLogEvents' 49 | - 'logs:DescribeLogGroup' 50 | - 'logs:FilterLogEvents' 51 | - 'logs:DeleteLogGroup' 52 | Resource: 53 | - '*' 54 | - Sid: S3GetObjectPolicy 55 | Effect: Allow 56 | Action: 57 | - 's3:GetObject' 58 | - 's3:GetObjectVersion' 59 | - 's3:GetBucketAcl' 60 | - 's3:GetBucketLocation' 61 | - 's3:PutObject' 62 | Resource: 63 | - '*' 64 | - Sid: RDSAccess 65 | Effect: Allow 66 | Action: 67 | - rds:* 68 | - secretsmanager:* 69 | - ec2:* 70 | - ecs:* 71 | - cloudformation:* 72 | - servicediscovery:* 73 | - application-autoscaling:* 74 | - elasticloadbalancing:* 75 | - route53:* 76 | - elasticfilesystem:* 77 | - iam:* 78 | - efs:* 79 | Resource: 80 | - '*' 81 | 82 | CodePipelineServiceRole: 83 | Type: 'AWS::IAM::Role' 84 | Properties: 85 | AssumeRolePolicyDocument: 86 | Version: 2012-10-17 87 | Statement: 88 | - Effect: Allow 89 | Principal: 90 | Service: 91 | - codepipeline.amazonaws.com 92 | Action: 93 | - sts:AssumeRole 94 | Policies: 95 | - PolicyName: root 96 | PolicyDocument: 97 | Version: 2012-10-17 98 | Statement: 99 | - Sid: CloudWatchLogsPolicy 100 | Effect: Allow 101 | Action: 102 | - 'logs:CreateLogGroup' 103 | - 'logs:CreateLogStream' 104 | - 'logs:PutLogEvents' 105 | Resource: 106 | - '*' 107 | - Sid: S3GetObjectPolicy 108 | Effect: Allow 109 | Action: 110 | - 's3:GetObject' 111 | - 's3:GetObjectVersion' 112 | Resource: 113 | - '*' 114 | - Sid: S3PutObjectPolicy 115 | Effect: Allow 116 | Action: 117 | - 's3:PutObject' 118 | Resource: 119 | - '*' 120 | - Sid: S3BucketIdentity 121 | Effect: Allow 122 | Action: 123 | - 's3:GetBucketAcl' 124 | - 's3:GetBucketLocation' 125 | Resource: 126 | - '*' 127 | - Sid: CodeBuildPolicy 128 | Action: 129 | - 'codebuild:BatchGetBuilds' 130 | - 'codebuild:StartBuild' 131 | Resource: '*' 132 | Effect: Allow 133 | 134 | ArtifactBucket: 135 | Type: AWS::S3::Bucket 136 | DeletionPolicy: Delete 137 | 138 | CodeBuildSourceCredential: 139 | Type: 'AWS::CodeBuild::SourceCredential' 140 | Properties: 141 | AuthType: PERSONAL_ACCESS_TOKEN 142 | ServerType: GITHUB 143 | Token: >- 144 | {{resolve:secretsmanager:github-oauth-token:SecretString:GITHUB_ACCESS_TOKEN}} 145 | 146 | CodeBuildProject: 147 | Type: 'AWS::CodeBuild::Project' 148 | Properties: 149 | Name: !Sub ${AWS::StackName}-codebuild-project 150 | ServiceRole: !GetAtt 151 | - CodeBuildServiceRole 152 | - Arn 153 | Source: 154 | Type: GITHUB 155 | Location: !Sub 'https://github.com/${GitHubOwner}/${GitHubRepository}.git' 156 | BuildSpec: operations/buildspec.yaml 157 | Auth: 158 | Type: OAUTH 159 | Resource: !Ref CodeBuildSourceCredential 160 | Artifacts: 161 | Type: NO_ARTIFACTS 162 | Triggers: 163 | Webhook: true 164 | FilterGroups: 165 | - - Type: EVENT 166 | Pattern: >- 167 | PULL_REQUEST_CREATED, PULL_REQUEST_UPDATED, 168 | PULL_REQUEST_REOPENED, PULL_REQUEST_MERGED, 169 | - Type: BASE_REF 170 | Pattern: !Sub '^refs/heads/${GitHubBranch}$' 171 | - - Type: EVENT 172 | Pattern: >- 173 | PUSH 174 | - Type: HEAD_REF 175 | Pattern: '^refs/tags/.*' 176 | Environment: 177 | Type: LINUX_CONTAINER 178 | ComputeType: BUILD_GENERAL1_SMALL 179 | Image: 'aws/codebuild/standard:5.0' 180 | PrivilegedMode: true 181 | EnvironmentVariables: 182 | - Name: DOCKERHUB_USERNAME 183 | Type: SECRETS_MANAGER 184 | Value: !Sub ${DockerPullSecretsManagerArn}:username 185 | - Name: DOCKERHUB_PASSWORD 186 | Type: SECRETS_MANAGER 187 | Value: !Sub ${DockerPullSecretsManagerArn}:password 188 | 189 | CodeBuildDeployProject: 190 | Type: 'AWS::CodeBuild::Project' 191 | Properties: 192 | Name: !Sub ${AWS::StackName}-deploy-project 193 | ServiceRole: !GetAtt 194 | - CodeBuildServiceRole 195 | - Arn 196 | Source: 197 | Type: GITHUB 198 | Location: !Sub 'https://github.com/${GitHubOwner}/${GitHubRepository}.git' 199 | BuildSpec: operations/deployspec.yaml 200 | Auth: 201 | Type: OAUTH 202 | Resource: !Ref CodeBuildSourceCredential 203 | Artifacts: 204 | Type: NO_ARTIFACTS 205 | Triggers: 206 | Webhook: true 207 | FilterGroups: 208 | - - Type: EVENT 209 | Pattern: >- 210 | PULL_REQUEST_CREATED, PULL_REQUEST_UPDATED, 211 | PULL_REQUEST_REOPENED, PULL_REQUEST_MERGED, 212 | - Type: BASE_REF 213 | Pattern: !Sub '^refs/heads/${GitHubBranch}$' 214 | - - Type: EVENT 215 | Pattern: >- 216 | PUSH 217 | - Type: HEAD_REF 218 | Pattern: '^refs/tags/.*' 219 | Environment: 220 | Type: LINUX_CONTAINER 221 | ComputeType: BUILD_GENERAL1_SMALL 222 | Image: 'aws/codebuild/standard:5.0' 223 | PrivilegedMode: true 224 | EnvironmentVariables: 225 | - Name: DOCKERHUB_USERNAME 226 | Type: SECRETS_MANAGER 227 | Value: !Sub ${DockerPullSecretsManagerArn}:username 228 | - Name: DOCKERHUB_PASSWORD 229 | Type: SECRETS_MANAGER 230 | Value: !Sub ${DockerPullSecretsManagerArn}:password 231 | - Name: DOCKER_PULL_SECRETS_MANAGER 232 | Type: PLAINTEXT 233 | Value: !Ref DockerPullSecretsManagerArn 234 | - Name: PROJECT_NAME 235 | Type: PLAINTEXT 236 | Value: !Ref ProjectName 237 | 238 | Pipeline: 239 | Type: AWS::CodePipeline::Pipeline 240 | Properties: 241 | Name: !Ref AWS::StackName 242 | RoleArn: !GetAtt 243 | - CodePipelineServiceRole 244 | - Arn 245 | ArtifactStore: 246 | Type: S3 247 | Location: !Ref ArtifactBucket 248 | Stages: 249 | - Name: Source 250 | Actions: 251 | - Name: Source 252 | ActionTypeId: 253 | Category: Source 254 | Owner: ThirdParty 255 | Version: '1' 256 | Provider: GitHub 257 | Configuration: 258 | Owner: !Ref GitHubOwner 259 | Repo: !Ref GitHubRepository 260 | Branch: main 261 | PollForSourceChanges: false 262 | OAuthToken: '{{resolve:secretsmanager:github-oauth-token:SecretString:GITHUB_ACCESS_TOKEN}}' 263 | OutputArtifacts: 264 | - Name: SourceCode 265 | RunOrder: 1 266 | - Name: Build 267 | Actions: 268 | - Name: Build 269 | ActionTypeId: 270 | Category: Build 271 | Owner: AWS 272 | Version: '1' 273 | Provider: CodeBuild 274 | Configuration: 275 | ProjectName: !Ref CodeBuildProject 276 | InputArtifacts: 277 | - Name: SourceCode 278 | OutputArtifacts: 279 | - Name: BuildOutput 280 | RunOrder: 1 281 | - Name: Deploy 282 | Actions: 283 | - Name: Deploy 284 | ActionTypeId: 285 | Category: Build 286 | Owner: AWS 287 | Version: '1' 288 | Provider: CodeBuild 289 | Configuration: 290 | ProjectName: !Ref CodeBuildDeployProject 291 | InputArtifacts: 292 | - Name: SourceCode 293 | OutputArtifacts: 294 | - Name: DeployOutput 295 | RunOrder: 1 296 | 297 | CodePipelineWebhook: 298 | Type: 'AWS::CodePipeline::Webhook' 299 | Properties: 300 | Authentication: GITHUB_HMAC 301 | AuthenticationConfiguration: 302 | SecretToken: >- 303 | {{resolve:secretsmanager:github-oauth-token:SecretString:GITHUB_ACCESS_TOKEN}} 304 | RegisterWithThirdParty: true 305 | Filters: 306 | - JsonPath: $.ref 307 | MatchEquals: 'refs/heads/{Branch}' 308 | - JsonPath: $.repository.name 309 | MatchEquals: '{Repo}' 310 | TargetPipeline: !Ref Pipeline 311 | TargetAction: Source 312 | TargetPipelineVersion: !GetAtt Pipeline.Version 313 | 314 | 315 | Outputs: 316 | PipelineUrl: 317 | Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${Pipeline} --------------------------------------------------------------------------------