Reputation: 493
I currently have a "master.yaml" template that runs "service-a.yaml" and "service-b.yaml" then "service-c.yaml" which relies on outputs from service-a and service-b.
Is there a way to break this nested stack into multiple nested stacks? That way when something deep inside "service-c" fails it doesn't cause a rollback all the way up the chain? I want to kick off A+B in parallel and then C when they are finished in an automated fashion.
I could have a master.yaml which builds "service-a" and "service-b" then manually kick off "service-c" when they're done but I would like to automate this somehow?
Upvotes: 0
Views: 729
Reputation: 1891
You can create a stack with Codebuild project and Codepipeline (Basically performing CI/CD) to trigger one stack after the other and thus each stack would fail and roll back separately.
For example the cloudformation template would have a Codebuld project as follows
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/python:3.6.5
Type: LINUX_CONTAINER
EnvironmentVariables:
- Name: bucket
Value: !Ref ArtifactStoreBucket
Type: PLAINTEXT
- Name: prefix
Value: build
Type: PLAINTEXT
Name: !Ref AWS::StackName
ServiceRole: !Ref CodeBuildRole
Source:
Type: CODEPIPELINE
BuildSpec: stack/buildspec.yaml
Tags:
- Key: owner
Value: !Ref StackOwner
- Key: task
Value: !Ref RepositoryName
In the buildspec.yaml file, you can package the cloudfromation templates as follows:
- aws cloudformation package --template-file master.yaml
--s3-bucket $bucket --s3-prefix $prefix
--output-template-file master-template.yaml
- aws cloudformation package --template-file service-a.yaml
--s3-bucket $bucket --s3-prefix $prefix
--output-template-file service-a-template.yaml
And finally, a codepipeline stage which links all together. For example in the below-provided snippet, you can have source code triggered by codecommit. So every push to the repository would build your pipeline automatically.
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref ArtifactStoreBucket
Type: S3
DisableInboundStageTransitions: []
Name: !Sub "${AWS::StackName}"
RoleArn: !GetAtt [PipelineRole, Arn]
Stages:
# Stage 1 - CodeUpdate Stage
- Name: CodeUpdate
Actions:
- Name: SourceCodeUpdate
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: CodeCommit
OutputArtifacts:
- Name: SourceCode
Configuration:
PollForSourceChanges: 'false'
RepositoryName: !Ref RepositoryName
BranchName: !Ref BranchName
RunOrder: '1'
# Stage 2 - Build Stage
- Name: Build
Actions:
- Name: UpdateLambda
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
InputArtifacts:
- Name: SourceCode
OutputArtifacts:
- Name: BuildArtifact
Configuration:
ProjectName: !Ref 'CodeBuildProject'
RunOrder: '1'
# Stage 3 - Build master stack
- Name: MasterSetup
Actions:
- Name: CreateMasterChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
InputArtifacts:
- Name: BuildArtifact
Configuration:
ActionMode: CHANGE_SET_REPLACE
StackName: !Sub "${AWS::StackName}-master"
ChangeSetName: !Sub "${AWS::StackName}-master-update"
RoleArn: !GetAtt [CFNRole, Arn]
TemplatePath: BuildArtifact::master-template.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: !Sub
- |
{
"MasterStack": "${w}",
"StackOwner": "${x}",
"Task": "${y}"
}
- {
w: !Sub '${AWS::StackName}',
x: !Sub '${StackOwner}',
y: !Sub '${RepositoryName}'
}
RunOrder: '1'
- Name: ExecuteMasterChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
StackName: !Sub "${AWS::StackName}-master"
ChangeSetName: !Sub "${AWS::StackName}-master-update"
RunOrder: '2'
# Stage 4 - Build service-a stack
- Name: ServiceASetup
Actions:
- Name: CreateServiceAChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
InputArtifacts:
- Name: BuildArtifact
Configuration:
ActionMode: CHANGE_SET_REPLACE
StackName: !Sub "${AWS::StackName}-service-a"
ChangeSetName: !Sub "${AWS::StackName}-service-a-update"
RoleArn: !GetAtt [CFNRole, Arn]
TemplatePath: BuildArtifact::service-a-template.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: !Sub
- |
{
"MasterStack": "${w}",
"StackOwner": "${x}",
"Task": "${y}"
}
- {
w: !Sub '${AWS::StackName}',
x: !Sub '${StackOwner}',
y: !Sub '${RepositoryName}'
}
RunOrder: '1'
- Name: ExecuteServiceAChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
StackName: !Sub "${AWS::StackName}-service-a"
ChangeSetName: !Sub "${AWS::StackName}-service-a-update"
RunOrder: '2'
If you want to have stacks executing in parallel, you can add more than 1 stack in each stage.
Obviously you need to setup the roles and buckets yourself and this should give you basic idea how to get started.
For more information, you can read up more about codepipeline as follows: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cd-pipeline.html https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html
Upvotes: 1