Case study · PT Telkom Indonesia Tbk
Dynamic EC2 Provisioning with Golden AMIs
Before the platform went multi-tenant, every client got their own EC2 deployment. Making that repeatable meant the image had to stop being assembled by hand at deploy time.
Context
This is the era before the multi-tenant ECS migration. The product ran one dedicated EC2 deployment per client, and onboarding a new client meant provisioning new infrastructure from scratch, an instance stood up, an OS configured, a runtime and dependencies installed, and the application laid on top, largely by hand.
As the client base grew, that per-client setup was being repeated often enough that its cost stopped being incidental and started being the bottleneck.
Challenge
Deployments took roughly 40 minutes, and most of that was spent doing the same setup work over and over on a fresh instance: package installation, dependency downloads, configuration, and the manual verification steps in between, each an opportunity for a fresh deployment to drift subtly from the last one.
Every one of those 40 minutes was work that had already been done, verified, and then done again on the last deployment. When the exact same install and configuration steps run on every rollout, that isn't deployment work anymore, it's setup work sitting in the wrong place. Move it into the image once, verify it once, and it stops costing anything on every deploy after that.
Action
Split the work into two phases: bake once, provision many. Anything identical across clients moved into the image; anything client-specific stayed in provisioning.
Build the golden image
An AMI builder pipeline produces a pre-baked image with the OS hardening, runtime, and dependencies already installed and verified.
Provision infrastructure with Terraform
Instances, networking, and supporting AWS resources declared as code rather than created by hand per client.
Configure with Ansible
Client-specific configuration applied on top of the golden image, the layer that genuinely differs between deployments.
Drive it from Bitbucket CI
The whole sequence runs from the pipeline, so a deployment is a triggered job rather than a person following a runbook.
Tech Stack
The AMI Builder pipeline produces the golden image itself, OS hardening, runtime, and dependencies baked in and verified once rather than re-verified on every deploy. Terraform provisions everything around it, instances, networking, and supporting AWS resources, as code instead of by hand. Ansible applies the one layer that's genuinely client-specific on top of that shared image. Bitbucket Pipelines ties the whole sequence together, so a deployment is a single triggered job instead of someone working through a runbook step by step.
Results
Deployment time dropped from roughly 40 minutes to about 10, and the remaining ten minutes were mostly waiting on infrastructure provisioning rather than performing manual steps. The manual verification checklist that used to catch, or miss, drift between deployments went away along with it, because there was no longer a place for drift to enter: the image was verified once at build time instead of being re-verified by hand on every rollout. The deeper effect was that client environments became reproducible, not just similar: two deployments from the same AMI and the same Terraform state start identical.
Reproducibility isn't a nice-to-have here, it's a prerequisite for everything that came after it. Consolidating client environments onto shared infrastructure, the move this work quietly set up, only works if those environments are identical to begin with. You can't merge deployments that already drifted apart from each other; you can only merge deployments that were built the same way from the start. That's what this work actually bought, not just a faster deploy, but the identical baseline the next migration needed to even be possible.