A CI/CD (Continuous Integration/Continuous Deployment) pipeline is a set of automated processes that facilitate the continuous integration and delivery of software applications.
This is a short write-up that provides a quick codepipeline setup in AWS cdk for automating code deployment anytime code is pushed to a github repository.
import "source-map-support/register";import * as cdk from "aws-cdk-lib";import { Construct } from "constructs";import * as s3 from "aws-cdk-lib/aws-s3";import * as sns from "aws-cdk-lib/aws-sns";import * as iam from "aws-cdk-lib/aws-iam";import * as codebuild from "aws-cdk-lib/aws-codebuild";import * as targets from "aws-cdk-lib/aws-events-targets";import * as codepipeline from "aws-cdk-lib/aws-codepipeline";import * as subscription from "aws-cdk-lib/aws-sns-subscriptions";import * as codepipeline_actions from "aws-cdk-lib/aws-codepipeline-actions";class PipelineStack extends cdk.Stack {constructor(scope: Construct, id: string, props?: cdk.StackProps) {super(scope, id, props);const env = "dev" // Specify the environment based on your organizations requirements.// Initialize your application stack herenew YourAppStack(this, "your-app-stack-name", {});const buildEnvironment: codebuild.BuildEnvironment = {buildImage:codebuild.LinuxBuildImage.fromDockerRegistry("node:18.18.2-slim"),};// Create a new pipeline roleconst pipelineRole = new iam.Role(this, `PipelineRole-${env}`, {assumedBy: new iam.ServicePrincipal("codebuild.amazonaws.com"),roleName: `PipelineRole-${env}`,});// Attach relevant policiespipelineRole.addToPolicy(new iam.PolicyStatement({actions: ["cloudformation:*","ssm:*","iam:*","lambda:InvokeFunction",],resources: ["*"],}));pipelineRole.addToPolicy(new iam.PolicyStatement({actions: ["sts:AssumeRole"],resources: ["*"],}));// Add your github auth token to AWS secrets manager and grant codebuild access.new codebuild.GitHubSourceCredentials(this, "GithubCredentials", {accessToken: cdk.SecretValue.secretsManager("name-of-secret"), // replace with the name of your secret.});// Create a github source connection for codebuildconst gitHubSource = codebuild.Source.gitHub({owner: "repo-owner", // replace with your github owner name.repo: "repo-name", // replace with your github repo name.cloneDepth: 1,webhook: true,branchOrRef: env,reportBuildStatus: true,webhookFilters: [// Specify the events you want the build to be triggered for.codebuild.FilterGroup.inEventOf(codebuild.EventAction.PULL_REQUEST_CREATED,codebuild.EventAction.PULL_REQUEST_REOPENED,codebuild.EventAction.PULL_REQUEST_UPDATEDcodebuild.EventAction.PULL_REQUEST_MERGED),],});// Create a new codebuild project. Codebuild provides a build environment to build your project.const project = new codebuild.Project(this, `CodebuildProject-${env}`, {projectName: `CodebuildProject-${env}`,source: gitHubSource,description: "project-description", // replace with your project description.environment: buildEnvironment,role: pipelineRole,concurrentBuildLimit: 1, // this ensures that you only have one build running per time to prevent multiple stack updates.buildSpec: codebuild.BuildSpec.fromObject({version: "0.2",phases: {// Specify your pre-build commands herepre_build: {commands: [],},// Specify your build commands herebuild: {commands: [],},},}),});// Create a S3 bucket that codepipeline would use to store information regarding your buildsconst artifactBucket = new s3.Bucket(scope, `ArtifactBucket-${env}`, {versioned: true,removalPolicy: cdk.RemovalPolicy.DESTROY,});// Create a new codepipeline to organize your build processconst pipeline = new codepipeline.Pipeline(scope, `PipelineName-${env}`, {pipelineName: `PipelineName-${env}`,artifactBucket,});// Grant codepipeline access to your github repositoryconst githubAction = new codepipeline_actions.GitHubSourceAction({actionName: "GitHubSource",output: new codepipeline.Artifact(),oauthToken: cdk.SecretValue.secretsManager("name-of-secret"), // replace with the name of your secret.owner: "repo-owner", // replace with your github owner name.repo: "repo-name", // replace with your github repo name.branch: env,trigger: codepipeline_actions.GitHubTrigger.WEBHOOK, // This enables github to setup webhooks that reference codepipeline which are triggered whenever a push is made to the target repository.});// Attach your codebuild project to the pipelineconst buildAction = new codepipeline_actions.CodeBuildAction({actionName: "BuildAction",project,input: githubAction.actionProperties.outputs?.[0] as codepipeline.Artifact,});// Setup your pipeline stages. First stage is to connect to github and the second is to trigger the build.pipeline.addStage({stageName: "Source",actions: [githubAction],});pipeline.addStage({stageName: "Build",actions: [buildAction],});// Add this section if you want to send build status reports to a lambda function.const buildLambda = new lambda.Function(this, "PipelineBuildLambda", {runtime: lambda.Runtime.NODEJS_18_X,handler: "index.handler",code: lambda.Code.fromAsset("path-to-function-code"),});const snsTopic = new sns.Topic(this, `CodeBuildNotifications-${env}`, {topicName: `CodeBuildNotifications-topic-${env}`,displayName: `CodeBuildNotifications-${env}`,});snsTopic.grantPublish(project);snsTopic.grantPublish(buildLambda);snsTopic.addSubscription(new subscription.LambdaSubscription(buildLambda));project.onBuildStarted("BuildStarted", {description: "Rule to send notification on build start",}).addTarget(new targets.SnsTopic(snsTopic));project.onBuildFailed("BuildFailed", {description: "Rule to send notification on unsuccessful build",}).addTarget(new targets.SnsTopic(snsTopic));project.onBuildSucceeded("BuildSucceded", {description: "Rule to send notification on successful build",}).addTarget(new targets.SnsTopic(snsTopic));}}
I have added relevant comments to explain each step. Feel free to use on your next project.
Whenever you push code or raise a pull request against your target repository, you should see your build trigger on AWS Codebuild.
There you have it folks.
If you need help or have questions, you can reach me on Twitter or shoot me an email at david.ajayi.anu@gmail.com.
Thanks for reading! 🙂