1. Start a base image of Debian / Ubuntu / whatever – this is often done with Terraform.
2. Packer types a boot command after power-on to configure whatever you'd like
3. Packer manages the installation; with Debian and its derivatives, this is done mostly through the arcane language of preseed [0]
4. As a last step, a pre-configured SSH password is set, then the new base VM reboots
5. Ansible detects SSH becoming available, and takes over to do whatever you'd like.
6. Shut down the VM, and create clones as desired. Manage ongoing config in a variety of ways – rolling out a new VM for any change, continuing with Ansible, shifting to Puppet, etc.