AWSTemplateFormatVersion: "2010-09-09" Transform: 'AWS::Serverless-2016-10-31' Parameters: ArtifactBucket: Type: String Description: S3 Bucket containing the artifacts ArtifactPrefix: Type: String Description: S3 Bucket Prefix for artifacts ServiceName: Type: String Description: Service name used for deployment # # TODO: Multi-account support # TestAccountId: # Type: String # Description: Account ID for the test environment # StagingAccountId: # Type: String # Description: Account ID for the staging environment # ProdAccountId: # Type: String # Description: Account ID for the prod environment CodeRepositoryName: Type: String Description: Name of the Repository CodeRepositoryArn: Type: String Description: Code Repository ARN Resources: ######### # ROLES # ######### CodeBuildRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: TestsProjectPolicy PolicyDocument: Version: "2012-10-17" Statement: # Grant access to CloudWatch Logs - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" # Grant permission to read from the artifact bucket - Effect: Allow Action: - s3:GetObject* Resource: !Sub "arn:${AWS::Partition}:s3:::${ArtifactBucket}/*" # Grant access to SSM parameters in tests - Effect: Allow Action: ssm:GetParameter Resource: - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ecommerce/tests/*" - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ecommerce/staging/*" # Grant permission to invoke API Gateway - Effect: Allow Action: - execute-api:Invoke Resource: "*" # Grant permission to read/write to DynamoDB - Effect: Allow Action: - dynamodb:GetItem - dynamodb:DeleteItem - dynamodb:PutItem - dynamodb:Scan - dynamodb:Query - dynamodb:UpdateItem - dynamodb:BatchWriteItem - dynamodb:BatchGetItem - dynamodb:DescribeTable - dynamodb:ConditionCheckItem Resource: "*" # Grant permission to read SQS - Effect: Allow Action: - sqs:ChangeMessageVisibility - sqs:ChangeMessageVisibilityBatch - sqs:DeleteMessage - sqs:DeleteMessageBatch - sqs:GetQueueAttributes - sqs:ReceiveMessage Resource: "*" # Grant permission to perform admin operations on Cognito User Pools - Effect: Allow Action: - cognito-idp:Admin* - cognito-idp:CreateUserPoolClient - cognito-idp:DeleteUserPoolClient Resource: "*" # Grant permission to put events on EventBridge - Effect: Allow Action: - events:PutEvents Resource: "*" # Grant permissions for AppSync - Effect: Allow Action: - appsync:CreateApiKey - appsync:DeleteApiKey Resource: "*" # Grant permissions for invoking Lambda functions - Effect: Allow Action: - lambda:InvokeFunction Resource: "*" # Grant access to CodeBuild reports - Effect: Allow Action: - codebuild:CreateReportGroup - codebuild:CreateReport - codebuild:UpdateReport - codebuild:BatchPutTestCases # TODO: scope down permission Resource: !Sub "arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/*" ############ # PIPELINE # ############ PipelineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: codepipeline.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: !Sub "${AWS::StackName}-PipelinePolicy" PolicyDocument: Version: "2012-10-17" Statement: # Grant access to the artifact bucket - Effect: Allow Action: - s3:GetBucketVersioning Resource: !Sub "arn:${AWS::Partition}:s3:::${ArtifactBucket}" - Effect: Allow Action: - s3:GetObject* - s3:PutObject - s3:PutObjectVersion Resource: !Sub "arn:${AWS::Partition}:s3:::${ArtifactBucket}/*" # Block write actions to the templates - Effect: Deny NotAction: - s3:GetObject* Resource: !Sub "arn:${AWS::Partition}:s3:::${ArtifactBucket}/${ArtifactPrefix}*" # Grant permission to access the repository - Effect: Allow Action: - codecommit:GetBranch - codecommit:GetCommit - codecommit:UploadArchive - codecommit:GetUploadArchiveStatus - codecommit:CancelUploadArchive Resource: !Ref CodeRepositoryArn # Grant permission to assume deployment roles - Effect: Allow Action: - iam:PassRole Resource: - !GetAtt TestsEnvironment.Outputs.RoleArn - !GetAtt StagingEnvironment.Outputs.RoleArn - !GetAtt ProdEnvironment.Outputs.RoleArn # Allow describing CloudFormation stacks - Effect: Allow Action: - cloudformation:CreateChangeSet - cloudformation:DeleteChangeSet - cloudformation:DescribeStacks - cloudformation:DescribeChangeSet - cloudformation:ExecuteChangeSet Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/ecommerce-*" # Allow starting a tests build - Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild Resource: !GetAtt TestsProject.Arn # Allow starting a staging build - Effect: Allow Action: - codebuild:BatchGetBuilds - codebuild:StartBuild Resource: !GetAtt StagingProject.Arn Pipeline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Type: S3 Location: !Ref ArtifactBucket RoleArn: !GetAtt PipelineRole.Arn Stages: ########## # SOURCE # ########## - Name: source Actions: - Name: source-s3 ActionTypeId: Category: Source Provider: S3 Owner: AWS Version: "1" Configuration: S3Bucket: !Ref ArtifactBucket S3ObjectKey: !Sub "${ArtifactPrefix}${ServiceName}.zip" OutputArtifacts: - Name: TemplateArtifact RunOrder: 1 - Name: source-repository ActionTypeId: Category: Source Provider: CodeCommit Owner: AWS Version: "1" Configuration: RepositoryName: !Ref CodeRepositoryName BranchName: main PollForSourceChanges: false OutputArtifacts: - Name: CodeArtifact RunOrder: 2 ######### # TESTS # ######### - Name: tests Actions: - Name: create-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_REPLACE StackName: !Sub "ecommerce-tests-${ServiceName}" ChangeSetName: !Sub "ecommerce-tests-${ServiceName}" Capabilities: CAPABILITY_IAM,CAPABILITY_AUTO_EXPAND RoleArn: !GetAtt TestsEnvironment.Outputs.RoleArn TemplateConfiguration: "TemplateArtifact::config.tests.json" TemplatePath: "TemplateArtifact::template.yaml" InputArtifacts: - Name: TemplateArtifact RunOrder: 1 - Name: execute-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_EXECUTE StackName: !Sub "ecommerce-tests-${ServiceName}" ChangeSetName: !Sub "ecommerce-tests-${ServiceName}" RoleArn: !GetAtt TestsEnvironment.Outputs.RoleArn RunOrder: 2 - Name: tests-integration ActionTypeId: Category: Test Provider: CodeBuild Owner: AWS Version: "1" Configuration: ProjectName: !Ref TestsProject InputArtifacts: - Name: CodeArtifact RunOrder: 3 # ########### # # STAGING # # ########### - Name: staging Actions: - Name: create-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_REPLACE StackName: !Sub "ecommerce-staging-${ServiceName}" ChangeSetName: !Sub "ecommerce-staging-${ServiceName}" Capabilities: CAPABILITY_IAM,CAPABILITY_AUTO_EXPAND RoleArn: !GetAtt StagingEnvironment.Outputs.RoleArn TemplateConfiguration: "TemplateArtifact::config.staging.json" TemplatePath: "TemplateArtifact::template.yaml" InputArtifacts: - Name: TemplateArtifact RunOrder: 1 - Name: execute-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_EXECUTE StackName: !Sub "ecommerce-staging-${ServiceName}" ChangeSetName: !Sub "ecommerce-staging-${ServiceName}" RoleArn: !GetAtt StagingEnvironment.Outputs.RoleArn RunOrder: 2 - Name: tests-e2e ActionTypeId: Category: Test Provider: CodeBuild Owner: AWS Version: "1" Configuration: ProjectName: !Ref StagingProject InputArtifacts: - Name: CodeArtifact RunOrder: 3 # ######## # # PROD # # ######## - Name: prod Actions: - Name: create-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_REPLACE StackName: !Sub "ecommerce-prod-${ServiceName}" ChangeSetName: !Sub "ecommerce-prod-${ServiceName}" Capabilities: CAPABILITY_IAM,CAPABILITY_AUTO_EXPAND RoleArn: !GetAtt ProdEnvironment.Outputs.RoleArn TemplateConfiguration: "TemplateArtifact::config.prod.json" TemplatePath: "TemplateArtifact::template.yaml" InputArtifacts: - Name: TemplateArtifact RunOrder: 1 - Name: execute-changeset ActionTypeId: Category: Deploy Provider: CloudFormation Owner: AWS Version: "1" Configuration: ActionMode: CHANGE_SET_EXECUTE StackName: !Sub "ecommerce-prod-${ServiceName}" ChangeSetName: !Sub "ecommerce-prod-${ServiceName}" RoleArn: !GetAtt ProdEnvironment.Outputs.RoleArn RunOrder: 2 ######### # TESTS # ######### TestsEnvironment: Type: AWS::CloudFormation::Stack Properties: TemplateURL: service-pipeline-environment.yaml Parameters: ServiceName: !Ref ServiceName Environment: tests TestsProject: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Environment: ComputeType: BUILD_GENERAL1_SMALL EnvironmentVariables: - Name: SERVICE_NAME Value: !Ref ServiceName - Name: ENVIRONMENT Value: tests Image: aws/codebuild/standard:3.0 Type: LINUX_CONTAINER ServiceRole: !GetAtt CodeBuildRole.Arn Source: BuildSpec: pipeline/resources/buildspec-tests.yaml Type: CODEPIPELINE TestsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/codebuild/${TestsProject}" # ########### # # STAGING # # ########### StagingEnvironment: Type: AWS::CloudFormation::Stack Properties: TemplateURL: service-pipeline-environment.yaml Parameters: ServiceName: !Ref ServiceName Environment: staging StagingProject: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Environment: ComputeType: BUILD_GENERAL1_SMALL EnvironmentVariables: - Name: SERVICE_NAME Value: !Ref ServiceName - Name: ENVIRONMENT Value: staging Image: aws/codebuild/standard:3.0 Type: LINUX_CONTAINER ServiceRole: !GetAtt CodeBuildRole.Arn Source: BuildSpec: pipeline/resources/buildspec-staging.yaml Type: CODEPIPELINE StagingLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/codebuild/${StagingProject}" # ######## # # PROD # # ######## ProdEnvironment: Type: AWS::CloudFormation::Stack Properties: TemplateURL: service-pipeline-environment.yaml Parameters: ServiceName: !Ref ServiceName Environment: prod