Back to portfolioVamsi Krishna Samboju

// case study · public project

Automating AWS AMI Creation for Linux and Windows Build Environments

A set of public infrastructure-as-code repositories demonstrating automated AMI builds for Linux build servers, Windows web servers, and Windows SQL servers — each using the same Packer, Ansible, and InSpec toolchain with an Azure DevOps pipeline for test and production delivery.

PackerAnsibleInSpecAWS AMIAzure DevOpsWindowsLinuxIaC
01

Project Context

Type

Public portfolio project

Repositories

3 public on GitHub

Author

sambojuforge

These repositories are independent public projects published to demonstrate infrastructure automation work. They are not exported employer artifacts — the source code and tooling choices reflect my own engineering approach to AMI creation and configuration management.

02

Problem

Building AWS AMIs by hand — launching a base instance, configuring it manually, and snapshotting it — produces images that are difficult to reproduce, impossible to audit, and prone to configuration drift between environments. Each manual build is a one-off: there is no record of what was installed, in what order, or whether the final image met any compliance baseline.\n\nThe problem these repositories address is making AMI creation repeatable, auditable, and validated. The same build that produces an image for a test account should produce an identical image for production, with automated checks confirming the result meets a defined compliance baseline before the AMI is published.

03

Approach & Toolchain

Packer

Build orchestration

Packer drives the entire AMI creation process. It launches a temporary EC2 instance from a base AMI, coordinates the provisioners, waits for them to complete, and creates the final AMI from the configured instance. The Packer template defines the builder (amazon-ebs), the source AMI, the instance type, the VPC and subnet, and the sequence of provisioning steps. All configuration is through environment variables — no credentials in the template.

Ansible

Package installation & configuration

Ansible playbooks handle the actual software installation and configuration on the instance. Each playbook installs one component — a runtime, a build tool, a system library — and they run in sequence via Packer's provisioner list. This modular structure means playbooks can be added, removed, or reordered without touching the Packer template structure. For Linux, Ansible runs over SSH. For Windows, Packer bootstraps WinRM using a boot configuration script, then Ansible connects over WinRM.

InSpec

Compliance validation

After provisioning completes and the instance reboots, InSpec runs a compliance profile against the instance. The profile checks that each installed package or tool is present at the expected version — verifying by package manager, command-line version output, or file existence depending on what each tool exposes. The build fails if InSpec checks do not pass. This means the AMI is only published when the instance has been confirmed to match the defined baseline.

Azure DevOps

CI/CD pipeline

Each repository includes an Azure DevOps pipeline that runs on every push. Branches other than main build and publish AMIs to test, development, and staging AWS accounts — after InSpec validation passes. Merges to main target the production AWS account and, after InSpec validation, publish and share the final AMI to the configured production target accounts. The AMI version number in the Packer template is incremented with each pull request to maintain a clear image version history.

04

Build Pipeline

AMI build pipeline — same pattern across all three repositories

BRANCH: non-mainGit pushfeature branchAzure DevOpspipeline triggersPacker validate + buildtest AWS accountAnsible provisionersinstall & configure packagesInSpec validationcompliance checks passAMI publishedtest / dev / staging accountsBRANCH: mainMerge to mainPR approvedAzure DevOpspipeline triggersPacker buildproduction AWS accountAnsible provisionersinstall & configure packagesInSpec validationgates AMI publishAMI publishedproduction accountsAMI version incremented in Packer template with each pull request
05

What the Repositories Contain

Linux_AMI

Custom Linux build server AMI. Installs a broad set of language runtimes and development tools needed for CI/CD build agents.

Ruby

Ansible provisioners

Java 11Node v8Node v12PythonRuby.NET SDK 2.2.NET SDK 3.1.NET SDK 5.NET SDK 6MavenGulp CLIKarmaDockerGitPowerShellKerberosPyWinRMFontconfigAnsiblePackerInSpecAzure DevOps Agent

InSpec: Verifies each installed runtime and tool by version — package manager checks, command-line version output, and file existence (e.g. Azure DevOps agent run.sh). Build fails if any check does not pass.

Windows_Webserver_AMI

Custom Windows web server AMI. Configures IIS with ASP.NET MVC, URL rewriting, and Web Deploy for application hosting.

PowerShell

Ansible provisioners

IIS web serverASP.NET MVCASP.NET MVC 4URL RewriteWeb DeployDefault IIS cleanup

InSpec: Automated compliance testing confirms the IIS configuration, ASP.NET components, and tooling are correctly installed before the AMI is published. WinRM is bootstrapped via a boot_config script so Packer can communicate with the Windows instance.

Windows_SQLserver_AMI

Custom Windows SQL Server AMI. Provisions and configures SQL Server for use as a database instance in AWS environments.

PowerShell

Ansible provisioners

SQL ServerWinRM bootstrap

InSpec: InSpec validates the SQL Server installation and configuration meet the defined baseline before the AMI is published and shared to target accounts.

06

Engineering Decisions

One playbook per component

Each Ansible playbook installs exactly one thing. This makes the provisioner list in the Packer template a readable manifest of what is in the image, and means components can be added, removed, or updated independently. A single large playbook would be harder to debug when one step fails and harder to maintain when one component needs updating.

InSpec as a build gate, not a post-build audit

InSpec runs as part of the Packer build before the AMI is created. If the compliance checks fail, the build fails and no AMI is published. This means the published AMI is always a validated artifact — there is no risk of a non-compliant image reaching production because validation was skipped or treated as advisory.

Branch-based account separation

Feature branches build and publish AMIs to test, development, and staging AWS accounts after InSpec validation passes. Merges to main build and publish to the production account only. This keeps production AMI registries clean — only reviewed, PR-approved builds reach production — while still giving lower environments a validated AMI from every branch.

WinRM bootstrap for Windows provisioning

Packer cannot use SSH to provision Windows instances. The boot_config directory in the Windows repositories contains the WinRM setup script that Packer injects as user data on instance launch. This bootstraps the WinRM listener before Ansible attempts to connect, without requiring any manual instance configuration.

Environment variables for all secrets and configuration

No credentials, account IDs, VPC IDs, or subnet IDs appear in the committed Packer templates. All are passed as environment variables at build time — in the pipeline from Azure DevOps pipeline variables, and locally from the engineer's shell. This keeps the repository clean of environment-specific configuration and avoids credential exposure in source control.