DEV Community

Prakash Rao
Prakash Rao

Posted on

Advanced CI/CD Pipelines on AWS

In this post, I’ll walk you through setting up an advanced CI/CD pipeline on AWS using native services and show you how to create and configure components like CodeBuild, CodeDeploy, and CodePipeline using AWS CLI commands and configuration files. I will also include outputs for each command so you can see what to expect when you run these commands yourself.

Flow Chart

Note:

  1. Flowchart was created using mermaid.live
  2. Since CodeCommit is no longer onboarding new customers, choosing GitHub as the source repository. If you already have a GitHub repository for your project, simply update the configuration files accordingly.

Prerequisites: Please make sure that the AWS CLI is installed and configured with the proper IAM permissions.

1. Create a Source Repository in GitHub

First, let’s create a new GitHub repository where our source code will be there.

git init
git remote add origin https://github.com/<YOUR_GITHUB_USERNAME>/advanced-ci-cd-repo.git
git add .
git commit -m "Initial commit"
git push -u origin main
Enter fullscreen mode Exit fullscreen mode

2. Set Up a CodeBuild Project

Next let's create a CodeBuild project that builds our application. In this example, we’ll use a simple Node.js project. Save the following JSON configuration as codebuild-project.json:

{
  "name": "advanced-ci-cd-build",
  "description": "Build project for advanced CI/CD pipeline demo",
  "source": {
    "type": "GITHUB",
    "location": "https://github.com/<YOUR_GITHUB_USERNAME>/advanced-ci-cd-repo"
  },
  "artifacts": {
    "type": "NO_ARTIFACTS"
  },
  "environment": {
    "type": "LINUX_CONTAINER",
    "image": "aws/codebuild/standard:6.0",
    "computeType": "BUILD_GENERAL1_MEDIUM",
    "environmentVariables": [
      {
        "name": "NODE_ENV",
        "value": "production"
      }
    ]
  },
  "serviceRole": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/CodeBuildServiceRole"
}

Enter fullscreen mode Exit fullscreen mode

Create project with:

aws codebuild create-project --cli-input-json file://codebuild-project.json
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "project": {
    "name": "advanced-ci-cd-build",
    "arn": "arn:aws:codebuild:us-east-1:123456789012:project/advanced-ci-cd-build",
    "description": "Build project for advanced CI/CD pipeline demo",
    "source": {
      "type": "GITHUB",
      "location": "https://github.com/<YOUR_GITHUB_USERNAME>/advanced-ci-cd-repo"
    },
    "artifacts": {
      "type": "NO_ARTIFACTS"
    },
    "environment": {
      "type": "LINUX_CONTAINER",
      "image": "aws/codebuild/standard:6.0",
      "computeType": "BUILD_GENERAL1_MEDIUM",
      "environmentVariables": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ]
    },
    "serviceRole": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/CodeBuildServiceRole",
    "created": "2024-05-10T15:00:00Z"
  }
}

Enter fullscreen mode Exit fullscreen mode

Note: Make sure CodeBuild service role has permissions for GitHub, S3 (if you use artifacts), and CloudWatch logging.

3. Define a Build Specification (buildspec.yml)

Create a buildspec.yml file in the root of the repository. This file instructs CodeBuild on how to run our build.

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 16
    commands:
      - echo "Installing dependencies..."
      - npm install
  pre_build:
    commands:
      - echo "Running lint and tests..."
      - npm run lint
      - npm test
  build:
    commands:
      - echo "Building the project..."
      - npm run build
  post_build:
    commands:
      - echo "Build completed on `date`"
artifacts:
  files:
    - '**/*'
  discard-paths: yes
Enter fullscreen mode Exit fullscreen mode

Push this file to the GitHub repository so CodeBuild uses it during the build process.

4. Set Up a CodeDeploy Application and Deployment Group

For blue/green deployments, we’ll use CodeDeploy.

Create a CodeDeploy Application

Command:

aws deploy create-application \
    --application-name advanced-ci-cd-app \
    --compute-platform Server
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "applicationId": "abcdef12-3456-7890-abcd-ef1234567890",
  "applicationName": "advanced-ci-cd-app",
  "computePlatform": "Server"
}
Enter fullscreen mode Exit fullscreen mode

Create a Deployment Group
Save the following as deployment-group.json (update below values as needed):

{
  "applicationName": "advanced-ci-cd-app",
  "deploymentGroupName": "advanced-ci-cd-deploy-group",
  "deploymentConfigName": "CodeDeployDefault.OneAtATime",
  "ec2TagFilters": [
    {
      "Key": "Name",
      "Value": "MyAppServer",
      "Type": "KEY_AND_VALUE"
    }
  ],
  "serviceRoleArn": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/CodeDeployServiceRole",
  "blueGreenDeploymentConfiguration": {
    "terminateBlueInstancesOnDeploymentSuccess": {
      "action": "TERMINATE",
      "terminationWaitTimeInMinutes": 5
    },
    "deploymentReadyOption": {
      "actionOnTimeout": "CONTINUE_DEPLOYMENT"
    }
  },
  "autoRollbackConfiguration": {
    "enabled": true,
    "events": ["DEPLOYMENT_FAILURE"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Create Deployment group with:

aws deploy create-deployment-group --cli-input-json file://deployment-group.json
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "deploymentGroupId": "abcdef12-3456-7890-abcd-ef1234567890",
  "deploymentGroupName": "advanced-ci-cd-deploy-group",
  "applicationName": "advanced-ci-cd-app"
}
Enter fullscreen mode Exit fullscreen mode

Note: Make sure that the CodeDeploy service role has the necessary permissions to access EC2 and execute deployment actions.

5. Create a CodePipeline

Let's put everything together by creating a pipeline that orchestrates the source, build, and deployment stages.

Save the following as pipeline.json (adjust names, regions, and bucket names as needed):

{
  "pipeline": {
    "name": "advanced-ci-cd-pipeline",
    "roleArn": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/CodePipelineServiceRole",
    "artifactStore": {
      "type": "S3",
      "location": "<YOUR_ARTIFACT_BUCKET>"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [
          {
            "name": "SourceAction",
            "actionTypeId": {
              "category": "Source",
              "owner": "ThirdParty",
              "provider": "GitHub",
              "version": "1"
            },
            "outputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "configuration": {
              "Owner": "<YOUR_GITHUB_USERNAME>",
              "Repo": "advanced-ci-cd-repo",
              "Branch": "main",
              "OAuthToken": "<YOUR_GITHUB_OAUTH_TOKEN>"
            },
            "runOrder": 1
          }
        ]
      },
      {
        "name": "Build",
        "actions": [
          {
            "name": "BuildAction",
            "actionTypeId": {
              "category": "Build",
              "owner": "AWS",
              "provider": "CodeBuild",
              "version": "1"
            },
            "inputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "outputArtifacts": [
              {
                "name": "BuildOutput"
              }
            ],
            "configuration": {
              "ProjectName": "advanced-ci-cd-build"
            },
            "runOrder": 1
          }
        ]
      },
      {
        "name": "Deploy",
        "actions": [
          {
            "name": "DeployAction",
            "actionTypeId": {
              "category": "Deploy",
              "owner": "AWS",
              "provider": "CodeDeploy",
              "version": "1"
            },
            "inputArtifacts": [
              {
                "name": "BuildOutput"
              }
            ],
            "configuration": {
              "ApplicationName": "advanced-ci-cd-app",
              "DeploymentGroupName": "advanced-ci-cd-deploy-group"
            },
            "runOrder": 1
          }
        ]
      }
    ],
    "version": 1
  }
}
Enter fullscreen mode Exit fullscreen mode

Create pipeline with:

aws codepipeline create-pipeline --cli-input-json file://pipeline.json
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "pipeline": {
    "name": "advanced-ci-cd-pipeline",
    "version": 1,
    "artifactStore": {
      "type": "S3",
      "location": "my-artifact-bucket"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [ { ..... } ]
      },
      {
        "name": "Build",
        "actions": [ { ..... } ]
      },
      {
        "name": "Deploy",
        "actions": [ { ..... } ]
      }
    ]
  },
  "created": "2024-05-10T15:30:00Z"
}

Enter fullscreen mode Exit fullscreen mode

6. Testing and Monitoring the Pipeline

After creating the pipeline, trigger a test execution by pushing a commit to your GitHub repository. Then, check the pipeline status with:

aws codepipeline get-pipeline-execution --pipeline-name advanced-ci-cd-pipeline --pipeline-execution-id <execution-id>
Enter fullscreen mode Exit fullscreen mode

Output:

{
  "pipelineExecution": {
    "pipelineName": "advanced-ci-cd-pipeline",
    "pipelineExecutionId": "12345678-1234-1234-1234-123456789012",
    "status": "Succeeded",
    "artifactRevisions": [ ..... ],
    "startTime": "2024-05-10T15:31:00Z",
    "lastUpdateTime": "2024-05-10T15:33:00Z"
  }
}

Enter fullscreen mode Exit fullscreen mode

Additionally, we can set up CloudWatch dashboards and alarms to monitor key metrics such as build duration and deployment status.

Conclusion

In this blog I demonstrated how we can build an advanced CI/CD pipeline on AWS using CLI commands and configuration files. By integrating GitHub, CodeBuild, CodeDeploy (with blue/green deployments), and CodePipeline, we created a fully automated workflow.

Happy Deploying! :)

Top comments (0)