Introduction
AWS CodePipeline helps in orchestrating CI/CD pipeline all the way from building and testing to deploying applications, from development to production environments.
However, one of the limitations of AWS CodePipeline is its inability to dynamically execute or skip any pipeline action.
This post demonstrates how to setup a CI/CD pipeline to dynamically orchestrate and execute actions based on inputs
AWS Services
This solution uses the following AWS services:
- AWS CodeCommit – Source Control
- AWS CodeBuild – Build System
- AWS CodePipeline – Continuous Delivery
- AWS Step Functions - Workflow automation using state machines
Solution Overview
- A change or commit to the code in the CodeCommit application repository triggers CodePipeline with the help of a CloudWatch event.
- The pipeline downloads the code from the CodeCommit repository, initiates the first build using CodeBuild and securely saves the built artifact to an S3 bucket.
- Once build succeeds, pipeline triggers a Step Function Workflow
- Step Function workflow uses artifact from previous build as input and iterates for each item in the input
- For each input, it triggers respective CodeBuild or Pass state using Choice state
- Step Functions will succeed once all invoked builds suceed
Architecture
Steps
CodeCommit
- Created a repo and have a file "sample_source.json" with below content
{"services":["service_A", "service_B"]}
CodeBuild
First CodeBuild
- It copies source file and generates ABC.json which is passed as output artifact and consumed by next Step Function
- This is optional when codebuild can generate a similar json file with list of services to be passed as input to step functions
- BuildSpec
ServiceA and ServiceB CodeBuild
- These are the actual builds invoked by step functions
- ServiceA BuildSpec
- ServiceB BuildSpec
Step Functions
- The artifact passed as json file will be received as json input by step functions state machine
- It receives the input using a "Map" state which supprts iteration
- Within iterator, "Choice" state is used to validate the values and invoke respective "Task" or "Pass" state
- The "Task" state in our scenario will invoke a CodeBuild task using "Optimized Integrations"
- CodeBuild task supports only sync invocation which means the next state following CodeBuild execution has to wait until it finishes.
- However, since the iterator uses a "Choice" state, each iteration is independent and happens in parallel
- In this example, for "ServiceA" and "ServiceB", CodeBuild is executed and "ServiceC" is set as a "pass" state
Note: Ensure enough permissions are granted for StepFunctions IAM role to make relevant service invocations
StepFunction IAM permissions for CodeBuild
CodePipeline
- Once all the above steps are done, all we need to do is to create pipeline in CodePipeline
- Create Source stage using any SCM, I used CodeCommit
- Create a Build stage using existing "First Codebuild" build project
- Create next "Step Sunctions" stage with following params
- Action Provider: Step Functions
- Input artifacts: Output Artifact from previous "First CodeBuild"
- State machine ARN: Choose the recently created stateMachine ARN
- Input type: File Path
- Input: ABC.json (use whatever file is sent in output artifact from previous build)
Execution
- Release a change to test the pipeline
- In this example, if services list changes in "sample_source.json", step functions will execute only those states
Conclusion
- This solution demonstrated the invocation of Step Functions from CodePipeline and Step Functions execution executing builds in CodeBuild dynamically based upon the received input
Top comments (5)
Good one Muthu, eagerly waiting for more articles sharing your vast experience in AWS.
One doubt on this, can you share a use case where we need couple of build levels (one at CodePipeline level and another at CodeBuild triggered by Step Function)?
Hi Ashok, CI pipelines are predefined and actions defined are static. With just codepipeline, every stage would execute as it is defined. A shared repo which has pipeline setup to do more than one build based on folder paths within the repo, all folders would get built or deployed even when files in one or few folders are updated. Step Functions can help in such cases to skip or execute stages conditionally
Something like this Ex: 1
Will also help in complex pipelines where some stages/actions need to be executed/skipped on conditions and like this Ex: 2
@omuthu Thanks for sharing that! How about the deploy after all builds happen? For example if you have a Blue/Green ECS deployment how does it fit on CodePipeline? Would it have to be detached and called by the lambda function? Keep Rocking!
Great first article! 👍
Thanks Jason