Post

IaC - Part 3

IaC - Part 3
flowchart TD

    A[Packer Build<br/>Phase 1] -->|Produces Image<br/>VDI/AMI/Azure Image| B[OpenTofu Deploy<br/>Phase 2]

    B -->|Boots VM with<br/>cloud-init seed| C[cloud-init First Boot<br/>Phase 3]

    C -->|SSH Ready| D[Ansible Provisioning<br/>Phase 4]

    D -->|VM Fully Configured| E[Ansible Cleanup<br/>Phase 5]

    E --> F[Snapshot / Export / Ready for Use]

    subgraph Local/Cloud Platforms
        B
        C
        D
        E
    end

    subgraph Image Factory
        A
    end

Phase 1 — Image Build (Packer)

Purpose: Convert an ISO into a reusable base image.

Why this is portable: Packer supports VirtualBox, AWS AMIs, Azure Managed Images, QEMU, and more — same template, different builders. Typical workflow:

  • Packer template defines:
    • ISO source
    • Boot automation
    • cloud-init seed injection
    • Ansible provisioners (optional)
  • Output:
    • VirtualBox: .vdi or .ova
    • AWS: AMI
    • Azure: Managed Image Portable artifacts:
  • Cloud-init seed ISO
  • Base OS image
  • SSH keys baked in

NOTE: Due to encoding issues with PowerShell, running the following command must be done in CMD - Windows command shell

packer build -var-file=.\vars\ubuntu-24.04.pkrvars.hcl .\ubuntu-24.04.pkr.hcl

Optionally if using PowerShell, then invoke cmd and pass it

1
cmd /c "packer build -var-file=.\vars\ubuntu-24.04.pkrvars.hcl .\ubuntu-24.04.pkr.hcl"

Phase 2 — IaC Deployment (OpenTofu)

Purpose: Create the VM, attach disks, configure networking, and boot it.

Why this is portable: OpenTofu has providers for:

  • VirtualBox
  • AWS
  • Azure
  • VMware
  • Proxmox
  • QEMU Same IaC patterns, different providers. Typical workflow:
  • main.tf defines:
    • VM size
    • Disk attachment
    • NICs
    • SSH access
  • tofu apply → VM created on chosen platform Portable IaC patterns:
  • Variables for CPU/RAM
  • Variables for image ID/path
  • Modules for each platform

Phase 3 — OS Installation Automation (cloud-init)

Purpose: Automate first boot, user creation, package install, and base config.

Why this is portable: cloud-init is the universal standard across:

  • AWS
  • Azure
  • GCP
  • OpenStack
  • Local VMs (VirtualBox, QEMU, VMware) Typical workflow:
  • Packer injects user-data + meta-data
  • VM boots → cloud-init runs:
  • Create users
  • Install packages
  • Configure SSH
  • Write files
  • Run commands Portable cloud-init modules:
  • packages:
  • runcmd:
  • write_files:
  • users:

Phase 4 — First-Boot Provisioning (Ansible)

Purpose: Apply configuration that is too complex or stateful for cloud-init.

Why this is portable: Ansible works identically across:

  • VirtualBox VMs
  • AWS EC2
  • Azure VMs
  • Bare metal
  • Containers Typical workflow:
  • Ansible inventory points to VM
  • Playbooks configure:
  • Hardening
  • Application install
  • Filesystems
  • Services
  • Monitoring agents Portable patterns:
  • SSH-based provisioning
  • Idempotent tasks
  • Role-based structure

Phase 5 — Post-Install Cleanup (Ansible from Windows)

Purpose: Run cleanup tasks from Windows after the VM is fully built.

Why this is portable: Ansible can run from:

  • Windows 11 (via WSL2)
  • Linux
  • CI/CD pipelines And it can target:
  • VirtualBox VMs
  • AWS
  • Azure Typical workflow:
  • Windows → WSL → Ansible
  • Run: ansible-playbook cleanup.yml
  • Cleanup tasks:
  • Remove temp files
  • Reset logs
  • Zero free space
  • Remove SSH host keys (if templating)
  • Final hardening Portable cleanup strategy:
  • Same playbook works everywhere
  • Only inventory changes
This post is licensed under CC BY 4.0 by the author.