diff --git a/deployments/buildspec/service.yml b/deployments/buildspec/service.yml new file mode 100644 index 0000000..5f9dd49 --- /dev/null +++ b/deployments/buildspec/service.yml @@ -0,0 +1,16 @@ +version: 0.2 +phases: + pre_build: + commands: + - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com + - TAG=$(echo $GIT_COMMIT_ID | head -c 8) + - IMAGE_URI=$REPO_URI:$TAG + build: + commands: + - docker build -t $IMAGE_URI -f server.Dockerfile . + post_build: + commands: + - docker push $IMAGE_URI + - printf '{"ImageUri":"%s"}' $IMAGE_URI > build.json +artifacts: + files: build.json \ No newline at end of file diff --git a/deployments/buildspec/services-all.yml b/deployments/buildspec/services-all.yml new file mode 100644 index 0000000..a1426d7 --- /dev/null +++ b/deployments/buildspec/services-all.yml @@ -0,0 +1,22 @@ +version: 0.2 +phases: + pre_build: + commands: + - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com + - TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8) + - IMAGE_SERVER_URI=$REPO_SERVER_URI:$TAG + - IMAGE_CACHE_URI=$REPO_CACHE_URI:$TAG + - IMAGE_DB_URI=$REPO_DB_URI:$TAG + build: + commands: + - docker build -t $IMAGE_SERVER_URI -f server.Dockerfile . + - docker build -t $IMAGE_CACHE_URI -f cache.Dockerfile . + - docker build -t $IMAGE_DB_URI -f database.Dockerfile . + post_build: + commands: + - docker push $IMAGE_SERVER_URI + - docker push $IMAGE_CACHE_URI + - docker push $IMAGE_DB_URI + - printf '{"ImageServerUri":"%s", "ImageCacheUri":"%s", "ImageDatabaseUri":"%s"}' $IMAGE_SERVER_URI $IMAGE_CACHE_URI $IMAGE_DB_URI > build.json +artifacts: + files: build.json \ No newline at end of file diff --git a/deployments/pipeline-services.yml b/deployments/pipeline-services.yml new file mode 100644 index 0000000..ca3e2f9 --- /dev/null +++ b/deployments/pipeline-services.yml @@ -0,0 +1,466 @@ +Parameters: + GitHubRepo: + Type: String + GitHubBranch: + Type: String + GitHubToken: + Type: String + NoEcho: true + GitHubUser: + Type: String + EnvironmentName: + Type: String + DeploymentType: + Type: String + Default: fargate + AllowedValues: [ecs, fargate] + +Resources: + # Create ECR respositories to hold built docker images + ServerRepository: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Sub ${EnvironmentName}-server + CacheRepository: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Sub ${EnvironmentName}-cache + DatabaseRepository: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Sub ${EnvironmentName}-database + + # A role used to give CodeBuild permission to access code, + # build it, and upload the build results to ECR + CodeBuildServiceRole: + Type: AWS::IAM::Role + Properties: + Path: / + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: root + PolicyDocument: + Version: 2012-10-17 + Statement: + - Resource: "*" + Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + - ecr:GetAuthorizationToken + - Resource: !Sub arn:aws:s3:::${ArtifactBucket}/* + Effect: Allow + Action: + - s3:GetObject + - s3:PutObject + - s3:GetObjectVersion + - Resource: + - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${ServerRepository} + - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${CacheRepository} + - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${DatabaseRepository} + Effect: Allow + Action: + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + - ecr:BatchCheckLayerAvailability + - ecr:PutImage + - ecr:InitiateLayerUpload + - ecr:UploadLayerPart + - ecr:CompleteLayerUpload + + # Role used to give CodePipeline to release a build. + CodePipelineServiceRole: + Type: AWS::IAM::Role + Properties: + Path: / + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codepipeline.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: root + PolicyDocument: + Version: 2012-10-17 + Statement: + # Allow codepipeline to put artifacts in the S3 bucket + # as well as get artifacts back out of it. + - Resource: + - !Sub arn:aws:s3:::${ArtifactBucket}/* + Effect: Allow + Action: + - s3:PutObject + - s3:GetObject + - s3:GetObjectVersion + - s3:GetBucketVersioning + # Allow codepipeline to build code builds + - Resource: "*" + Effect: Allow + Action: + - codebuild:StartBuild + - codebuild:BatchGetBuilds + - iam:PassRole + # Allow codepipeline to deploy cloudformation stacks + - Effect: Allow + Action: + - cloudformation:CreateChangeSet + - cloudformation:CreateStack + - cloudformation:CreateUploadBucket + - cloudformation:DeleteStack + - cloudformation:Describe* + - cloudformation:List* + - cloudformation:UpdateStack + - cloudformation:ValidateTemplate + - cloudformation:ExecuteChangeSet + Resource: "*" + + # CloudFormation deployment role. This role is passed by CodeBuild to + # CloudFormation to use when setting up the application resources + CloudFormationDeployRole: + Type: AWS::IAM::Role + Properties: + Path: / + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: cloudformation.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: deploy-stack + PolicyDocument: + Statement: + - Effect: Allow + Action: + - "iam:*" + - "ec2:*" + - "ecs:*" + - "elasticloadbalancing:*" + - "autoscaling:*" + - "elasticache:*" + - "logs:*" + - "application-autoscaling:*" + - "cloudwatch:*" + - "route53:*" + - "rds:*" + - "mq:*" + # - "secretsmanager:*" + - "ssm:*" + Resource: "*" + + # While the build is in progress we need a place to store artifacts + ArtifactBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Sub ${EnvironmentName}-${AWS::AccountId} + + # Build a service + CodeBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Artifacts: + Type: CODEPIPELINE + ServiceRole: !Ref CodeBuildServiceRole + Environment: + ComputeType: BUILD_GENERAL1_SMALL + Image: aws/codebuild/standard:5.0 + Type: LINUX_CONTAINER + PrivilegedMode: true + Source: + Type: CODEPIPELINE + BuildSpec: deployments/buildspec/service.yml + + # Create three pipelines for the three services + # Server pipeline + PipelineServer: + Type: AWS::CodePipeline::Pipeline + Properties: + RoleArn: !GetAtt CodePipelineServiceRole.Arn + ArtifactStore: + Type: S3 + Location: !Ref ArtifactBucket + Stages: + # Pull the source code from the Github repository + - Name: Source + Actions: + - Name: Source + Namespace: SourceVariables + ActionTypeId: + Category: Source + Owner: ThirdParty + Version: 1 + Provider: GitHub + Configuration: + Owner: !Ref GitHubUser + Repo: !Ref GitHubRepo + Branch: !Ref GitHubBranch + OAuthToken: !Ref GitHubToken + OutputArtifacts: + - Name: Source + RunOrder: 1 + # Build a service image + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Version: 1 + Provider: CodeBuild + Configuration: + ProjectName: !Ref CodeBuildProject + EnvironmentVariables: !Sub | + [ + { + "name":"AWS_DEFAULT_REGION", + "value":"${AWS::Region}", + "type":"PLAINTEXT" + }, + { + "name":"AWS_ACCOUNT_ID", + "value":"${AWS::AccountId}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_URI", + "value":"${ServerRepository.RepositoryUri}", + "type":"PLAINTEXT" + }, + { + "name":"GIT_COMMIT_ID", + "value":"#{SourceVariables.CommitId}", + "type":"PLAINTEXT" + } + ] + InputArtifacts: + - Name: Source + OutputArtifacts: + - Name: BuildOutput + RunOrder: 1 + # Deploy the service to the ECS/Fargate cluster + - Name: Deploy + Actions: + - Name: Deploy + ActionTypeId: + Category: Deploy + Owner: AWS + Version: 1 + Provider: CloudFormation + Configuration: + ActionMode: CREATE_UPDATE + RoleArn: !GetAtt CloudFormationDeployRole.Arn + StackName: !Sub ${EnvironmentName}-ServerService + TemplatePath: !Sub Source::deployments/services-${DeploymentType}/server.yml + Capabilities: CAPABILITY_IAM + ParameterOverrides: !Sub | + { + "EnvironmentName": "${EnvironmentName}", + "ImageUrl": { + "Fn::GetParam" : ["BuildOutput", "build.json", "ImageUri"] + } + } + InputArtifacts: + - Name: Source + - Name: BuildOutput + + # The cache pipeline + PipelineCache: + Type: AWS::CodePipeline::Pipeline + Properties: + RoleArn: !GetAtt CodePipelineServiceRole.Arn + ArtifactStore: + Type: S3 + Location: !Ref ArtifactBucket + Stages: + # Pull the source code from the Github repository + - Name: Source + Actions: + - Name: Source + Namespace: SourceVariables + ActionTypeId: + Category: Source + Owner: ThirdParty + Version: 1 + Provider: GitHub + Configuration: + Owner: !Ref GitHubUser + Repo: !Ref GitHubRepo + Branch: !Ref GitHubBranch + OAuthToken: !Ref GitHubToken + OutputArtifacts: + - Name: Source + RunOrder: 1 + # Build a service image + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Version: 1 + Provider: CodeBuild + Configuration: + ProjectName: !Ref CodeBuildProject + EnvironmentVariables: !Sub | + [ + { + "name":"AWS_DEFAULT_REGION", + "value":"${AWS::Region}", + "type":"PLAINTEXT" + }, + { + "name":"AWS_ACCOUNT_ID", + "value":"${AWS::AccountId}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_URI", + "value":"${CacheRepository.RepositoryUri}", + "type":"PLAINTEXT" + }, + { + "name":"GIT_COMMIT_ID", + "value":"#{SourceVariables.CommitId}", + "type":"PLAINTEXT" + } + ] + InputArtifacts: + - Name: Source + OutputArtifacts: + - Name: BuildOutput + RunOrder: 1 + # Deploy the service to the ECS/Fargate cluster + - Name: Deploy + Actions: + - Name: Deploy + ActionTypeId: + Category: Deploy + Owner: AWS + Version: 1 + Provider: CloudFormation + Configuration: + ActionMode: CREATE_UPDATE + RoleArn: !GetAtt CloudFormationDeployRole.Arn + StackName: !Sub ${EnvironmentName}-CacheService + TemplatePath: !Sub Source::deployments/services-${DeploymentType}/cache.yml + Capabilities: CAPABILITY_IAM + ParameterOverrides: !Sub | + { + "EnvironmentName": "${EnvironmentName}", + "ImageUrl": { + "Fn::GetParam" : ["BuildOutput", "build.json", "ImageUri"] + } + } + InputArtifacts: + - Name: Source + - Name: BuildOutput + + # The database pipeline + PipelineDatabase: + Type: AWS::CodePipeline::Pipeline + Properties: + RoleArn: !GetAtt CodePipelineServiceRole.Arn + ArtifactStore: + Type: S3 + Location: !Ref ArtifactBucket + Stages: + # Pull the source code from the Github repository + - Name: Source + Actions: + - Name: Source + Namespace: SourceVariables + ActionTypeId: + Category: Source + Owner: ThirdParty + Version: 1 + Provider: GitHub + Configuration: + Owner: !Ref GitHubUser + Repo: !Ref GitHubRepo + Branch: !Ref GitHubBranch + OAuthToken: !Ref GitHubToken + OutputArtifacts: + - Name: Source + RunOrder: 1 + # Build a service image + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Version: 1 + Provider: CodeBuild + Configuration: + ProjectName: !Ref CodeBuildProject + EnvironmentVariables: !Sub | + [ + { + "name":"AWS_DEFAULT_REGION", + "value":"${AWS::Region}", + "type":"PLAINTEXT" + }, + { + "name":"AWS_ACCOUNT_ID", + "value":"${AWS::AccountId}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_URI", + "value":"${DatabaseRepository.RepositoryUri}", + "type":"PLAINTEXT" + }, + { + "name":"GIT_COMMIT_ID", + "value":"#{SourceVariables.CommitId}", + "type":"PLAINTEXT" + } + ] + InputArtifacts: + - Name: Source + OutputArtifacts: + - Name: BuildOutput + RunOrder: 1 + # Deploy the service to the ECS/Fargate cluster + - Name: Deploy + Actions: + - Name: Deploy + ActionTypeId: + Category: Deploy + Owner: AWS + Version: 1 + Provider: CloudFormation + Configuration: + ActionMode: CREATE_UPDATE + RoleArn: !GetAtt CloudFormationDeployRole.Arn + StackName: !Sub ${EnvironmentName}-DatabaseService + TemplatePath: !Sub Source::deployments/services-${DeploymentType}/database.yml + Capabilities: CAPABILITY_IAM + ParameterOverrides: !Sub | + { + "EnvironmentName": "${EnvironmentName}", + "ImageUrl": { + "Fn::GetParam" : ["BuildOutput", "build.json", "ImageUri"] + } + } + InputArtifacts: + - Name: Source + - Name: BuildOutput + +Outputs: + PipelineServerUrl: + Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineServer} + PipelineCacheUrl: + Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineCache} + PipelineDatabaseUrl: + Value: !Sub https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${PipelineDatabase} \ No newline at end of file diff --git a/deployments/pipeline.yml b/deployments/pipeline.yml index 5400248..86daa5a 100644 --- a/deployments/pipeline.yml +++ b/deployments/pipeline.yml @@ -19,16 +19,16 @@ Resources: # Create ECR respositories to hold built docker images ServerRepository: Type: AWS::ECR::Repository - DeletionPolicy: Retain - UpdateReplacePolicy: Retain + Properties: + RepositoryName: !Sub ${EnvironmentName}-server CacheRepository: Type: AWS::ECR::Repository - DeletionPolicy: Retain - UpdateReplacePolicy: Retain + Properties: + RepositoryName: !Sub ${EnvironmentName}-cache DatabaseRepository: Type: AWS::ECR::Repository - DeletionPolicy: Retain - UpdateReplacePolicy: Retain + Properties: + RepositoryName: !Sub ${EnvironmentName}-database # A role used to give CodeBuild permission to access code, # build it, and upload the build results to ECR @@ -161,8 +161,8 @@ Resources: # While the build is in progress we need a place to store artifacts ArtifactBucket: Type: AWS::S3::Bucket - DeletionPolicy: Retain - UpdateReplacePolicy: Retain + Properties: + BucketName: !Sub ${EnvironmentName}-${AWS::AccountId} # This is the definition of how to build the code in the repository CodeBuildProject: @@ -170,51 +170,15 @@ Resources: Properties: Artifacts: Type: CODEPIPELINE - Source: - Type: CODEPIPELINE - BuildSpec: | - version: 0.2 - phases: - pre_build: - commands: - - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REGISTRY - - TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8) - - IMAGE_SERVER_URI=$REPO_SERVER_URI:$TAG - - IMAGE_CACHE_URI=$REPO_CACHE_URI:$TAG - - IMAGE_DB_URI=$REPO_DB_URI:$TAG - build: - commands: - - docker build -t $IMAGE_SERVER_URI -f server.Dockerfile . - - docker build -t $IMAGE_CACHE_URI -f cache.Dockerfile . - - docker build -t $IMAGE_DB_URI -f database.Dockerfile . - post_build: - commands: - - docker push $IMAGE_SERVER_URI - - docker push $IMAGE_CACHE_URI - - docker push $IMAGE_DB_URI - - printf '{"ImageServerUri":"%s", "ImageCacheUri":"%s", "ImageDatabaseUri":"%s"}' $IMAGE_SERVER_URI $IMAGE_CACHE_URI $IMAGE_DB_URI > build.json - artifacts: - files: build.json + ServiceRole: !Ref CodeBuildServiceRole Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/standard:5.0 Type: LINUX_CONTAINER PrivilegedMode: true - EnvironmentVariables: - - Name: AWS_DEFAULT_REGION - Value: !Ref AWS::Region - # - Name: AWS_ACCOUNT_ID - # Value: !Ref AWS::AccountId - - Name: REGISTRY - Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com - - Name: REPO_SERVER_URI - Value: !Sub ${ServerRepository.RepositoryUri} - - Name: REPO_CACHE_URI - Value: !Sub ${CacheRepository.RepositoryUri} - - Name: REPO_DB_URI - Value: !Sub ${DatabaseRepository.RepositoryUri} - Name: !Ref AWS::StackName - ServiceRole: !Ref CodeBuildServiceRole + Source: + Type: CODEPIPELINE + BuildSpec: deployments/buildspec/services-all.yml # This pipeline defines the steps to build, deploy, and release the application Pipeline: @@ -349,6 +313,34 @@ Resources: Provider: CodeBuild Configuration: ProjectName: !Ref CodeBuildProject + EnvironmentVariables: !Sub | + [ + { + "name":"AWS_DEFAULT_REGION", + "value":"${AWS::Region}", + "type":"PLAINTEXT" + }, + { + "name":"AWS_ACCOUNT_ID", + "value":"${AWS::AccountId}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_SERVER_URI", + "value":"${ServerRepository.RepositoryUri}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_CACHE_URI", + "value":"${CacheRepository.RepositoryUri}", + "type":"PLAINTEXT" + }, + { + "name":"REPO_DB_URI", + "value":"${DatabaseRepository.RepositoryUri}", + "type":"PLAINTEXT" + } + ] InputArtifacts: - Name: Source OutputArtifacts: