Technical deep dives

Technical deep dive · Health Promotion Board · CI/CD

Bamboo to GitLab CI/CD

Rebuilding delivery around environment-specific runners, reusable scripts and the checks required for an AKS deployment.

I was the main engineer for this migration at Health Promotion Board, Singapore, during my Thoughtworks engagement. This page explains the implementation behind the HPB case study .

EnvironmentsSIT · UAT · PRD
Delivery platformBamboo → GitLab CI/CD
OutcomeMigration completed

01 / Environment preparation

Prepare the place where the jobs run.

The migration began with access and runner-host preparation. Each environment had one runner VM in a designated network segment, managed through VPN and a bastion host.

Management access and runner allocation
Management accessVPNBastion host

Manage the designated hosts

SIT1 runner VM
UAT1 runner VM
PRD1 runner VM
Host automation
I wrote Ansible scripts to manage host creation, software installation and updates. I prepared the required users and permissions, then registered the hosts as GitLab runners.
Network and identity dependencies
I prepared the NSG connectivity, registry access permissions and allowlists, and managed identity setup needed by the delivery environment.
Execution across access boundaries
I prepared and coordinated the work. The client’s support teams executed operations that were outside our team’s permitted access.

02 / Script structure

Preserve the tasks. Rework how they fit together.

I reviewed the Bamboo steps, their dependencies and their expected results. This established which tasks had to remain when the workflow moved to GitLab CI/CD.

Some inherited scripts mixed several responsibilities. I rewrote most of them, separating common functionality from task-specific work and building reusable GitLab steps around those boundaries.

a. Shared functionality

Reusable scripts and templates

I reused the Helm deployment script across SIT, UAT and production. HPB also provided shared scripts for tasks such as image scanning, which I integrated into the pipelines.

b. Specific responsibilities

Focused task scripts

Work specific to a particular task stayed separate, making the script structure clearer and easier to follow.

I also adapted the environment automation and security-scanning integration. The pipeline, its scripts and the hosts running them needed to work together in the migrated environment.

03 / SIT pipeline

From a pull request to a checked deployment.

Creating a pull request triggered the SIT pipeline automatically. The deployment counted as successful after every stage passed, including the checks after the Helm deployment.

SIT delivery sequence
TriggerCreate a pull requestStarts the SIT pipeline automatically

Check source and build the package

  1. 01Static code scanning
  2. 02Dependency and vulnerability scanning
  3. 03Build application package

Build, check and publish the image

  1. 04Build container image
  2. 05Scan image
  3. 06Push image to registry

Deploy and check the running application

  1. 07Create Helm chart
  2. 08Deploy through Helm to SIT AKS
  3. 09Check application liveness and status
  4. 10Run DAST

All stages pass

SIT deployment successful

Scanning at different points in the workflow

Fortify on Demand (FOD) provided the SAST/DAST integration. The workflow also used other scanning tools, including SonarQube. Code, dependency and image checks had separate places in the sequence; DAST followed deployment and application-status checks.

04 / Environment differences

The same release source, built in each environment.

Access between environments was strictly restricted. SIT, UAT and PRD each built and deployed from the same code for a given release version. We did not promote one immutable package or image through all three environments.

Release model across environments
Same code for the same release versionUsed separately in each environment

SIT

Build and deploy separately

Automatic deployment

UAT

Build and deploy separately

Manual confirmation

PRD

Build and deploy separately

Manual confirmation

Migration order: I completed and validated the SIT/UAT migration work before progressing PRD, reusing the work already prepared. This migration sequence was separate from the release controls shown above.

05 / Validation & outcome

Complete the required checks and integration.

All stages had to pass for a SIT deployment to count as successful. The wider migration was completed after the environment, access and scanning dependencies were resolved and the required validation passed.

Delivery responsibilities and cross-team coordination

06 / Technical questions

Build on the implementation walkthrough.

The questions below clarify the release model and deployment checks, then explore how I would separate shared deployment logic from environment settings. Further questions focus on script behaviour, repeatable builds and failure handling.

Build and deployment model

Did the environments deploy the same image?

My answer

Each environment built and deployed separately from the same code for a release version. The environment model shows these separate paths and the automatic or manual release controls.

Post-deployment checks

Was completing the Helm deployment enough to call the SIT deployment successful?

My answer

No. Application liveness and status checks followed Helm deployment, then DAST ran against the deployed application. Every stage in the SIT sequence had to pass.

Design discussion / Reusable deployment logic

How would you design a deployment script that can be reused across SIT, UAT and production?

My proposed design

I would put the common deployment steps in one shared script. I would pass in each environment’s settings, such as the cluster, namespace and Helm values. Non-secret settings could come from GitLab variables or configuration files. I would read credentials from the CI/CD variables configured in GitLab.

Each environment’s pipeline would mainly control the job order and deployment rules. This would let me reuse the deployment logic while keeping environment settings separate.

This is my current design approach. The project example confirms shared Helm deployment scripts; it does not establish their original configuration or credential-loading mechanism.

Technical follow-ups · Answers in preparation

The next topics will examine the mechanisms behind the workflow.

  • How would you make Ansible host preparation safe to repeat, including recovery from a partially completed run?
  • Can you walk through a shared script’s inputs, outputs and failure reporting, and how its calling pipeline step handles that failure?
  • Which build inputs could cause environments to produce different artifacts from the same source, and how would you investigate a difference?
  • If image upload succeeded but deployment or a later check failed, how would you narrow down the failing layer and verify the application’s actual state?

The decisions behind the implementation

The engineering judgment discussion explores script responsibilities, environment constraints and migration ownership, with further challenges to develop.

Prepared on from my project account and retained working notes. AI assisted with editing and visual presentation.

HPB case study