Create custom Windows BYOL images


To create a Windows VM on Google Cloud, you must use a VM image that has Windows or Windows Server preinstalled. Google Cloud provides public images for commonly used versions of Windows Server, but these images are only suitable for on-demand licensing. To bring your own Windows license (BYOL), you must either import an existing image, or you must build a custom image.

This guide describes how you can create a custom image by using the same tools and processes that Google Cloud uses to create the public images.

To complete this guide, you need:

  • An ISO file containing the Windows or Windows Server installation media.
  • Optionally, one or more Windows update packages (in .msu format) to apply to the image.

Before you begin

  • If you haven't already, set up authentication. Authentication is the process by which your identity is verified for access to Google Cloud services and APIs. To run code or samples from a local development environment, you can authenticate to Compute Engine as follows.

    Select the tab for how you plan to use the samples on this page:

    Console

    When you use the Google Cloud console to access Google Cloud services and APIs, you don't need to set up authentication.

    gcloud

    1. Install the Google Cloud CLI, then initialize it by running the following command:

      gcloud init
    2. Set a default region and zone.

Understand the build process

To install Windows from scratch, a common approach is to boot a computer from a DVD or ISO file that contains the Windows installation files. Unlike some on-premises hypervisors, Compute Engine does not let you boot from an ISO file.

To install Windows from scratch, you must therefore follow a different approach that entails the following steps:

  1. Creating a new disk.
  2. Extracting the Windows image (install.wim from the installation media) to the disk.
  3. Adding necessary drivers, configuring Windows Setup so that it runs unattended, and making the disk bootable.
  4. Booting from the new disk to run Windows Setup.
  5. Installing additional software including the Guest OS agent.
  6. Creating an image from the disk.

Instead of performing these steps manually, this guide describes how you can use Cloud Build, the daisy tool, and the reference workflows which are available on GitHub to automate the process.

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 suitable to automate the steps required to build a Windows image from scratch.

The daisy workflows for building custom Windows images create two temporary VM instances. The first VM instance (prefixed bootstrap) performs the necessary steps to create a bootable disk. The second VM instance (prefixed install) runs Windows Setup and performs all the remaining steps.

Prepare the project for building images

To prevent the daisy tool from interfering with your existing VM instances or infrastructure, create a dedicated project for building images:

    Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.

    In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

    Make sure that billing is enabled for your Google Cloud project.

    In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

    Make sure that billing is enabled for your Google Cloud project.

  1. Enable the Compute Engine API, Cloud Build API, and Cloud Storage API.

    Enable APIs

The next steps differ depending on whether you use Windows or Linux on your local computer:

Windows

  1. On your local computer, open a Windows PowerShell window.
  2. Initialize a variable:

    $PROJECT_ID = "PROJECT_ID"
    

    where PROJECT_ID is the project ID of the Google Cloud project that you created in the previous section.

  3. Initialize another variable so that it contains the project number of the project:

    $PROJECT_NUMBER = gcloud projects describe $PROJECT_ID --format=value`(projectNumber`)
    
  4. Disable the default firewall rules for RDP and SSH access:

    gcloud compute firewall-rules update default-allow-rdp --project $PROJECT_ID --disabled
    gcloud compute firewall-rules update default-allow-ssh --project $PROJECT_ID --disabled
    
  5. Grant the Compute Instance Admin and Service Account User role to Cloud Build so that Cloud Build can create the temporary VM instances required to build images:

    gcloud projects add-iam-policy-binding $PROJECT_ID `
       --member serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com `
       --role roles/compute.instanceAdmin.v1
    gcloud projects add-iam-policy-binding $PROJECT_ID `
       --member serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com `
       --role roles/iam.serviceAccountUser
    

Linux

  1. On your local computer, open a terminal window.
  2. Initialize a variable:

    PROJECT_ID=PROJECT_ID
    

    where PROJECT_ID is the project ID of the Google Cloud project that you created in the previous section.

  3. Initialize another variable so that it contains the project number of the project:

    PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format=value\(projectNumber\)`
    
  4. Disable the default firewall rules for RDP and SSH access:

    gcloud compute firewall-rules update default-allow-rdp --project $PROJECT_ID --disabled
    gcloud compute firewall-rules update default-allow-ssh --project $PROJECT_ID --disabled
    
  5. Grant the Compute Instance Admin and Service Account User role to Cloud Build so that Cloud Build can create the temporary VM instances required to build images:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
       --member serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
       --role roles/compute.instanceAdmin.v1
    gcloud projects add-iam-policy-binding $PROJECT_ID \
       --member serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
       --role roles/iam.serviceAccountUser
    

Upload installation files

You now collect all the installation files required for the custom image and upload them to a Cloud Storage bucket. By storing the files in a Cloud Storage bucket, you ensure that the files are accessible by daisy and by the temporary VM instances that daisy uses to build the image.

  1. On your local computer, download required installation packages:

  2. Create a new Cloud Storage bucket to store the installation files:

    gsutil mb -p $PROJECT_ID gs://$PROJECT_ID-media
    
  3. Grant the Storage Object Viewer role to Cloud Build so that it can read the installation files:

    gsutil iam ch serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com:objectViewer  gs://$PROJECT_ID-media
    
  4. Upload the PowerShell installation package:

    gsutil cp POWERSHELL_PACKAGE gs://$PROJECT_ID-media/PowerShell.msi
    

    where POWERSHELL_PACKAGE is the path to the PowerShell installation package.

  5. Upload the .NET Framework installation package:

    gsutil cp DOTNET_PACKAGE gs://$PROJECT_ID-media/dotnet-sdk.exe
    

    where DOTNET_PACKAGE is the path to the NET Framework installation package.

  6. Upload the gcloud CLI installation package:

    gsutil cp CLOUDSDK_PACKAGE gs://$PROJECT_ID-media/GoogleCloudSDKInstaller.exe
    

    where CLOUDSDK_PACKAGE is the path to the gcloud CLI installation package.

  7. Upload the ISO file containing your Windows installation media:

    gsutil cp ISO gs://$PROJECT_ID-media/
    

    where ISO is the name of the ISO file.

  8. Optionally, upload additional update packages:

    gsutil cp UPDATE_DIR/*.msu gs://$PROJECT_ID-media/updates/
    

    where UPDATE_DIR is the directory containing the update packages.

You are now ready to build the custom image.

Build the image

Executing the daisy workflow to build a custom image takes up to four hours. Instead of running daisy locally, you now create a Cloud Build configuration so that you can let Cloud Build execute the workflow in the background.

  1. On your local computer, clone the Git repository containing the daisy workflows for building Windows images:

    git clone https://github.com/GoogleCloudPlatform/compute-image-tools.git
    
  2. Switch to windows directory:

    cd compute-image-tools/daisy_workflows/image_build/windows/
    
  3. In the windows directory, you find a selection of files with the suffix .wf.json. These files contain Daisy workflow definitions for commonly used Windows versions:

    Windows version Workflow file
    Windows Server 2019 (64-bit) windows-server-2019-dc-uefi-byol.wf.json
    Windows Server Core 2019 (64-bit) windows-server-2019-dc-core-uefi-byol.wf.json
    Windows Server 2016 (64-bit) windows-server-2016-dc-uefi-byol.wf.json
    Windows Server Core 2016 (64-bit) windows-server-2016-dc-core-uefi-byol.wf.json
    Windows 11 (64-bit) windows-11-21h2-ent-x64-uefil.wf.json
    Windows 10 (64-bit) windows-10-21h2-ent-x64-uefil.wf.json

    Open the workflow file that most closely matches the Windows version that you want to install. If necessary, change the Windows edition (edition) and license key (product_key) settings in the workflow files so that they match your installation media.

    If you are unsure about the correct edition name, open an elevated PowerShell prompt and run the following commands to list all editions supported by your installation media:

    $IsoFile = "ISO"
    
    $Mount = Mount-DiskImage -ImagePath (Resolve-Path $IsoFile)
    
    $DriveLetter = ($Mount | Get-Volume).DriveLetter
    Get-WindowsImage -ImagePath "$($DriveLetter):\sources\install.wim" | select ImageName
    
    Dismount-DiskImage -InputObject $Mount | Out-Null
    

    Replace ISO with the local path to the ISO image.

  4. In the windows directory, create a new file named cloudbuild.yaml and paste the following code:

    timeout: 14400s  # 4 hour timeout for entire build
    steps:
    - name: 'gcr.io/compute-image-tools/daisy'
      timeout: 14400s  # 4 hour timeout for build step
      args:
        - -project=$PROJECT_ID
        - -zone=us-central1-a
        - -var:updates=gs://$PROJECT_ID-media/updates/
        - -var:pwsh=gs://$PROJECT_ID-media/PowerShell.msi
        - -var:dotnet48=gs://$PROJECT_ID-media/dotnet-sdk.exe
        - -var:cloudsdk=gs://$PROJECT_ID-media/GoogleCloudSDKInstaller.exe
        - -var:media=gs://$PROJECT_ID-media/ISO
        - WORKFLOW
    

    Replace:

    • ISO: name of the ISO file on Cloud Storage.
    • WORKFLOW: name of the workflow file that corresponds to the Windows version that you're using.
  5. Submit the build to Cloud Build:

    gcloud builds submit --project $PROJECT_ID --async
    

    The build takes around up to four hours to complete. You can track the status of the build in the Google Cloud console under Cloud Build > History.

    Cloud Build History

Use the custom image

After the build has completed, you can find the custom BYOL image in the Google Cloud console under Compute Engine > Images.

To help differentiate between multiple versions of the same image, the build process embeds a timestamp into the image name, for example windows-server-2019-dc-v1613488342. In addition, the process associates the image with a custom image family, for example windows-server-2019.

To create a VM instance that uses the custom BYOL image, you must provision the VM instance on a sole-tenant node.

Troubleshooting

If you suspect that the build process failed or is not progressing, use the following approaches to diagnose the situation:

  • Verify that you've uploaded the right installation packages and ISO file.
  • Verify that you selected a workflow that matches the Windows version of the ISO file.
  • Review the build log in Cloud Build and check whether there are any error messages.
  • If the build seems stuck, review the serial port output of the VM instance created by the build and check for error messages.

What's next