DEV Community

VIREN PATEL
VIREN PATEL

Posted on

Building a Reusable AWS SAM CI/CD Pipeline for Multi-Account Deployments

Introduction & The Problem
I am an Intermediate Software Engineer at one of New Zealand’s global QSR enterprises.

One challenge kept showing up in my day-to-day work: every new AWS SAM project in our team came with yet another deployment pipeline.

Different repositories used different AWS CLI and AWS SAM CLI versions. Some pipelines deployed to a single account, others to multiple accounts. Even small improvements to the deployment process had to be repeated repo by repo.

After maintaining this for a while, I realized we weren’t solving deployment problems anymore. We were solving the same tooling problem over and over again.

I wanted every project to use one reusable deployment engine instead of maintaining dozens of nearly identical pipelines. So, this article is aimed at teams that deploy multiple AWS SAM applications and want to standardize deployments across AWS accounts without duplicating CI/CD logic.

Architecture Overview

Because this article is about solving a repeated deployment problem, I am showing two architecture views:

  1. The common fragmented model many teams start with.
  2. The standardized deployment-engine model I implemented with sam-pipeline.

Architecture 1: Fragmented Team-by-Team Deployments (Current State)

In this model, each repository carries its own deployment logic, tool versions, and account assumptions. It works initially, but maintenance scales badly and drift becomes unavoidable.

Architecture 2: Shared Deployment Engine with sam-pipeline (Solution)

Here, every project uses the same deployment engine. Runtime setup, SAM build/deploy flow, and multi-account behavior are standardized, which reduces duplication and makes deployments more predictable.

Building the Docker Deployment Image

To solve this consistently, I built sam-multi-account-pipeline, a reusable deployment image that teams can pull from Amazon ECR Public.

The idea is simple: developers use a shared public ECR image to deploy AWS SAM stacks across multiple AWS accounts and regions, without rebuilding deployment logic in every repository.

Image: public.ecr.aws/z7q5l7x5/sam-pipeline:1.0.0

Publishing to Amazon ECR Public

I published the image to Amazon ECR Public so anyone can consume it directly in their CI/CD pipeline.

Complete setup and usage instructions are available in the repository:
https://github.com/Viren1993-web/sam-multi-account-pipeline

Multi-Account Deployment with AssumeRole
Here’s how multi-account deployment works in this model:

For every AWS account you want to deploy to, create a deployment role in that account and grant it the permissions required by your SAM template resources.

The pipeline then assumes that role and performs the deployment securely in the target account. In my repository, I’ve included an example using a DeployerGithub role to demonstrate this pattern.

What I Wanted to Achieve
I wasn’t trying to build “another CI script.” I wanted a deployment foundation the team could trust and reuse everywhere.

My goals were:

One consistent deployment engine for all SAM projects.
Support for both single-account and multi-account deployments.
No repository-specific setup drift for AWS/SAM tooling.
Easy onboarding for new services.
A safer security model using role assumption instead of long-lived credentials.
In short, I wanted deployment to be boring, predictable, and easy to maintain.

Where This Fits Alongside AWS Services

AWS provides excellent services such as AWS CodePipeline and CloudFormation Change Sets for building and managing deployment workflows. My goal wasn't to replace these services, but to solve a different challenge: standardizing how AWS SAM applications are deployed across multiple repositories and AWS accounts.

By packaging the deployment tooling into a reusable, versioned Docker image, every project uses the same AWS CLI, AWS SAM CLI, and deployment process regardless of the CI/CD platform. This approach reduces duplication, simplifies maintenance, and provides a consistent deployment experience whether the pipeline runs in Bitbucket Pipelines, GitHub Actions, Jenkins, or even AWS CodePipeline itself.

Supporting Node.js and Python
One practical requirement was runtime flexibility. Some of our services are Node.js, others are Python, and the deployment pipeline needed to support both without branching into separate systems.

So I designed the image and scripts to be runtime-aware:

Node.js projects can use the right Node version.
Python projects can use the right Python version.
The SAM deployment process stays consistent.
That means teams can keep their service language choices while still using a common deployment pipeline.

Repository Walkthrough
The repository is structured to keep responsibilities clear:

Core pipeline orchestration logic.
Scripts for runtime setup and SAM build/deploy execution.
Example projects (Node.js and Python) showing expected usage.
Tests for parsing, validation, and deployment flow behavior.
Sample CI workflows to demonstrate real integration.
This structure helped keep the project approachable, especially for teams adopting it for the first time.

How to Use the Pipeline
Using the pipeline follows a simple pattern:

Configure your CI workflow to use the public ECR image.

image:
  name: public.ecr.aws/z7q5l7x5/sam-pipeline:1.0.0

pipelines:
  branches:
    main:
      - step:
          script:
            - ./deploy.sh
Enter fullscreen mode Exit fullscreen mode

Provide deployment variables (accounts, regions, runtime language, stack name, working directory, and optional SAM arguments).
Ensure target AWS accounts have the deploy role and trust policy configured.
Trigger the pipeline.
After that, the container handles runtime setup, account role assumption, and SAM build/deploy flow in a standardized way.

Benefits & Results
Moving to this model gave immediate benefits:

Standardization: same deployment behavior across repositories.
Lower maintenance: no repeated fixes in every pipeline.
Faster onboarding: new projects plug into an existing pattern.
Better security posture: role assumption with temporary credentials.
More confidence in releases: fewer environment/toolchain surprises.
The biggest gain was reducing operational noise. We now spend more time shipping features and less time fixing deployment plumbing.

Lessons Learned
A few lessons stood out while building this:

Reusability matters more than cleverness in CI/CD tooling.
Most deployment pain comes from inconsistency, not complexity.
Security and developer productivity can improve together.
Good examples are just as important as good code.
Treat deployment tooling like a product: version it, document it, test it.
Also, making one good shared pipeline is usually easier than maintaining ten almost-identical ones.

Future Improvements
There are still a few things I want to improve:

Better deployment reporting per account/region.
Optional pre-deployment checks and guardrails.
Stronger observability around failures and retries.
More advanced runtime customization for edge cases.
Cleaner developer experience for local dry-run validation.
The long-term goal is to keep scaling this without increasing operational complexity.

Conclusion
The biggest lesson from this work is that the real value is not just a Docker image, but a reusable deployment model.

Instead of maintaining similar deployment logic across many repositories, teams can standardize on one runtime and one SAM execution flow, then securely deploy to multiple accounts using AssumeRole.

If you want quick adoption, you can use my public image directly:
public.ecr.aws/z7q5l7x5/sam-pipeline:1.0.0

If your organization requires tighter governance, use the same architecture and publish your own private deployment image.

Either way, the outcome is the same: less deployment drift, lower maintenance costs, and faster delivery across environments/ multi-account/ regions.

Top comments (0)