Immutable Infrastructure on AWS with Packer, Auto Scaling, and CodeDeploy
Immutable Infrastructure on AWS with Packer, Auto Scaling, and CodeDeploy

Nelle architetture enterprise e in particolare nei settori regolamentati come il bancario, la gestione del parco macchine virtuali storicamente si è affidata al modello dell'infrastruttura mutabile. In questi contesti, i server a lunga vita e i sistemi transazionali legacy sono spesso una realtà imprescindibile: per governarli con rigore, piattaforme enterprise di automazione centralizzata come Ansible Automation Platform (AAP) giocano un ruolo essenziale, coordinando patch di sicurezza, inventari dinamici e playbook idempotenti nel pieno rispetto dei vincoli di compliance e auditabilità.
Accanto a questa prassi consolidata, la maturazione dei servizi cloud-native su AWS ha reso possibile adottare una strategia complementare e ad alta efficienza per i tier applicativi, i microservizi e i carichi scalabili: l'infrastruttura immutabile (immutable infrastructure). In questo modello, le istanze di calcolo non vengono manutenute o riconfigurate in-place nel tempo, ma vengono considerate entità effimere e sostituibili (cattle): ogni modifica a livello di sistema operativo, patch di sicurezza o upgrade di runtime si traduce nella creazione di una nuova immagine certificata a monte e nella sostituzione controllata dell'istanza in esecuzione.
In questo articolo analizzeremo come ingegnerizzare su Amazon Web Services un'infrastruttura Linux realmente immutabile, combinando una pipeline di baking a due livelli con HashiCorp Packer (Golden AMI e Silver AMI), il disaccoppiamento dello stato persistente su Amazon EFS, l'orchestrazione continua del calcolo tramite EC2 Auto Scaling Groups e il deployment dell'applicazione via AWS CodeDeploy.
Paradigmi a Confronto: Quando Scegliere la Sostituzione rispetto alla Manutenzione In-Place
Comprendere i punti di forza di entrambi i modelli consente all'architetto cloud di adottare la soluzione ottimale per ciascun carico di lavoro aziendale:
- Il Modello Mutabile Governato (es. AAP): Ideale e spesso inevitabile per database relazionali complessi con storage locale, appliance certificate di terze parti o sistemi monolitici core banking con cicli di rilascio rigidi. La convergenza in-place, orchestrata centralmente tramite Ansible Automation Platform, assicura tracciabilità, minimizza i disservizi attraverso riavvii coordinati e applica policy di sicurezza uniformi senza dover ricostruire la macchina da zero.
- Il Modello Immutabile Cloud-Native: Perfetto per il tier web, microservizi stateless, backend API e worker di elaborazione batch. Rimuove alla radice il rischio di disallineamento (configuration drift), rende l'ambiente di produzione identico bit-per-bit a quello di collaudo e trasforma il patching ordinario in una procedura standardizzata di sostituzione automatica.
Il Valore dell'Immutabilità Enterprise: Per i carichi di calcolo stateless su cloud, il patching non è un aggiornamento applicato in runtime sulla macchina viva, ma una sostituzione programmata del nodo tramite immagini pre-compilate e convalidate offline. Se un'immagine supera i test di sicurezza e prestazionali in staging, si ha la certezza matematica che manterrà lo stesso comportamento deterministico in produzione.
La tabella seguente riassume i trade-off e i contesti di applicazione ideali dei due approcci:
| Dimensione Operativa | Modello Mutabile Centralizzato (es. AAP) | Modello Immutabile Cloud-Native (Packer + ASG) |
|---|---|---|
| Contesto Primario d'Uso | Sistemi core banking, appliance certificate, DB monolitici | Tier web, microservizi stateless, worker scalabili, API |
| Meccanismo di Aggiornamento | Esecuzione playbook idempotenti e riavvio coordinato | Bake offline di nuova AMI e rolling refresh dell'ASG |
| Controllo del Configuration Drift | Mitigato periodicamente tramite automazione e audit AAP | Azzerato per design (istanze ricreate da baseline identiche) |
| Strategia di Rollback | Esecuzione playbook di revert o ripristino snapshot | Switch immediato alla versione precedente del Launch Template |
| Validazione di Sicurezza | Scansione periodica ad agenti sulle macchine attive | Scansione CVE e conformità CIS a monte sull'immagine (pre-deploy) |
| Gestione dello Stato Dati | Spesso persistente e legato ai volumi locali | Disaccoppiato su storage gestito multi-AZ ( Amazon EFS) |
Architettura a Cinque Layer: Dalla Compilazione al Lifecycle
Per realizzare un'architettura che sia al contempo resiliente, conforme alle normative e snella dal punto di vista operativo ed economico, abbiamo strutturato la soluzione su cinque livelli integrati in tre macro-componenti logici:
Layer 1 & 2: La Pipeline di Baking a Due Livelli con Packer
Uno dei dilemmi tipici nella creazione di immagini immutabili è la granularità del bake: creare un'unica grande AMI per ogni singolo microservizio (fat image) oppure utilizzare immagini minime configurate interamente a runtime (thin image)?
La soluzione enterprise ottimale adotta un approccio a due livelli:
- Livello 1 — Golden AMI: Gestita dal team di Cloud Platform e Cyber Security. Parte da un'immagine upstream certificata (es. Amazon Linux 2023 o RHEL 9) e applica l'hardening del sistema operativo secondo lo standard CIS Benchmark Level 1, configura gli agenti centrali di telemetria e sicurezza (AWS Systems Manager Agent, CloudWatch Agent, CrowdStrike Falcon o Wazuh) e inietta le Root CA certificate dell'organizzazione.
- Livello 2 — Silver AMI: Gestita dai team DevOps applicativi. Utilizza come sorgente (
source_ami_filter) l'ultima Golden AMI approvata e installa le dipendenze di runtime del servizio (es. Node.js LTS, OpenJDK, Python virtual environment, Nginx reverse proxy preconfigurato, parametri di ottimizzazione del kernelsysctl).
Configurazione Packer HCL: Golden AMI
Il file seguente mostra la definizione dichiarativa in HCL della Golden AMI con installazione degli agenti e hardening base:
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
variable "aws_region" {
type = string
default = "eu-west-1"
}
source "amazon-ebs" "golden_linux" {
region = var.aws_region
instance_type = "t3.medium"
ami_name = "golden-ami-al2023-cis-{{timestamp}}"
ami_description = "Enterprise Golden AMI based on Amazon Linux 2023 with CIS Baseline"
encrypt_boot = true
ssh_username = "ec2-user"
associate_public_ip_address = false
source_ami_filter {
filters = {
name = "al2023-ami-2023.*-kernel-6.1-x86_64"
root-device-type = "ebs"
virtualization-type = "hvm"
}
owners = ["amazon"]
most_recent = true
}
tags = {
Name = "Golden-AMI-AL2023"
Environment = "Core-Shared"
ManagedBy = "Platform-Security"
}
}
build {
sources = ["source.amazon-ebs.golden_linux"]
# 1. Aggiornamento base e installazione agenti enterprise
provisioner "shell" {
inline = [
"sudo dnf update -y --releasever=latest",
"sudo dnf install -y amazon-cloudwatch-agent amazon-ssm-agent amazon-efs-utils",
"sudo systemctl enable amazon-ssm-agent",
"sudo systemctl enable amazon-cloudwatch-agent"
]
}
# 2. Hardening CIS e tuning kernel
provisioner "shell" {
inline = [
"echo 'net.ipv4.tcp_syncookies = 1' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"echo 'net.ipv4.conf.all.accept_redirects = 0' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"echo 'fs.file-max = 2097152' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"sudo sysctl --system"
]
}
}
Configurazione Packer HCL: Silver AMI
La Silver AMI eredita direttamente dalla Golden AMI approvata, riducendo i tempi di compilazione a meno di tre minuti:
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
variable "aws_region" {
type = string
default = "eu-west-1"
}
source "amazon-ebs" "silver_runtime" {
region = var.aws_region
instance_type = "t3.medium"
ami_name = "silver-ami-nodejs22-nginx-{{timestamp}}"
ami_description = "Application Silver AMI with Node.js 22 LTS, Nginx and CodeDeploy Agent"
encrypt_boot = true
ssh_username = "ec2-user"
associate_public_ip_address = false
# Eredita dinamicamente dall'ultima Golden AMI convalidata
source_ami_filter {
filters = {
name = "golden-ami-al2023-cis-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
owners = ["self"]
most_recent = true
}
tags = {
Name = "Silver-AMI-WebRuntime"
Environment = "Production"
Role = "Application-Server"
}
}
build {
sources = ["source.amazon-ebs.silver_runtime"]
provisioner "shell" {
inline = [
"sudo dnf install -y nginx nodejs ruby wget",
"# Installazione ufficiale agente AWS CodeDeploy",
"cd /home/ec2-user",
"wget https://aws-codedeploy-${var.aws_region}.s3.${var.aws_region}.amazonaws.com/latest/install",
"chmod +x ./install",
"sudo ./install auto",
"sudo systemctl enable codedeploy-agent",
"sudo systemctl enable nginx",
"rm -f ./install"
]
}
}
Layer 3 & 4: Storage Disaccoppiato con EFS e Resilienza ad Unico Nodo
Un errore frequente nella progettazione di macchine virtuali su cloud è l'utilizzo del volume root EBS per archiviare dati di sessione, caricamenti di file o asset applicativi. In un'architettura immutabile, il disco EBS è considerato rigorosamente effimero: quando un'istanza viene terminata, il suo volume EBS deve essere distrutto senza esitazione.
Per garantire la persistenza dei dati e consentire l'immutabilità dei nodi, l'intero stato dinamico viene delegato ad Amazon EFS (Elastic File System), file system scalabile, multi-Availability Zone e completamente gestito, montato all'avvio via NFS v4.1 con cifratura del traffico in transito tramite TLS.
Resilienza ed Efficienza dei Costi: Il Pattern Single-Instance HA
Non tutti i carichi aziendali richiedono cluster distribuiti con decine di nodi costantemente attivi. Molti workload interni (sistemi gestionali, micro-backend, server di build, ambienti batch) necessitano di elevata disponibilità ma presentano budget contenuti.
Grazie alla combinazione di Auto Scaling Group impostato a capacità fissa (min_size = 1, max_size = 1, desired_capacity = 1) e Amazon EFS per lo storage condiviso, otteniamo una resilienza enterprise al minor costo possibile:
1. Self-Healing Istantaneo: Se l'istanza EC2 subisce un guasto hardware nell'Availability Zone A o il sistema operativo va in kernel panic, l'Auto Scaling Group rileva il mancato superamento dell'health check di EC2.
2. Sostituzione Automatica Cross-AZ: L'ASG termina l'istanza non funzionante e ne avvia istantaneamente una nuova, posizionandola nell'Availability Zone B (se configurato su subnet multi-AZ).
3. Ricollegamento Trasparente dei Dati: La nuova istanza esegue il bootstrap, rimonta lo share EFS cifrato su /var/www/shared e torna immediatamente operativa senza perdita di un singolo byte di dati.
Con questo pattern, l'azienda paga il computo di una sola istanza EC2 (senza sprechi di VM in standby passivo o licenze raddoppiate), preservando comunque un Recovery Time Objective (RTO) di pochi minuti e un Recovery Point Objective (RPO) pari a zero grazie alla replica multi-AZ nativa di Amazon EFS.
Gotcha di Produzione su Amazon EFS: Per workload con traffico I/O elevato su file di piccole dimensioni, verificare di configurare EFS in modalità Elastic Throughput anziché la legacy Bursting Throughput. La modalità Bursting, in assenza di grandi volumi di dati memorizzati, può esaurire rapidamente i burst credit, provocando code I/O e latenze elevate al bootstrap dei servizi.
Layer 5: Convergenza Applicativa con AWS CodeDeploy
Un altro snodo critico dell'architettura riguarda la frequenza di rilascio del codice applicativo: se ogni deploy di un fix di due righe richiedesse il bake di un'intera Silver AMI (tempo medio: 3-5 minuti per Packer + 5 minuti per l'Instance Refresh dell'ASG), la velocità di sviluppo dei team crollerebbe.
La soluzione consiste nel separare il ciclo di vita dell'infrastruttura da quello del codice:
- Frequenza Mensile / Quindicinale: Refresh immutabile delle AMI (OS, kernel security patch, librerie di runtime).
- Frequenza Quotidiana / Continua: Distribuzione del codice applicativo tramite AWS CodeDeploy.
Il Meccanismo di Hook e Bootstrap
All'avvio di una nuova istanza (per scale-out, self-healing o rolling refresh dell'AMI):
- La macchina si avvia dalla Silver AMI con l'agente CodeDeploy attivo.
- L'integrazione tra Auto Scaling Group e CodeDeploy aggancia l'istanza nello stato di
Pending:Waitattraverso un Lifecycle Hook di Auto Scaling. - CodeDeploy scarica l'ultima release applicativa memorizzata su Amazon S3, estrae il pacchetto ed esegue le istruzioni del file
appspec.yml. - Solo al superamento dell'hook
ValidateService, CodeDeploy segnala il successo all'Auto Scaling Group, che completa il transitorio e apre il traffico di rete.
version: 0.0
os: linux
files:
- source: /
destination: /var/www/application
hooks:
BeforeInstall:
- location: scripts/validate_efs_mount.sh
timeout: 60
runas: root
AfterInstall:
- location: scripts/configure_application.sh
timeout: 120
runas: root
ApplicationStart:
- location: scripts/start_service.sh
timeout: 60
runas: root
ValidateService:
- location: scripts/health_check.sh
timeout: 45
runas: root
Orchestrazione Terraform: Launch Template e Instance Refresh
L'integrazione di questi componenti viene formalizzata tramite Infrastructure as Code con Terraform/OpenTofu, configurando la policy di instance_refresh direttamente nel blocco dell'Auto Scaling Group:
resource "aws_launch_template" "immutable_worker" {
name_prefix = "immutable-worker-"
image_id = data.aws_ami.latest_silver.id
instance_type = "t3.medium"
iam_instance_profile {
name = aws_iam_instance_profile.worker_profile.name
}
vpc_security_group_ids = [aws_security_group.worker_sg.id]
# User data minimale per montaggio EFS cifrato via TLS
user_data = base64encode(<<-EOF
#!/bin/bash
set -euo pipefail
mkdir -p /var/www/shared
# Montaggio Amazon EFS con TLS e opzioni di resilienza NFS
mount -t efs -o tls,iam ${aws_efs_file_system.shared_storage.id}:/ /var/www/shared
echo "${aws_efs_file_system.shared_storage.id}:/ /var/www/shared efs _netdev,tls,iam 0 0" >> /etc/fstab
EOF
)
tag_specifications {
resource_type = "instance"
tags = {
Name = "Immutable-Worker-Node"
Environment = "Production"
}
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "immutable_asg" {
name_prefix = "asg-immutable-"
vpc_zone_identifier = var.private_subnet_ids
min_size = 1
max_size = 2
desired_capacity = 1
launch_template {
id = aws_launch_template.immutable_worker.id
version = "$Latest"
}
# Configurazione dell'Instance Refresh automatico
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 100
instance_warmup = 300
checkpoint_delay = 120
}
triggers = ["tag"]
}
lifecycle {
create_before_destroy = true
ignore_changes = [desired_capacity]
}
}
Zero-Downtime su ASG a Singola Istanza: Impostando max_size = 2 e min_healthy_percentage = 100, durante l'Instance Refresh l'ASG istanzierà prima la nuova macchina con la nuova Silver AMI; una volta che la nuova istanza avrà completato il bootstrap e il deploy via CodeDeploy, l'ASG drenerà e terminerà la vecchia istanza, ottenendo una sostituzione a zero tempo di fermo (blue/green rolling) anche per servizi mononodo.
Il Ciclo di Vita Mensile Automatizzato
Il valore supremo dell'infrastruttura immutabile emerge nella gestione ordinaria e ricorrente delle patch. Anziché dedicare notti o fine settimana all'applicazione manuale di aggiornamenti tramite SSH, l'intero ciclo viene affidato a una schedulazione mensile automatizzata (es. ogni primo lunedì del mese via Amazon EventBridge e AWS Step Functions o pipeline CI/CD):
- Trigger Automatico della Golden Pipeline: EventBridge avvia il build Packer della Golden AMI, che preleva l'ultima immagine ufficiale Amazon Linux, applica le patch di sicurezza del kernel e valida i controlli CIS.
- Scansione e Approvazione: La nuova Golden AMI viene scansionata da Amazon Inspector o Trivy. Al superamento dei quality gate di sicurezza, viene marcata con il tag
Status = Approved. - Compilazione a Cascata della Silver AMI: Il tag attiva la pipeline della Silver AMI, che eredita dalla nuova Golden, installa i runtime applicativi aggiornati e genera la Silver AMI finale.
- Aggiornamento del Launch Template: La pipeline CI/CD aggiorna il Launch Template con la nuova Silver AMI (
version = $Latest). - Esecuzione dell'Instance Refresh: Viene invocata l'API
StartInstanceRefreshdell'Auto Scaling Group. L'ASG avvia la nuova istanza, CodeDeploy distribuisce l'ultima versione stabile dell'applicazione, e la vecchia macchina viene ritirata e terminata in modo pulito.
Conclusioni: Affidabilità Predicibile
Adottare l'infrastruttura immutabile su AWS con Packer, Auto Scaling, CodeDeploy ed EFS trasforma il patching e la manutenzione ordinaria da evento rischioso ad attività ingegneristica standardizzata, deterministica e invisibile.
Eliminando il configuration drift e separando in modo netto il computo effimero dallo stato persistente, l'infrastruttura acquisisce una resilienza innata: ogni singolo server è sostituibile in qualsiasi momento, i ripristini sono immediati e il carico operativo del team si azzera, garantendo al contempo la massima sicurezza e conformità ai più severi requisiti enterprise.
In enterprise IT architectures, and particularly within regulated industries such as banking and financial services, virtual machine management has historically relied upon the mutable infrastructure paradigm. In these mission-critical environments, long-lived servers, transactional monoliths, and specialized appliances represent an unavoidable operational reality. To govern these workloads with precision, enterprise centralized automation platforms like Ansible Automation Platform (AAP) play a pivotal role—orchestrating security patching, dynamic inventories, and idempotent playbooks while satisfying rigorous regulatory compliance and auditability mandates.
Alongside this established operational baseline, the evolution of cloud-native primitives on AWS enables a complementary, highly efficient strategy for web tiers, stateless microservices, and elastic workloads: immutable infrastructure. In this paradigm, virtual compute instances are never altered or patched in-place over time; instead, they are treated as disposable entities (cattle). Every OS-level security patch, kernel update, or runtime adjustment is baked into a certified machine image upstream, followed by a controlled, rolling replacement of the live instances.
In this article, we examine how to engineer a production-ready, truly immutable Linux infrastructure on Amazon Web Services. We will orchestrate a two-tier baking pipeline using HashiCorp Packer (Golden and Silver AMIs), decouple persistent dynamic application state with Amazon EFS, maintain high availability through EC2 Auto Scaling Groups, and deliver application updates cleanly via AWS CodeDeploy.
Comparing Paradigms: When to Choose Replacement Over In-Place Maintenance
Understanding the respective strengths of both operational models allows cloud architects to deploy the right strategy for each specific workload within the enterprise portfolio:
- The Governed Mutable Model (e.g., AAP): Essential and often inevitable for complex relational databases with local storage, certified third-party vendor appliances, or legacy core banking engines with rigid deployment windows. In-place convergence, coordinated through Ansible Automation Platform, guarantees auditability, minimizes disruption via synchronized restarts, and enforces organizational baseline compliance without requiring nodes to be re-provisioned from scratch.
- The Cloud-Native Immutable Model: Ideal for the web tier, stateless microservices, API gateways, and scalable batch processors. It completely eliminates configuration drift, guarantees bit-for-bit parity between staging and production, and transforms recurring patch cycles into a standardized, automated rolling replacement workflow.
The Core Value of Enterprise Immutability: For stateless cloud workloads, patching is not an in-place modification executed on a running VM, but a scheduled node replacement using pre-compiled, offline-validated images. When an image passes automated security and regression suites in staging, teams gain mathematical certainty that it will exhibit the exact same deterministic behavior in production.
The table below outlines the key operational trade-offs and recommended enterprise contexts for both paradigms:
| Operational Dimension | Governed Mutable Model (e.g., AAP) | Cloud-Native Immutable Model (Packer + ASG) |
|---|---|---|
| Primary Context of Use | Core banking backends, certified appliances, monolithic DBs | Web tiers, stateless microservices, elastic worker nodes, APIs |
| Update Mechanism | Idempotent playbook execution and coordinated restarts | Offline AMI bake and automated ASG rolling instance refresh |
| Configuration Drift Control | Actively mitigated via continuous automation and AAP audits | Eliminated by design (all nodes spun up from identical baseline) |
| Rollback Strategy | Automated revert playbooks or storage snapshot restore | Instantaneous switch back to previous Launch Template revision |
| Security Validation | Scheduled agent-based scanning across active instances | Upstream CVE and CIS compliance scanning on AMI (pre-deploy) |
| State Management | Frequently stateful and tied to local storage volumes | Decoupled and resilient on multi-AZ managed storage ( Amazon EFS) |
Five-Layer Topology: From Baking Pipeline to Lifecycle
To achieve an infrastructure that is simultaneously resilient, compliant, and cost-effective, we organize the solution into five decoupled architectural layers integrated across three logical macro-components:
Layers 1 & 2: Two-Tier Baking Pipeline with HashiCorp Packer
A recurrent design debate in immutable architectures centers on image granularity: should organizations bake a monolithic AMI per microservice (fat image), or rely on minimal images configured on the fly via massive Cloud-Init scripts (thin image)?
The optimal enterprise architecture employs a two-tier baking model:
- Tier 1 — Golden AMI: Owned by the Cloud Platform and Enterprise Security teams. It starts from a certified upstream base (such as Amazon Linux 2023 or RHEL 9) and enforces OS hardening aligned with CIS Benchmark Level 1, pre-installs baseline observability and management agents (AWS Systems Manager Agent, CloudWatch Agent, CrowdStrike Falcon or Wazuh), and provisions corporate Root Certificate Authorities.
- Tier 2 — Silver AMI: Owned by application DevOps teams. It uses the latest approved Golden AMI as its
source_ami_filterand provisions application-specific runtimes (e.g., Node.js LTS, OpenJDK, Python virtualenvs, pre-configured Nginx reverse proxies, and kernelsysctloptimizations).
Declarative Packer HCL: Golden AMI
The configuration below details the declarative HCL definition for the Golden AMI, applying CIS hardening and telemetry daemons:
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
variable "aws_region" {
type = string
default = "eu-west-1"
}
source "amazon-ebs" "golden_linux" {
region = var.aws_region
instance_type = "t3.medium"
ami_name = "golden-ami-al2023-cis-{{timestamp}}"
ami_description = "Enterprise Golden AMI based on Amazon Linux 2023 with CIS Baseline"
encrypt_boot = true
ssh_username = "ec2-user"
associate_public_ip_address = false
source_ami_filter {
filters = {
name = "al2023-ami-2023.*-kernel-6.1-x86_64"
root-device-type = "ebs"
virtualization-type = "hvm"
}
owners = ["amazon"]
most_recent = true
}
tags = {
Name = "Golden-AMI-AL2023"
Environment = "Core-Shared"
ManagedBy = "Platform-Security"
}
}
build {
sources = ["source.amazon-ebs.golden_linux"]
# 1. Base OS update and enterprise agent provisioning
provisioner "shell" {
inline = [
"sudo dnf update -y --releasever=latest",
"sudo dnf install -y amazon-cloudwatch-agent amazon-ssm-agent amazon-efs-utils",
"sudo systemctl enable amazon-ssm-agent",
"sudo systemctl enable amazon-cloudwatch-agent"
]
}
# 2. CIS hardening and network stack sysctl tuning
provisioner "shell" {
inline = [
"echo 'net.ipv4.tcp_syncookies = 1' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"echo 'net.ipv4.conf.all.accept_redirects = 0' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"echo 'fs.file-max = 2097152' | sudo tee -a /etc/sysctl.d/99-cis.conf",
"sudo sysctl --system"
]
}
}
Declarative Packer HCL: Silver AMI
The Silver AMI inherits directly from the validated Golden AMI, accelerating compilation times to under three minutes:
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
variable "aws_region" {
type = string
default = "eu-west-1"
}
source "amazon-ebs" "silver_runtime" {
region = var.aws_region
instance_type = "t3.medium"
ami_name = "silver-ami-nodejs22-nginx-{{timestamp}}"
ami_description = "Application Silver AMI with Node.js 22 LTS, Nginx and CodeDeploy Agent"
encrypt_boot = true
ssh_username = "ec2-user"
associate_public_ip_address = false
# Dynamically pull the latest validated Golden AMI
source_ami_filter {
filters = {
name = "golden-ami-al2023-cis-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
owners = ["self"]
most_recent = true
}
tags = {
Name = "Silver-AMI-WebRuntime"
Environment = "Production"
Role = "Application-Server"
}
}
build {
sources = ["source.amazon-ebs.silver_runtime"]
provisioner "shell" {
inline = [
"sudo dnf install -y nginx nodejs ruby wget",
"# Install official AWS CodeDeploy agent",
"cd /home/ec2-user",
"wget https://aws-codedeploy-${var.aws_region}.s3.${var.aws_region}.amazonaws.com/latest/install",
"chmod +x ./install",
"sudo ./install auto",
"sudo systemctl enable codedeploy-agent",
"sudo systemctl enable nginx",
"rm -f ./install"
]
}
}
Layers 3 & 4: Decoupled EFS Storage and Single-Instance Resilience
A widespread mistake in cloud virtual machine design is storing dynamic session files, media uploads, or application caches directly on the local root EBS volume. In an immutable architecture, the root EBS volume is strictly disposable: when an EC2 instance terminates, its attached EBS volume must be destroyed without hesitation.
To achieve true compute immutability without losing critical state, all persistent data is externalized to Amazon EFS (Elastic File System)—a fully managed, multi-Availability Zone, POSIX-compliant file system mounted at boot via NFS v4.1 with TLS encryption in transit.
Cost-Effective High Availability: The Single-Instance HA Pattern
Not every enterprise workload justifies large, multi-node distributed clusters running around the clock. Many essential corporate systems—such as administrative portals, batch-processing daemons, and internal microservices—require enterprise-grade durability and high availability while operating on tight infrastructure budgets.
By coupling an Auto Scaling Group pinned to a fixed single node (min_size = 1, max_size = 1, desired_capacity = 1) with Amazon EFS for persistent state, we establish resilient self-healing at minimum compute spend:
1. Automated Self-Healing: Should an underlying hardware failure or hypervisor impairment strike Availability Zone A, EC2 automated health checks flag the node as unhealthy.
2. Cross-AZ Replacement: The Auto Scaling Group terminates the degraded instance and instantly provisions a fresh replacement in Availability Zone B (spanning multiple private subnets).
3. Transparent State Recovery: The new instance boots clean, mounts the multi-AZ EFS share over TLS at /var/www/shared, and restores service within minutes without risking a single byte of data loss.
Under this pattern, the enterprise pays for the runtime compute of exactly one modest EC2 instance (eliminating idle passive standbys and duplicate third-party license fees), while retaining a sub-5-minute Recovery Time Objective (RTO) and a zero Recovery Point Objective (RPO) backed by EFS native cross-AZ durability.
Production Gotcha on Amazon EFS Throughput: For workloads handling frequent I/O bursts across small metadata files, always configure Amazon EFS in Elastic Throughput mode rather than legacy Bursting Throughput. In environments with low stored storage volumes, Bursting mode can rapidly deplete burst credits, triggering severe I/O queuing and prolonging cold-start bootstrapping.
Layer 5: Continuous Application Delivery with AWS CodeDeploy
Another structural bottleneck in immutable setups is code release frequency: if shipping a minor three-line bugfix forced engineers to bake a complete Silver AMI (taking 3–5 minutes for Packer plus another 5 minutes for ASG Instance Refresh), engineering velocity would grind to a halt.
The resolution lies in decoupling infrastructure immutability from software delivery cadence:
- Monthly / Bi-Weekly Cadence: Immutable AMI refresh (operating system, kernel patches, runtime runtimes, shared dependencies).
- Daily / Continuous Cadence: Application code deployments orchestrated via AWS CodeDeploy.
Auto Scaling Lifecycle Hook Orchestration
When a new instance is launched (due to scale-out, hardware self-healing, or an AMI rolling refresh):
- The VM powers up from the Silver AMI with the CodeDeploy agent pre-enabled.
- The Auto Scaling Group intercepts the instance in a
Pending:Waitstate through an automated Auto Scaling Lifecycle Hook. - CodeDeploy fetches the latest validated application artifact from an Amazon S3 bucket, unpacks the release, and executes the lifecycle directives defined in
appspec.yml. - Only upon successfully passing the
ValidateServicehook does CodeDeploy emit a success signal back to the Auto Scaling Group, shifting the instance intoInServiceand opening load balancer ingress traffic.
version: 0.0
os: linux
files:
- source: /
destination: /var/www/application
hooks:
BeforeInstall:
- location: scripts/validate_efs_mount.sh
timeout: 60
runas: root
AfterInstall:
- location: scripts/configure_application.sh
timeout: 120
runas: root
ApplicationStart:
- location: scripts/start_service.sh
timeout: 60
runas: root
ValidateService:
- location: scripts/health_check.sh
timeout: 45
runas: root
Terraform Orchestration: Launch Template and Instance Refresh
We codify this immutable ecosystem in Infrastructure as Code using Terraform/OpenTofu, configuring the native instance_refresh rolling update policy directly within the Auto Scaling Group resource:
resource "aws_launch_template" "immutable_worker" {
name_prefix = "immutable-worker-"
image_id = data.aws_ami.latest_silver.id
instance_type = "t3.medium"
iam_instance_profile {
name = aws_iam_instance_profile.worker_profile.name
}
vpc_security_group_ids = [aws_security_group.worker_sg.id]
# Minimal user-data to mount EFS over TLS using official helper utilities
user_data = base64encode(<<-EOF
#!/bin/bash
set -euo pipefail
mkdir -p /var/www/shared
# Mount EFS filesystem with in-transit TLS and IAM authentication
mount -t efs -o tls,iam ${aws_efs_file_system.shared_storage.id}:/ /var/www/shared
echo "${aws_efs_file_system.shared_storage.id}:/ /var/www/shared efs _netdev,tls,iam 0 0" >> /etc/fstab
EOF
)
tag_specifications {
resource_type = "instance"
tags = {
Name = "Immutable-Worker-Node"
Environment = "Production"
}
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "immutable_asg" {
name_prefix = "asg-immutable-"
vpc_zone_identifier = var.private_subnet_ids
min_size = 1
max_size = 2
desired_capacity = 1
launch_template {
id = aws_launch_template.immutable_worker.id
version = "$Latest"
}
# Automated rolling instance refresh configuration
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 100
instance_warmup = 300
checkpoint_delay = 120
}
triggers = ["tag"]
}
lifecycle {
create_before_destroy = true
ignore_changes = [desired_capacity]
}
}
Zero-Downtime Rolling Refresh on Single-Instance ASGs: By configuring max_size = 2 and min_healthy_percentage = 100, the Auto Scaling Group provisions the new instance backed by the latest Silver AMI before terminating the active node. Once the replacement instance finishes its bootstrap sequence and passes CodeDeploy health checks, connection draining commences on the legacy node, achieving a flawless blue/green rolling replacement even on single-node workloads.
The Automated Monthly Lifecycle
The ultimate architectural payoff of immutable infrastructure materializes in recurring maintenance routines. Rather than mandating late-night emergency maintenance windows or tedious SSH patching scripts, the entire pipeline executes autonomously on a monthly cadence (e.g., triggered on the first Monday of every month via Amazon EventBridge and AWS Step Functions):
- Automated Golden Pipeline Trigger: EventBridge initiates the Golden AMI Packer build, ingesting upstream kernel patches and validating CIS controls.
- Vulnerability Evaluation & Tagging: The Golden AMI undergoes automated vulnerability assessments via Amazon Inspector. Upon passing security compliance gates, it is tagged as
Status = Approved. - Cascading Silver Build: The approved tag fires the Silver AMI pipeline, which builds from the fresh Golden image, packages runtime updates, and publishes the final Silver AMI.
- Launch Template Evolution: The CI/CD pipeline deploys an updated Launch Template revision with the new Silver AMI ID (
version = $Latest). - Orchestrated Instance Refresh: The Auto Scaling Group triggers
StartInstanceRefresh. Fresh instances initialize, CodeDeploy synchronizes the active software release, and superseded instances are cleanly drained and terminated.
Conclusion: Predictable Operational Durability
Implementing immutable infrastructure on AWS utilizing Packer, Auto Scaling, CodeDeploy, and Amazon EFS elevates maintenance and patching from a fragile, error-prone event into a standardized, deterministic, and frictionless engineering process.
By eradicating configuration drift and strictly decoupling stateless compute from resilient shared storage, systems achieve innate resilience: individual servers become entirely disposable, disaster recovery is instantaneous, and the ongoing administrative burden on engineering teams drops to near zero—all while maintaining ironclad security and governance across the enterprise estate.