Google Cloud Image creation on Google Cloud, part 2: Build process

In the previous post, we reviewed how the Windows Setup works and how it can be customized. works. In this post, we’ll take a look at how Windows Setup is used to build custom Windows base images.

Build process

The Windows Setup process can be summarized to comprise three basic steps:

  1. Boot WinPE from a DVD or over a network.
  2. Use WinPE to extract a Windows image (install.wim) to the installation disk.
  3. Boot from the disk and specialize the system.

For further details, see my previous post.

To create a Windows image from scratch, we obviously have to run Windows Setup. However, Google Cloud does not support virtual DVD drives, and also does not support network boot. Without the ability to boot WinPE from a DVD or over a network, how do we launch Windows Setup then?

Conveniently, the tools and scripts that Google Cloud uses for building images are all open source and available on GitHub. So let’s take a closer look at how they work and how they manage to solve this challenge.

Replacing WinPE

Windows Setup uses WinPE to kickstart the setup process because WinPE is small and can be booted from a DVD. But that does not mean only WinPE can be used to perform the initial setup steps: we can also boot a full Windows installation from a different disk and let that system handle the initial setup steps. As it turns out, that’s the key idea of how the image build works.

In a nutshell, the image build process works like this:

  1. Create a new, empty disk.
  2. Start a temporary Windows Server VM and let it extract a Windows image (install.wim) to the disk. Also, add any necessary drivers, configure Windows Setup so that it runs unattended, and make the disk bootable.
  3. Create another temporary VM and have it boot from the new disk to run Windows Setup.
  4. Throw away the temporary VMs and create an image from the disk.

To orchestrate all these steps, Google Cloud uses a tool called daisy. daisy is an open-source command-line tool that lets you execute workflows. Workflows are authored as JSON files and contain a sequence of steps. Each such step describes a Compute Engine operation – for example, creating a disk, or shutting down a VM instance. Daisy workflows are therefore well-suited to automate the steps required to build a Windows image from scratch.


The following diagram illustrates the first half of the build process as defined by the daisy workflows. I refer to this part as bootstrapping.


  1. Daisy creates a new, empty install disk. This disk later becomes the source of the image.
  2. Daisy creates a temporary VM instance called bootstrap-* that uses the following configuration:
    • The boot disk is created from an existing Windows Server Core image.
    • The install disk is attached as a secondary disk (d:).
    • A special script, bootstrap_install.ps1 is passed as a startup script. This script drives the following steps in the process.
  3. Executing bootstrap_install.ps1, the temporary instance downloads the Windows installation ISO from Cloud Storage and mounts it as a virtual DVD drive.
  4. The script extracts the install.wim image from the ISO to the install disk (d:).
  5. Once the install.wim has been extracted, the adds additional drivers and (optionally) updates to the install disk (d:).
  6. The script downloads Autounattend-template.xml from Cloud Storage and uses it as template to generate a unattend.xml file.
  7. Similarly, the script downloads SetupComplete.cmd from Cloud Storage and places it in the right folder so that it’s executed after Setup completes.
  8. Finally, the script makes the install disk (d:) bootable.
  9. Daisy deletes the temporary bootstrap VM as it has completed its job of preparing the install disk for a first boot.

The result of the process is a bootable disk that contains an extracted install.wim along with necessary drivers and updates. In addition, the disk contains an unattend.xml file that causes Windows Setup to run without user interaction and a custom SetupComplete.cmd.


Let’s continue with the second half of the build process, the actual installation:


  1. Daisy creates a temporary VM instance called install-* that uses the following configuration:
    • The install disk (result of the previous step) is used as boot disk.
    • A special script, post_install.ps1 is passed as a startup script. This script drives the later steps in the process
  2. The VM boots and Windows Setup kicks in. Setup finds the unattend.xml file and uses it to drive the specialize phase and oobeSystem phase without user interaction.
  3. Setup invokes SetupComplete.cmd This script installs the .NET framework, googet and the necessary components to run startup scripts.
  4. SetupComplete.cmd reboots the system.
  5. The system boots and runs post_install.ps1 as startup script. This script invokes Windows Update, applies a number of settings, and finally uses google to install remaining Compute Engine tools.
  6. Windows Setup is not complete and the system is fully usable – but it’s in a specialized state and thus not yet suitable to be turned into an image. Therefore, post-install.ps1 runs sysprep to generalize the system.
  7. Daisy deletes the temporary VM instance as its job is done.
  8. Finally, daisy takes the install disk and creates an image from it.

And that’s it! The result is an image containing a generalized copy of Windows including all the necessary drivers, tools, and settings to ensure that the system runs properly on Compute Engine.

To see what happens when you first boot the image, take a look at one of my previous posts, Understanding Windows VM initialization on Google Cloud.

Any opinions expressed on this blog are Johannes' own. Refer to the respective vendor’s product documentation for authoritative information.
« Back to home