יצירה וניהול של מכונות וירטואליות של Windows Server

‫Compute Engine מספק תמונות ציבוריות עם Windows Server שבהן אפשר להשתמש כדי ליצור מכונות. הוראות ליצירת מכונה של Windows Server עם SQL Server שכבר מותקן מראש מופיעות במאמר יצירת מכונות של SQL Server.

מידע כללי נוסף על מופעי Windows Server ואפליקציות Windows שאפשר להריץ ב-Compute Engine זמין במאמר בנושא Windows ב-Compute Engine.

תמחור

לפני שמתחילים

יצירת מכונה של Windows Server

כדי ליצור מכונה עם Windows Server, צריך לציין את משפחת התמונות של הגרסה הספציפית של Windows שאתם צריכים. ב-Compute Engine יש כמה גרסאות של Windows Server, ורובן זמינות כתמונות של מכונות וירטואליות מוגנות. תמונות של מכונות וירטואליות מוגנות מציעות אמצעי אבטחה כמו קושחה שתואמת ל-UEFI, אתחול מאובטח ואתחול מדוד שמוגן על ידי vTPM. רשימה של משפחות התמונות הזמינות מופיעה במאמר תמונות ציבוריות.

אם אתם צריכים יותר משני סשנים בו-זמניים של שולחן עבודה מרוחק, תצטרכו לרכוש רישיונות גישת לקוח (CAL) של Remote Desktop Session ‏ (RDS). מידע נוסף זמין במאמר רישוי של פריסת RDS באמצעות רישיונות גישת לקוח (CAL).

עבודה עם Microsoft Active Directory

אם אתם מתכננים להשתמש ב-Microsoft Active Directory ‏ (AD) עם המופע החדש, הקפידו שאורך שם המופע לא יעלה על 15 תווים, כדי לעמוד במגבלות האורך המקסימלי של השם שצוינו במערכת.

ב-AD נעשה שימוש בשמות NetBIOS של מכונות, שנוצרים כשם המכונה, אבל מקוצרים ל-15 תווים. כתוצאה מכך, יכול להיות שתיתקלו בשגיאה הבאה כשאתם מנסים להיכנס כמשתמשים בדומיין:The Security Database on the Server does not have a Computer Account for this Workstation Trust Relationship.

יצירת מכונת Windows Server שמשתמשת בכתובת IP חיצונית להפעלה

בקטע הזה מוסבר איך ליצור מכונת Windows Server עם כתובת IP חיצונית. צריך להגדיר את רשת ה-VPC כך שתהיה גישה אל kms.windows.googlecloud.com.

המסוף

כדי ליצור מכונת Windows וירטואלית בסיסית:

  1. נכנסים לדף Create an instance במסוף Google Cloud .

    כניסה לדף Create an instance

  2. בקטע דיסק אתחול, לוחצים על שינוי ופועלים לפי השלבים הבאים:

    1. בכרטיסייה תמונות ציבוריות, בוחרים מערכת הפעלה של Windows Server.
    2. לוחצים על בחירה.
  3. כדי ליצור את המכונה הווירטואלית (VM), לוחצים על האפשרות Create.

כדי ליצור מכונת מכונה וירטואלית מוגנת עם מערכת הפעלה Windows:

  1. נכנסים לדף Create an instance במסוף Google Cloud .

    כניסה לדף Create an instance

  2. בקטע דיסק אתחול, לוחצים על שינוי ופועלים לפי השלבים הבאים:

    1. בכרטיסייה Public images, בוחרים מערכת הפעלה של Windows Server.
    2. כדי לשמור את ההגדרה של דיסק האתחול, לוחצים על Select.
  3. אם רוצים לשנות את ההגדרות של המכונה הווירטואלית המוגנת, מרחיבים את הקטע Advanced options. לאחר מכן, מבצעים את הפעולות הבאות:

    1. מרחיבים את הקטע Security.
    2. כדי להשבית את האתחול המאובטח, מבטלים את הסימון בתיבת הסימון הפעלת אתחול מאובטח. אתחול מאובטח עוזר להגן על מופעי המכונות הווירטואליות מפני תוכנות זדוניות וערכות rootkit ברמת האתחול וברמת הליבה. מידע נוסף זמין במאמר בנושא אתחול מאובטח.
    3. כדי להשבית את מודול הפלטפורמה הווירטואלית המהימנה (vTPM), מבטלים את הסימון בתיבת הסימון הפעלת vTPM. ה-vTPM מאפשר אתחול מדוד, שמאמת את תקינות המכונה הווירטואלית לפני האתחול ובמהלכו. למידע נוסף, קראו את המאמר מודול פלטפורמה מהימנה וירטואלי (vTPM).

    4. כדי להשבית את המעקב אחר התקינות, מבטלים את הסימון בתיבה הפעלת מעקב אחר התקינות. באמצעות Cloud Monitoring אפשר לעקוב אחרי תקינות האתחול של מכונות וירטואליות מוגנות. מידע נוסף זמין במאמר מעקב אחר תקינות.

  4. כדי ליצור את המכונה הווירטואלית (VM), לוחצים על האפשרות Create.

gcloud

משתמשים בפקודה compute images list כדי לראות רשימה של תמונות Windows Server זמינות:

gcloud compute images list --project windows-cloud --no-standard-images

כדי לבדוק אם תמונה תומכת בתכונות של מכונה וירטואלית מוגנת, מריצים את הפקודה הבאה ובודקים אם הפלט כולל את UEFI_COMPATIBLE:

gcloud compute images describe IMAGE_NAME --project windows-cloud

מחליפים את IMAGE_NAME בשם של קובץ האימג' שרוצים לבדוק אם הוא תומך בתכונות של מכונה וירטואלית מוגנת.

משתמשים בפקודה compute instances create כדי ליצור מכונה חדשה ולציין את משפחת האימג' לאחד מקובצי האימג' הציבוריים של Windows Server.

gcloud compute instances create INSTANCE_NAME \
    --image-project windows-cloud \
    --image-family IMAGE_FAMILY \
    --machine-type MACHINE_TYPE \
    --boot-disk-size BOOT_DISK_SIZE \
    --boot-disk-type BOOT_DISK_TYPE

מחליפים את מה שכתוב בשדות הבאים:

אם בחרתם תמונה שתומכת ב-Shielded VM, אתם יכולים לשנות את ההגדרות של ה-Shielded VM של המופע באמצעות אחד מהדגלים הבאים:

  • ‫--no-shielded-secure-boot: השבתה של אתחול מאובטח. אתחול מאובטח עוזר להגן על מופעי מכונות וירטואליות מפני תוכנות זדוניות וערכות rootkit ברמת האתחול וברמת הליבה. מידע נוסף זמין במאמר בנושא אתחול מאובטח.
  • ‫--no-shielded-vtpm: השבתה של מודול פלטפורמה וירטואלית מהימנה (vTPM). ה-vTPM מאפשר אתחול מדוד, שמאמת את תקינות המכונה הווירטואלית לפני האתחול ובמהלכו. מידע נוסף זמין במאמר מודול פלטפורמה מהימנה וירטואלי (vTPM).

  • ‫--no-shielded-integrity-monitoring: השבתה של מעקב אחר תקינות. ניטור התקינות מאפשר לכם לעקוב אחרי תקינות האתחול של מכונות וירטואליות מוגנות באמצעות Cloud Monitoring. מידע נוסף זמין במאמר מעקב אחר תקינות.

בדוגמה הבאה נוצרת מכונה וירטואלית מוגנת של Windows 2022 עם השבתה של אתחול מאובטח:

gcloud compute instances create my-instance \
    --image-family windows-2022 --image-project windows-cloud \
    --no-shielded-secure-boot

המשך

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "cloud.google.com/go/compute/apiv1/computepb"
	"google.golang.org/protobuf/proto"
)

// createWndowsServerInstanceExternalIP creates a new Windows Server instance
// that has an external IP address.
func createWndowsServerInstanceExternalIP(
	w io.Writer,
	projectID, zone, instanceName, machineType, sourceImageFamily string,
) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"
	// machineType := "n1-standard-1"
	// sourceImageFamily := "windows-2022"

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	disk := &computepb.AttachedDisk{
		// Describe the size and source image of the boot disk to attach to the instance.
		InitializeParams: &computepb.AttachedDiskInitializeParams{
			DiskSizeGb: proto.Int64(64),
			SourceImage: proto.String(
				fmt.Sprintf(
					"projects/windows-cloud/global/images/family/%s",
					sourceImageFamily,
				),
			),
		},
		AutoDelete: proto.Bool(true),
		Boot:       proto.Bool(true),
	}

	network := &computepb.NetworkInterface{
		// If you are using a custom VPC network it must be configured
		// to allow access to kms.windows.googlecloud.com.
		// https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server.
		Name: proto.String("global/networks/default"),
		AccessConfigs: []*computepb.AccessConfig{
			{
				Type: proto.String("ONE_TO_ONE_NAT"),
				Name: proto.String("External NAT"),
			},
		},
	}

	inst := &computepb.Instance{
		Name: proto.String(instanceName),
		Disks: []*computepb.AttachedDisk{
			disk,
		},
		MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/%s", zone, machineType)),
		NetworkInterfaces: []*computepb.NetworkInterface{
			network,
		},
		// If you chose an image that supports Shielded VM,
		// you can optionally change the instance's Shielded VM settings.
		// ShieldedInstanceConfig: &computepb.ShieldedInstanceConfig{
		// 	EnableSecureBoot: proto.Bool(true),
		// 	EnableVtpm: proto.Bool(true),
		// 	EnableIntegrityMonitoring: proto.Bool(true),
		// },
	}

	req := &computepb.InsertInstanceRequest{
		Project:          projectID,
		Zone:             zone,
		InstanceResource: inst,
	}

	op, err := instancesClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create instance: %w", err)
	}

	if err = op.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Instance created\n")

	return nil
}

Java


import com.google.cloud.compute.v1.AccessConfig;
import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.InsertInstanceRequest;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateWindowsServerInstanceExternalIp {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // projectId - ID or number of the project you want to use.
    String projectId = "your-google-cloud-project-id";

    // zone - Name of the zone you want to use, for example: us-west3-b
    String zone = "europe-central2-b";

    // instanceName - Name of the new machine.
    String instanceName = "instance-name";

    createWindowsServerInstanceExternalIp(projectId, zone, instanceName);
  }

  // Creates a new Windows Server instance that has an external IP address.
  public static void createWindowsServerInstanceExternalIp(String projectId, String zone,
      String instanceName)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {

    // machineType - Machine type you want to create in following format:
    //  *    "zones/{zone}/machineTypes/{type_name}". For example:
    //  *    "zones/europe-west3-c/machineTypes/f1-micro"
    //  *    You can find the list of available machine types using:
    //  *    https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
    String machineType = "n1-standard-1";
    // sourceImageFamily - Name of the public image family for Windows Server or SQL Server images.
    //  *    https://cloud.google.com/compute/docs/images#os-compute-support
    String sourceImageFamily = "windows-2022";

    // Instantiates a client.
    try (InstancesClient instancesClient = InstancesClient.create()) {

      AttachedDisk attachedDisk = AttachedDisk.newBuilder()
          // Describe the size and source image of the boot disk to attach to the instance.
          .setInitializeParams(AttachedDiskInitializeParams.newBuilder()
              .setDiskSizeGb(64)
              .setSourceImage(
                  String.format("projects/windows-cloud/global/images/family/%s",
                      sourceImageFamily))
              .build())
          .setAutoDelete(true)
          .setBoot(true)
          .setType(AttachedDisk.Type.PERSISTENT.toString())
          .build();

      Instance instance = Instance.newBuilder()
          .setName(instanceName)
          .setMachineType(String.format("zones/%s/machineTypes/%s", zone, machineType))
          .addDisks(attachedDisk)
          .addNetworkInterfaces(NetworkInterface.newBuilder()
              .addAccessConfigs(AccessConfig.newBuilder()
                  .setType("ONE_TO_ONE_NAT")
                  .setName("External NAT")
                  .build())
              // If you're going to use a custom VPC network, it must be configured
              // to allow access to kms.windows.googlecloud.com.
              // https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server.
              .setName("global/networks/default")
              .build())
          // If you chose an image that supports Shielded VM, you can optionally change the
          // instance's Shielded VM settings.
          // .setShieldedInstanceConfig(ShieldedInstanceConfig.newBuilder()
          //    .setEnableSecureBoot(true)
          //    .setEnableVtpm(true)
          //    .setEnableIntegrityMonitoring(true)
          //    .build())
          .build();

      InsertInstanceRequest request = InsertInstanceRequest.newBuilder()
          .setProject(projectId)
          .setZone(zone)
          .setInstanceResource(instance)
          .build();

      // Wait for the operation to complete.
      Operation operation = instancesClient.insertAsync(request).get(5, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.printf("Error in creating instance %s", operation.getError());
        return;
      }

      System.out.printf("Instance created %s", instanceName);
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const instanceName = 'YOUR_INSTANCE_NAME';
// const machineType = 'n1-standard-1';
// const sourceImageFamily = 'windows-2022';

const compute = require('@google-cloud/compute');

async function createWindowsServerInstanceExpernalIP() {
  const instancesClient = new compute.InstancesClient();

  const [response] = await instancesClient.insert({
    instanceResource: {
      name: instanceName,
      disks: [
        {
          // Describe the size and source image of the boot disk to attach to the instance.
          initializeParams: {
            diskSizeGb: '64',
            sourceImage: `projects/windows-cloud/global/images/family/${sourceImageFamily}/`,
          },
          autoDelete: true,
          boot: true,
          type: 'PERSISTENT',
        },
      ],
      machineType: `zones/${zone}/machineTypes/${machineType}`,
      networkInterfaces: [
        {
          accessConfigs: [
            {
              type: 'ONE_TO_ONE_NAT',
              name: 'External NAT',
            },
          ],
          // If you are using a custom VPC network it must be configured to allow access to kms.windows.googlecloud.com.
          // https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server.
          name: 'global/networks/default',
        },
      ],
      // If you chose an image that supports Shielded VM, you can optionally change the instance's Shielded VM settings.
      // "shieldedInstanceConfig": {
      //   "enableSecureBoot": true,
      //   "enableVtpm": true,
      //   "enableIntegrityMonitoring": true
      // },
    },
    project: projectId,
    zone,
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.ZoneOperationsClient();

  // Wait for the create operation to complete.
  while (operation.status !== 'DONE') {
    [operation] = await operationsClient.wait({
      operation: operation.name,
      project: projectId,
      zone: operation.zone.split('/').pop(),
    });
  }

  console.log('Instance created.');
}

createWindowsServerInstanceExpernalIP();

Python

from __future__ import annotations

import re
import sys
from typing import Any
import warnings

from google.api_core.extended_operation import ExtendedOperation
from google.cloud import compute_v1


def get_image_from_family(project: str, family: str) -> compute_v1.Image:
    """
    Retrieve the newest image that is part of a given family in a project.

    Args:
        project: project ID or project number of the Cloud project you want to get image from.
        family: name of the image family you want to get image from.

    Returns:
        An Image object.
    """
    image_client = compute_v1.ImagesClient()
    # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
    newest_image = image_client.get_from_family(project=project, family=family)
    return newest_image


def disk_from_image(
    disk_type: str,
    disk_size_gb: int,
    boot: bool,
    source_image: str,
    auto_delete: bool = True,
) -> compute_v1.AttachedDisk:
    """
    Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
    source for the new disk.

    Args:
         disk_type: the type of disk you want to create. This value uses the following format:
            "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
            For example: "zones/us-west3-b/diskTypes/pd-ssd"
        disk_size_gb: size of the new disk in gigabytes
        boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
        source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
            of the publicly available images or an image from one of your projects.
            This value uses the following format: "projects/{project_name}/global/images/{image_name}"
        auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it

    Returns:
        AttachedDisk object configured to be created using the specified image.
    """
    boot_disk = compute_v1.AttachedDisk()
    initialize_params = compute_v1.AttachedDiskInitializeParams()
    initialize_params.source_image = source_image
    initialize_params.disk_size_gb = disk_size_gb
    initialize_params.disk_type = disk_type
    boot_disk.initialize_params = initialize_params
    # Remember to set auto_delete to True if you want the disk to be deleted when you delete
    # your VM instance.
    boot_disk.auto_delete = auto_delete
    boot_disk.boot = boot
    return boot_disk


def wait_for_extended_operation(
    operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
) -> Any:
    """
    Waits for the extended (long-running) operation to complete.

    If the operation is successful, it will return its result.
    If the operation ends with an error, an exception will be raised.
    If there were any warnings during the execution of the operation
    they will be printed to sys.stderr.

    Args:
        operation: a long-running operation you want to wait on.
        verbose_name: (optional) a more verbose name of the operation,
            used only during error and warning reporting.
        timeout: how long (in seconds) to wait for operation to finish.
            If None, wait indefinitely.

    Returns:
        Whatever the operation.result() returns.

    Raises:
        This method will raise the exception received from `operation.exception()`
        or RuntimeError if there is no exception set, but there is an `error_code`
        set for the `operation`.

        In case of an operation taking longer than `timeout` seconds to complete,
        a `concurrent.futures.TimeoutError` will be raised.
    """
    result = operation.result(timeout=timeout)

    if operation.error_code:
        print(
            f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
            file=sys.stderr,
            flush=True,
        )
        print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
        raise operation.exception() or RuntimeError(operation.error_message)

    if operation.warnings:
        print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
        for warning in operation.warnings:
            print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)

    return result


def create_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    disks: list[compute_v1.AttachedDisk],
    machine_type: str = "n1-standard-1",
    network_link: str = "global/networks/default",
    subnetwork_link: str = None,
    internal_ip: str = None,
    external_access: bool = False,
    external_ipv4: str = None,
    accelerators: list[compute_v1.AcceleratorConfig] = None,
    preemptible: bool = False,
    spot: bool = False,
    instance_termination_action: str = "STOP",
    custom_hostname: str = None,
    delete_protection: bool = False,
) -> compute_v1.Instance:
    """
    Send an instance creation request to the Compute Engine API and wait for it to complete.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        disks: a list of compute_v1.AttachedDisk objects describing the disks
            you want to attach to your new instance.
        machine_type: machine type of the VM being created. This value uses the
            following format: "zones/{zone}/machineTypes/{type_name}".
            For example: "zones/europe-west3-c/machineTypes/f1-micro"
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
            "regions/{region}/subnetworks/{subnetwork_name}"
        internal_ip: internal IP address you want to assign to the new instance.
            By default, a free address from the pool of available internal IP addresses of
            used subnet will be used.
        external_access: boolean flag indicating if the instance should have an external IPv4
            address assigned.
        external_ipv4: external IPv4 address to be assigned to this instance. If you specify
            an external IP address, it must live in the same region as the zone of the instance.
            This setting requires `external_access` to be set to True to work.
        accelerators: a list of AcceleratorConfig objects describing the accelerators that will
            be attached to the new instance.
        preemptible: boolean value indicating if the new instance should be preemptible
            or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
        spot: boolean value indicating if the new instance should be a Spot VM or not.
        instance_termination_action: What action should be taken once a Spot VM is terminated.
            Possible values: "STOP", "DELETE"
        custom_hostname: Custom hostname of the new VM instance.
            Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
        delete_protection: boolean value indicating if the new virtual machine should be
            protected against deletion or not.
    Returns:
        Instance object.
    """
    instance_client = compute_v1.InstancesClient()

    # Use the network interface provided in the network_link argument.
    network_interface = compute_v1.NetworkInterface()
    network_interface.network = network_link
    if subnetwork_link:
        network_interface.subnetwork = subnetwork_link

    if internal_ip:
        network_interface.network_i_p = internal_ip

    if external_access:
        access = compute_v1.AccessConfig()
        access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
        access.name = "External NAT"
        access.network_tier = access.NetworkTier.PREMIUM.name
        if external_ipv4:
            access.nat_i_p = external_ipv4
        network_interface.access_configs = [access]

    # Collect information into the Instance object.
    instance = compute_v1.Instance()
    instance.network_interfaces = [network_interface]
    instance.name = instance_name
    instance.disks = disks
    if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
        instance.machine_type = machine_type
    else:
        instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"

    instance.scheduling = compute_v1.Scheduling()
    if accelerators:
        instance.guest_accelerators = accelerators
        instance.scheduling.on_host_maintenance = (
            compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
        )

    if preemptible:
        # Set the preemptible setting
        warnings.warn(
            "Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
        )
        instance.scheduling = compute_v1.Scheduling()
        instance.scheduling.preemptible = True

    if spot:
        # Set the Spot VM setting
        instance.scheduling.provisioning_model = (
            compute_v1.Scheduling.ProvisioningModel.SPOT.name
        )
        instance.scheduling.instance_termination_action = instance_termination_action

    if custom_hostname is not None:
        # Set the custom hostname for the instance
        instance.hostname = custom_hostname

    if delete_protection:
        # Set the delete protection bit
        instance.deletion_protection = True

    # Prepare the request to insert an instance.
    request = compute_v1.InsertInstanceRequest()
    request.zone = zone
    request.project = project_id
    request.instance_resource = instance

    # Wait for the create operation to complete.
    print(f"Creating the {instance_name} instance in {zone}...")

    operation = instance_client.insert(request=request)

    wait_for_extended_operation(operation, "instance creation")

    print(f"Instance {instance_name} created.")
    return instance_client.get(project=project_id, zone=zone, instance=instance_name)


def create_windows_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    machine_type: str,
    source_image_family: str = "windows-2022",
    network_link: str = "global/networks/default",
    subnetwork_link: str | None = None,
) -> compute_v1.Instance:
    """
    Creates a new Windows Server instance that has only an internal IP address.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        machine_type: machine type you want to create in following format:
            "zones/{zone}/machineTypes/{type_name}". For example:
            "zones/europe-west3-c/machineTypes/f1-micro"
            You can find the list of available machine types using:
            https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
        source_image_family: name of the public image family for Windows Server or SQL Server images.
            https://cloud.google.com/compute/docs/images#os-compute-support
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
           "regions/{region}/subnetworks/{subnetwork_name}"

    Returns:
        Instance object.
    """
    if subnetwork_link is None:
        subnetwork_link = f"regions/{zone}/subnetworks/default"

    base_image = get_image_from_family(
        project="windows-cloud", family=source_image_family
    )
    disk_type = f"zones/{zone}/diskTypes/pd-standard"
    disks = [disk_from_image(disk_type, 100, True, base_image.self_link, True)]

    # You must verify or configure routes and firewall rules in your VPC network
    # to allow access to kms.windows.googlecloud.com.
    # More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server

    # Additionally, you must enable Private Google Access for subnets in your VPC network
    # that contain Windows instances with only internal IP addresses.
    # More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling

    instance = create_instance(
        project_id,
        zone,
        instance_name,
        disks,
        machine_type=machine_type,
        network_link=network_link,
        subnetwork_link=subnetwork_link,
        external_access=True,  # Set this to False to disable external IP for your instance
    )
    return instance

REST

כדי ליצור מכונה באמצעות ה-API, צריך לכלול את המאפיין initializeParams בבקשה ליצירת המכונה ולציין קובץ אימג' של Windows. לדוגמה, גוף הבקשה יכול להיראות כך:

instance = {
  "name": "INSTANCE_NAME",
  "machineType": "zones/ZONE/machineTypes/MACHINE_TYPE",
  "disks": [{
      "boot": "true",
      "type": "PERSISTENT",
      "initializeParams": {
         "diskName": "DISK_NAME",
         "sourceImage": "https://www.googleapis.com/compute/v1/projects/windows-cloud/global/images/family/IMAGE_FAMILY",
         "diskSizeGb": "BOOT_DISK_SIZE",
         "diskType": "BOOT_DISK_TYPE",
       }
    }],
  "networkInterfaces": [{
    "accessConfigs": [{
      "type": "ONE_TO_ONE_NAT",
      "name": "External NAT"
     }],
    "network": "global/networks/default"
  }],
  "serviceAccounts": [{
       "email": DEFAULT_SERVICE_EMAIL,
       "scopes": DEFAULT_SCOPES
  }]
}

מחליפים את ה-placeholders הבאים בערכים תקינים:

אם בחרתם תמונה שתומכת ב-מכונה וירטואלית מוגנת, אתם יכולים לשנות את ההגדרות של מכונה וירטואלית מוגנת של המופע באמצעות הפריטים הבוליאניים הבאים בגוף הבקשה:

  • ‫enableSecureBoot: הפעלה או השבתה של אתחול מאובטח. אתחול מאובטח עוזר להגן על מופעים של מכונות וירטואליות מפני תוכנות זדוניות וערכות rootkit ברמת האתחול וברמת הליבה. מידע נוסף זמין במאמר אתחול מאובטח.
  • ‫enableVtpm: הפעלה או השבתה של מודול הפלטפורמה המהימנה הווירטואלית (vTPM). ה-vTPM מאפשר אתחול מדוד, שמאמת את התקינות של המכונה הווירטואלית לפני האתחול ובמהלכו. מידע נוסף זמין במאמר מודול פלטפורמה מהימנה וירטואלי (vTPM).

  • ‫enableIntegrityMonitoring: הפעלה או השבתה של מעקב אחרי תקינות. מעקב אחרי התקינות מאפשר לכם לעקוב אחרי תקינות ההפעלה בזמן הריצה של המכונות הווירטואליות המוגנות, ולאמת אותה באמצעות דוחות של Cloud Monitoring. מידע נוסף זמין במאמר מעקב אחר תקינות.

מידע נוסף על יצירת מופע זמין במאמר בנושא השיטה instances.insert().

אחרי שיוצרים את מכונת Windows או SQL Server, צריך להגדיר את הסיסמה הראשונית למכונה כדי שתוכלו להתחבר למכונה דרך RDP.

בנוסף, אפשר לצרף את המכונה הווירטואלית לדומיין של שירות מנוהל ל-Microsoft AD, במהלך יצירת המכונה הווירטואלית או אחרי יצירתה. מידע נוסף זמין במאמר איך מצרפים באופן אוטומטי VM של Windows לדומיין.

יצירת מכונת Windows Server שמשתמשת בכתובת IP פנימית כדי להפעיל אותה

כדי ליצור מכונת Windows Server שיש לה רק כתובת IP פנימית, צריך לאמת או להגדיר מסלולים וכללי חומת אש ברשת ה-VPC כדי לאפשר גישה אל kms.windows.googlecloud.com. בנוסף, צריך להפעיל גישה פרטית ל-Google ברשתות המשנה של רשת ה-VPC שמכילות מכונות של Windows עם כתובות IP פנימיות בלבד.

gcloud

כשיוצרים מופע חדש באמצעות ה-CLI של gcloud, אפשר להשתמש בדגל --no-address כדי לוודא שלא מוקצית לו כתובת IP חיצונית:

gcloud compute instances create INSTANCE_NAME --network NETWORK_NAME \
 --subnet SUBNET_NAME \
 --no-address \
 --zone ZONE \
 --image-project windows-cloud \
 --image-family IMAGE_FAMILY \
 --machine-type MACHINE_TYPE \
 --boot-disk-size BOOT_DISK_SIZE \
 --boot-disk-type BOOT_DISK_TYPE

מחליפים את ה-placeholders הבאים בערכים תקינים:

  • ‫INSTANCE_NAME: שם למופע החדש.
  • ‫SUBNET_NAME: השם של תת-הרשת ברשת ה-VPC שבה המכונה תשתמש. רשת המשנה צריכה להיות באותו אזור כמו התחום שבוחרים עבור המכונה.
  • ‫IMAGE_FAMILY: אחת ממשפחות התמונות הציבוריות של תמונות Windows Server.
  • ‫MACHINE_TYPE: אחד מסוגי המכונות הזמינים.
  • ‫BOOT_DISK_SIZE: גודל דיסק האתחול ב-GiB. לדיסקים גדולים יותר יש תפוקה גבוהה יותר.
  • ‫BOOT_DISK_TYPE: הסוג של דיסק האתחול של המכונה. לדוגמה, hyperdisk-balanced או pd-ssd.

המשך

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "cloud.google.com/go/compute/apiv1/computepb"
	"google.golang.org/protobuf/proto"
)

// createWndowsServerInstanceInternalIP creates a new Windows Server instance
// that has only an internal IP address.
func createWndowsServerInstanceInternalIP(
	w io.Writer,
	projectID, zone, instanceName, machineType, sourceImageFamily, networkLink, subnetworkLink string,
) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"
	// machineType := "n1-standard-1"
	// sourceImageFamily := "windows-2022"
	// networkLink := "global/networks/default"
	// subnetworkLink := "regions/europe-central2/subnetworks/default"

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	disk := &computepb.AttachedDisk{
		// Describe the size and source image of the boot disk to attach to the instance.
		InitializeParams: &computepb.AttachedDiskInitializeParams{
			DiskSizeGb: proto.Int64(64),
			SourceImage: proto.String(
				fmt.Sprintf(
					"projects/windows-cloud/global/images/family/%s",
					sourceImageFamily,
				),
			),
		},
		AutoDelete: proto.Bool(true),
		Boot:       proto.Bool(true),
	}

	network := &computepb.NetworkInterface{
		// You must verify or configure routes and firewall rules in your VPC network
		// to allow access to kms.windows.googlecloud.com.
		// More information about access to kms.windows.googlecloud.com:
		// https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server

		// Additionally, you must enable Private Google Access for subnets in your VPC network
		// that contain Windows instances with only internal IP addresses.
		// More information about Private Google Access:
		// https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
		Name:       proto.String(networkLink),
		Subnetwork: proto.String(subnetworkLink),
	}

	inst := &computepb.Instance{
		Name: proto.String(instanceName),
		Disks: []*computepb.AttachedDisk{
			disk,
		},
		MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/%s", zone, machineType)),
		NetworkInterfaces: []*computepb.NetworkInterface{
			network,
		},
		// If you chose an image that supports Shielded VM,
		// you can optionally change the instance's Shielded VM settings.
		// ShieldedInstanceConfig: &computepb.ShieldedInstanceConfig{
		// 	EnableSecureBoot: proto.Bool(true),
		// 	EnableVtpm: proto.Bool(true),
		// 	EnableIntegrityMonitoring: proto.Bool(true),
		// },
	}

	req := &computepb.InsertInstanceRequest{
		Project:          projectID,
		Zone:             zone,
		InstanceResource: inst,
	}

	op, err := instancesClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create instance: %w", err)
	}

	if err = op.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Instance created\n")

	return nil
}

Java


import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.InsertInstanceRequest;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import com.google.cloud.compute.v1.Operation;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateWindowsServerInstanceInternalIp {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // projectId - ID or number of the project you want to use.
    String projectId = "your-google-cloud-project-id";

    // zone - Name of the zone you want to use, for example: us-west3-b
    String zone = "europe-central2-b";

    // instanceName - Name of the new machine.
    String instanceName = "instance-name";

    // networkLink - Name of the network you want the new instance to use.
    //  *   For example: "global/networks/default" represents the network
    //  *   named "default", which is created automatically for each project.
    String networkLink = "global/networks/default";

    // subnetworkLink - Name of the subnetwork you want the new instance to use.
    //  *   This value uses the following format:
    //  *   "regions/{region}/subnetworks/{subnetwork_name}"
    String subnetworkLink = "regions/europe-central2/subnetworks/default";

    createWindowsServerInstanceInternalIp(projectId, zone, instanceName, networkLink,
        subnetworkLink);
  }

  // Creates a new Windows Server instance that has only an internal IP address.
  public static void createWindowsServerInstanceInternalIp(String projectId, String zone,
      String instanceName, String networkLink, String subnetworkLink)
      throws IOException, ExecutionException, InterruptedException, TimeoutException {

    // machineType - Machine type you want to create in following format:
    //  *    "zones/{zone}/machineTypes/{type_name}". For example:
    //  *    "zones/europe-west3-c/machineTypes/f1-micro"
    //  *    You can find the list of available machine types using:
    //  *    https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
    String machineType = "n1-standard-1";
    // sourceImageFamily - Name of the public image family for Windows Server or SQL Server images.
    //  *    https://cloud.google.com/compute/docs/images#os-compute-support
    String sourceImageFamily = "windows-2022";

    // Instantiates a client.
    try (InstancesClient instancesClient = InstancesClient.create()) {

      AttachedDisk attachedDisk = AttachedDisk.newBuilder()
          // Describe the size and source image of the boot disk to attach to the instance.
          .setInitializeParams(AttachedDiskInitializeParams.newBuilder()
              .setDiskSizeGb(64)
              .setSourceImage(
                  String.format("projects/windows-cloud/global/images/family/%s",
                      sourceImageFamily))
              .build())
          .setAutoDelete(true)
          .setBoot(true)
          .setType(AttachedDisk.Type.PERSISTENT.toString())
          .build();

      Instance instance = Instance.newBuilder()
          .setName(instanceName)
          .setMachineType(String.format("zones/%s/machineTypes/%s", zone, machineType))
          .addDisks(attachedDisk)
          .addNetworkInterfaces(NetworkInterface.newBuilder()
              // You must verify or configure routes and firewall rules in your VPC network
              // to allow access to kms.windows.googlecloud.com.
              // More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server

              // Additionally, you must enable Private Google Access for subnets in your VPC network
              // that contain Windows instances with only internal IP addresses.
              // More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
              .setName(networkLink)
              .setSubnetwork(subnetworkLink)
              .build())
          // If you chose an image that supports Shielded VM, you can optionally change the
          // instance's Shielded VM settings.
          // .setShieldedInstanceConfig(ShieldedInstanceConfig.newBuilder()
          //    .setEnableSecureBoot(true)
          //    .setEnableVtpm(true)
          //    .setEnableIntegrityMonitoring(true)
          //    .build())
          .build();

      InsertInstanceRequest request = InsertInstanceRequest.newBuilder()
          .setProject(projectId)
          .setZone(zone)
          .setInstanceResource(instance)
          .build();

      // Wait for the operation to complete.
      Operation operation = instancesClient.insertAsync(request).get(5, TimeUnit.MINUTES);

      if (operation.hasError()) {
        System.out.printf("Error in creating instance %s", operation.getError());
        return;
      }

      System.out.printf("Instance created %s", instanceName);
    }
  }
}

Node.js

/**
 * TODO(developer): Uncomment and replace these variables before running the sample.
 */
// const projectId = 'YOUR_PROJECT_ID';
// const zone = 'europe-central2-b';
// const instanceName = 'YOUR_INSTANCE_NAME';
// const machineType = 'n1-standard-1';
// const sourceImageFamily = 'windows-2022';
// const networkLink = 'global/networks/default';
// const subnetworkLink = 'regions/europe-central2/subnetworks/default';

const compute = require('@google-cloud/compute');

async function createWindowsServerInstanceInternalIP() {
  const instancesClient = new compute.InstancesClient();

  const [response] = await instancesClient.insert({
    instanceResource: {
      name: instanceName,
      disks: [
        {
          // Describe the size and source image of the boot disk to attach to the instance.
          initializeParams: {
            diskSizeGb: '64',
            sourceImage: `projects/windows-cloud/global/images/family/${sourceImageFamily}/`,
          },
          autoDelete: true,
          boot: true,
          type: 'PERSISTENT',
        },
      ],
      machineType: `zones/${zone}/machineTypes/${machineType}`,
      networkInterfaces: [
        {
          // You must verify or configure routes and firewall rules in your VPC network
          // to allow access to kms.windows.googlecloud.com.
          // More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server

          // Additionally, you must enable Private Google Access for subnets in your VPC network
          // that contain Windows instances with only internal IP addresses.
          // More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling
          name: networkLink,
          subnetwork: subnetworkLink,
        },
      ],
      // If you chose an image that supports Shielded VM, you can optionally change the instance's Shielded VM settings.
      // "shieldedInstanceConfig": {
      //   "enableSecureBoot": true,
      //   "enableVtpm": true,
      //   "enableIntegrityMonitoring": true
      // },
    },
    project: projectId,
    zone,
  });
  let operation = response.latestResponse;
  const operationsClient = new compute.ZoneOperationsClient();

  // Wait for the create operation to complete.
  while (operation.status !== 'DONE') {
    [operation] = await operationsClient.wait({
      operation: operation.name,
      project: projectId,
      zone: operation.zone.split('/').pop(),
    });
  }

  console.log('Instance created.');
}

createWindowsServerInstanceInternalIP();

Python

from __future__ import annotations

import re
import sys
from typing import Any
import warnings

from google.api_core.extended_operation import ExtendedOperation
from google.cloud import compute_v1


def get_image_from_family(project: str, family: str) -> compute_v1.Image:
    """
    Retrieve the newest image that is part of a given family in a project.

    Args:
        project: project ID or project number of the Cloud project you want to get image from.
        family: name of the image family you want to get image from.

    Returns:
        An Image object.
    """
    image_client = compute_v1.ImagesClient()
    # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
    newest_image = image_client.get_from_family(project=project, family=family)
    return newest_image


def disk_from_image(
    disk_type: str,
    disk_size_gb: int,
    boot: bool,
    source_image: str,
    auto_delete: bool = True,
) -> compute_v1.AttachedDisk:
    """
    Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
    source for the new disk.

    Args:
         disk_type: the type of disk you want to create. This value uses the following format:
            "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
            For example: "zones/us-west3-b/diskTypes/pd-ssd"
        disk_size_gb: size of the new disk in gigabytes
        boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
        source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
            of the publicly available images or an image from one of your projects.
            This value uses the following format: "projects/{project_name}/global/images/{image_name}"
        auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it

    Returns:
        AttachedDisk object configured to be created using the specified image.
    """
    boot_disk = compute_v1.AttachedDisk()
    initialize_params = compute_v1.AttachedDiskInitializeParams()
    initialize_params.source_image = source_image
    initialize_params.disk_size_gb = disk_size_gb
    initialize_params.disk_type = disk_type
    boot_disk.initialize_params = initialize_params
    # Remember to set auto_delete to True if you want the disk to be deleted when you delete
    # your VM instance.
    boot_disk.auto_delete = auto_delete
    boot_disk.boot = boot
    return boot_disk


def wait_for_extended_operation(
    operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
) -> Any:
    """
    Waits for the extended (long-running) operation to complete.

    If the operation is successful, it will return its result.
    If the operation ends with an error, an exception will be raised.
    If there were any warnings during the execution of the operation
    they will be printed to sys.stderr.

    Args:
        operation: a long-running operation you want to wait on.
        verbose_name: (optional) a more verbose name of the operation,
            used only during error and warning reporting.
        timeout: how long (in seconds) to wait for operation to finish.
            If None, wait indefinitely.

    Returns:
        Whatever the operation.result() returns.

    Raises:
        This method will raise the exception received from `operation.exception()`
        or RuntimeError if there is no exception set, but there is an `error_code`
        set for the `operation`.

        In case of an operation taking longer than `timeout` seconds to complete,
        a `concurrent.futures.TimeoutError` will be raised.
    """
    result = operation.result(timeout=timeout)

    if operation.error_code:
        print(
            f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
            file=sys.stderr,
            flush=True,
        )
        print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
        raise operation.exception() or RuntimeError(operation.error_message)

    if operation.warnings:
        print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
        for warning in operation.warnings:
            print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)

    return result


def create_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    disks: list[compute_v1.AttachedDisk],
    machine_type: str = "n1-standard-1",
    network_link: str = "global/networks/default",
    subnetwork_link: str = None,
    internal_ip: str = None,
    external_access: bool = False,
    external_ipv4: str = None,
    accelerators: list[compute_v1.AcceleratorConfig] = None,
    preemptible: bool = False,
    spot: bool = False,
    instance_termination_action: str = "STOP",
    custom_hostname: str = None,
    delete_protection: bool = False,
) -> compute_v1.Instance:
    """
    Send an instance creation request to the Compute Engine API and wait for it to complete.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        disks: a list of compute_v1.AttachedDisk objects describing the disks
            you want to attach to your new instance.
        machine_type: machine type of the VM being created. This value uses the
            following format: "zones/{zone}/machineTypes/{type_name}".
            For example: "zones/europe-west3-c/machineTypes/f1-micro"
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
            "regions/{region}/subnetworks/{subnetwork_name}"
        internal_ip: internal IP address you want to assign to the new instance.
            By default, a free address from the pool of available internal IP addresses of
            used subnet will be used.
        external_access: boolean flag indicating if the instance should have an external IPv4
            address assigned.
        external_ipv4: external IPv4 address to be assigned to this instance. If you specify
            an external IP address, it must live in the same region as the zone of the instance.
            This setting requires `external_access` to be set to True to work.
        accelerators: a list of AcceleratorConfig objects describing the accelerators that will
            be attached to the new instance.
        preemptible: boolean value indicating if the new instance should be preemptible
            or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
        spot: boolean value indicating if the new instance should be a Spot VM or not.
        instance_termination_action: What action should be taken once a Spot VM is terminated.
            Possible values: "STOP", "DELETE"
        custom_hostname: Custom hostname of the new VM instance.
            Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
        delete_protection: boolean value indicating if the new virtual machine should be
            protected against deletion or not.
    Returns:
        Instance object.
    """
    instance_client = compute_v1.InstancesClient()

    # Use the network interface provided in the network_link argument.
    network_interface = compute_v1.NetworkInterface()
    network_interface.network = network_link
    if subnetwork_link:
        network_interface.subnetwork = subnetwork_link

    if internal_ip:
        network_interface.network_i_p = internal_ip

    if external_access:
        access = compute_v1.AccessConfig()
        access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
        access.name = "External NAT"
        access.network_tier = access.NetworkTier.PREMIUM.name
        if external_ipv4:
            access.nat_i_p = external_ipv4
        network_interface.access_configs = [access]

    # Collect information into the Instance object.
    instance = compute_v1.Instance()
    instance.network_interfaces = [network_interface]
    instance.name = instance_name
    instance.disks = disks
    if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
        instance.machine_type = machine_type
    else:
        instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"

    instance.scheduling = compute_v1.Scheduling()
    if accelerators:
        instance.guest_accelerators = accelerators
        instance.scheduling.on_host_maintenance = (
            compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
        )

    if preemptible:
        # Set the preemptible setting
        warnings.warn(
            "Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
        )
        instance.scheduling = compute_v1.Scheduling()
        instance.scheduling.preemptible = True

    if spot:
        # Set the Spot VM setting
        instance.scheduling.provisioning_model = (
            compute_v1.Scheduling.ProvisioningModel.SPOT.name
        )
        instance.scheduling.instance_termination_action = instance_termination_action

    if custom_hostname is not None:
        # Set the custom hostname for the instance
        instance.hostname = custom_hostname

    if delete_protection:
        # Set the delete protection bit
        instance.deletion_protection = True

    # Prepare the request to insert an instance.
    request = compute_v1.InsertInstanceRequest()
    request.zone = zone
    request.project = project_id
    request.instance_resource = instance

    # Wait for the create operation to complete.
    print(f"Creating the {instance_name} instance in {zone}...")

    operation = instance_client.insert(request=request)

    wait_for_extended_operation(operation, "instance creation")

    print(f"Instance {instance_name} created.")
    return instance_client.get(project=project_id, zone=zone, instance=instance_name)


def create_windows_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    machine_type: str,
    source_image_family: str = "windows-2022",
    network_link: str = "global/networks/default",
    subnetwork_link: str | None = None,
) -> compute_v1.Instance:
    """
    Creates a new Windows Server instance that has only an internal IP address.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        machine_type: machine type you want to create in following format:
            "zones/{zone}/machineTypes/{type_name}". For example:
            "zones/europe-west3-c/machineTypes/f1-micro"
            You can find the list of available machine types using:
            https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list
        source_image_family: name of the public image family for Windows Server or SQL Server images.
            https://cloud.google.com/compute/docs/images#os-compute-support
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
           "regions/{region}/subnetworks/{subnetwork_name}"

    Returns:
        Instance object.
    """
    if subnetwork_link is None:
        subnetwork_link = f"regions/{zone}/subnetworks/default"

    base_image = get_image_from_family(
        project="windows-cloud", family=source_image_family
    )
    disk_type = f"zones/{zone}/diskTypes/pd-standard"
    disks = [disk_from_image(disk_type, 100, True, base_image.self_link, True)]

    # You must verify or configure routes and firewall rules in your VPC network
    # to allow access to kms.windows.googlecloud.com.
    # More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server

    # Additionally, you must enable Private Google Access for subnets in your VPC network
    # that contain Windows instances with only internal IP addresses.
    # More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling

    instance = create_instance(
        project_id,
        zone,
        instance_name,
        disks,
        machine_type=machine_type,
        network_link=network_link,
        subnetwork_link=subnetwork_link,
        external_access=True,  # Set this to False to disable external IP for your instance
    )
    return instance

למכונה הזו אין כתובת IP חיצונית, ולכן אי אפשר להתחבר אליה ישירות דרך האינטרנט. אפשר להתחבר מרשת אחרת שמחוברת לרשת ה-VPC באמצעות Cloud Interconnect או Cloud VPN, או שאפשר להתחבר קודם למכונת Bastion באמצעות RDP ואז להתחבר למכונה שיש לה רק כתובת IP פנימית.

בנוסף, אפשר לצרף את המכונה הווירטואלית לדומיין של שירות מנוהל ל-Microsoft AD, במהלך יצירת המכונה הווירטואלית או אחרי יצירתה. מידע נוסף זמין במאמר צירוף אוטומטי של מכונת Windows וירטואלית לדומיין.

הגדרת גישה אל kms.windows.googlecloud.com

כדי להפעיל ולחדש את Windows, רשת ה-VPC שלכם צריכה לעמוד בדרישות הבאות בנוגע לניתוב ולכללי חומת האש.

דרישות לגבי ניתוב

למכונות ה-Windows שלכם צריכה להיות אפשרות להגיע אל kms.windows.googlecloud.com (35.190.247.13 או 2001:4860:4802:32::86) דרך מסלול שהצעד הבא שלו הוא שער האינטרנט שמוגדר כברירת מחדל. אי אפשר להפעיל מכונות של Windows באמצעות שער NAT שמבוסס על מכונה או באמצעות Cloud NAT, כי kms.windows.googlecloud.com דוחה בקשות הפעלה מכתובות IP שלא אושרו כמכונות של Compute Engine.

אתם יכולים להשתמש בנתיב ברירת המחדל ברשת ה-VPC כדי לנתב תנועה ישירות אל kms.windows.googlecloud.com. אם אתם מסירים את המסלול הזה, או אם אתם מתכננים לעשות את זה בעתיד, צרו מסלול סטטי בהתאמה אישית עם היעד 35.190.247.13 או 2001:4860:4802:32::86, והגדירו את הצעד הבא ל-default Internet gateway, באופן הבא:

‫IPv4 בלבד

gcloud compute routes create mskms-ipv4-route-ipv4-network \
    --destination-range=35.190.247.13/32 \
    --network=ipv4-network \
    --next-hop-gateway=default-internet-gateway

Dual stack

gcloud compute routes create mskms-ipv4-route-ipv4-network \
    --destination-range=35.190.247.13/32 \
    --network=ipv4-network \
    --next-hop-gateway=default-internet-gateway
gcloud compute routes create mskms-ipv6-route-ipv6-network \
    --destination-range=2001:4860:4802:32::86/128 \
    --network=ipv6-network \
    --next-hop-gateway=default-internet-gateway

‫IPv6 בלבד

gcloud compute routes create mskms-ipv6-route-ipv6-network \
    --destination-range=2001:4860:4802:32::86/128 \
    --network=ipv6-network \
    --next-hop-gateway=default-internet-gateway

מחליפים את ipv4-network או ipv6-network בשם של רשת ה-VPC.

הנתיב שמוגדר כברירת מחדל או נתיב סטטי מותאם אישית מאפשרים למכונות עם כתובות IP חיצוניות להגיע אל kms.windows.googlecloud.com. אם יש לכם מכונות עם Windows ללא כתובות IP חיצוניות או שאתם משתמשים ב-Cloud NAT, אתם צריכים גם להפעיל גישה פרטית ל-Google כדי שמכונות עם כתובות IP פנימיות בלבד יוכלו לשלוח תעבורה לכתובת ה-IP החיצונית של kms.windows.googlecloud.com (35.190.247.13 או 2001:4860:4802:32::86).

דרישות לכללי חומת אש

כלל חומת האש implied allow egress מאפשר למופעים לשלוח בקשות ולקבל תשובות. אם לא יצרתם כללים בהתאמה אישית של חומת האש שדוחים תעבורת נתונים יוצאת, מכונות Windows יכולות לתקשר עם kms.windows.googlecloud.com.

אם אתם מתאימים אישית את כללי חומת האש, מומלץ ליצור כלל יציאה (egress) עם עדיפות גבוהה שמאפשר באופן מפורש תקשורת עם 35.190.247.13 או 2001:4860:4802:32::86. כך, כשמשנים את כללי חומת האש, לא משביתים בטעות את ההפעלה של Windows.

בדוגמאות הבאות gcloud נוצר כלל מומלץ להתרת תעבורה יוצאת עם העדיפות הגבוהה ביותר:

‫IPv4 בלבד

gcloud compute firewall-rules create mskms-ipv4-firewall-rule-ipv4-network \
    --direction=EGRESS \
    --network=ipv4-network \
    --action=ALLOW \
    --rules=tcp:1688 \
    --destination-ranges=35.190.247.13/32 \
    --priority=0

Dual stack

gcloud compute firewall-rules create mskms-ipv4-firewall-rule-ipv4-network \
    --direction=EGRESS \
    --network=ipv4-network \
    --action=ALLOW \
    --rules=tcp:1688 \
    --destination-ranges=35.190.247.13/32 \
    --priority=0
gcloud compute firewall-rules create mskms-ipv6-firewall-rule-ipv6-network \
    --direction=EGRESS \
    --network=ipv6-network \
    --action=ALLOW \
    --rules=tcp:1688 \
    --destination-ranges=2001:4860:4802:32::86/128 \
    --priority=0

‫IPv6 בלבד

gcloud compute firewall-rules create mskms-ipv6-firewall-rule-ipv6-network \
    --direction=EGRESS \
    --network=ipv6-network \
    --action=ALLOW \
    --rules=tcp:1688 \
    --destination-ranges=2001:4860:4802:32::86/128 \
    --priority=0

מחליפים את ipv4-network או ipv6-network בשם של רשת ה-VPC.

איך מוודאים שהפעלת המכונה הושלמה בהצלחה

למכונות של Windows לוקח יותר זמן להתחיל לפעול בגלל תהליך ה-sysprep. יכול להיות שבמסוף Google Cloud יופיע שהמכונה פועלת, גם אם תהליך ה-sysprep עדיין לא הסתיים. כדי לבדוק אם המכונה הווירטואלית הופעלה בהצלחה ומוכנה לשימוש, בודקים את הפלט של היציאה הטורית באמצעות הפקודה הבאה:

gcloud compute instances get-serial-port-output INSTANCE_NAME

מחליפים את INSTANCE_NAME בשם המכונה שרוצים לאמת.

...[snip]...
Running schtasks with arguments /run /tn GCEStartup
-->  SUCCESS: Attempted to run the scheduled task "GCEStartup".
-------------------------------------------------------------
Instance setup finished. INSTANCE_NAME is ready to use.
-------------------------------------------------------------

הפעלה והשבתה של תכונות במופע Windows

אם יש לכם מכונות של Windows עם גרסאות תמונה v20170509 ואילך או עם גרסת סוכן 4.1.0 ואילך, אתם יכולים להגדיר את המכונה בקובץ תצורה או במטא-נתונים מותאמים אישית של הפרויקט או המכונה. קובץ התצורה הוא בפורמט INI והוא נמצא בנתיב הבא:

C:\Program Files\Google\Compute Engine\instance_configs.cfg

המערכת מבטלת את הגדרות התצורה לפי סדר העדיפויות הבא, מהעדיפות הגבוהה ביותר לנמוכה ביותר:

  1. פרמטרים של הגדרות שקובעים בקובץ התצורה
  2. פרמטרים של הגדרה שמוגדרים במטא-נתונים מותאמים אישית ברמת המופע
  3. פרמטרים של הגדרות שמוגדרים במטא-נתונים מותאמים אישית ברמת הפרויקט

לדוגמה, אם אתם יכולים להפעיל את התכונה accountManager בקובץ תצורה, המכונה מתעלמת מהפרמטרים שהגדרתם במטא-נתונים בהתאמה אישית כדי להשבית את התכונה הזו.

יתרון אחד בהגדרת הפרמטרים האלה בקובץ התצורה הוא שההגדרות האלה נשמרות כשיוצרים אימג' בהתאמה אישית עבור מכונת Windows Server. מטא-נתונים מותאמים אישית ברמת המכונה לא נשמרים אחרי שהמכונה נמחקת.

אתם יכולים להשבית תכונות שונות של מכונות Windows באמצעות הדוגמאות הבאות.

השבתה של מנהל החשבונות

השבתת מנהל החשבונות משביתה גם את האפשרות לאפס סיסמאות באמצעות Google Cloud CLI או מסוף Google Cloud :

  • קובץ תצורה:

    [accountManager]
    disable=true
    
  • במטא-נתונים בהתאמה אישית, מגדירים את disable-account-manager לערך true במטא-נתונים.

השבתת מנהל הכתובות

  • רשומה בקובץ התצורה:

    [addressManager]
    disable=true
    
  • במטא-נתונים בהתאמה אישית, מגדירים את disable-address-manager לערך true במטא-נתונים.

Windows Server Failover Clustering

מפעילים את הסוכן של Windows Server Failover Clustering:

  • רשומה בקובץ התצורה:

    [wsfc]
    enable=true
    
  • במטא-נתונים בהתאמה אישית, מגדירים את enable-wsfc לערך true במטא-נתונים.

שימוש בכמה מאזני עומסים פנימיים

מציינים את כתובת ה-IP של מופע איזון העומסים הפנימי עבור אשכולות מעבר לגיבוי. זוהי הגדרה מתקדמת שלא צריך להגדיר עבור אשכול ייעודי למעבר אוטומטי לגיבוי.

בדרך כלל משתמשים במכונה של איזון עומסים פנימי כדי להפנות את התנועה ברשת למכונה וירטואלית אחת בכל פעם. אם מוסיפים מופע שני של איזון עומסים פנימי שמשתמש במכונות וירטואליות של אשכולות מעבר לגיבוי בעת כשל כחלק מקצה עורפי של אתר עם איזון עומסים, יהיו שתי כתובות IP של איזון עומסים פנימי. אם נעשה שימוש ב-10.0.0.10 בגיבוי בעת כשל, וב-10.0.0.11 במאזן העומסים של האתר, צריך לציין את כתובת ה-IP של מאזן העומסים שבו משתמשים בגיבוי בעת כשל. כך אפשר להבחין בין הכתובות שנמצאות בשימוש באשכול.

  • רשומה בקובץ התצורה:

    [wsfc]
    addresses=10.0.0.10
    
  • במטא-נתונים בהתאמה אישית, מגדירים את wsfc-addrs לערך 10.0.0.10.

שינוי היציאה של סוכן האשכול

מגדירים את היציאה של סוכן אשכולות היתירות כשל. יציאת ברירת המחדל היא 59998. צריך לציין יציאה רק כשרוצים להשתמש ביציאה אחרת:

  • רשומה בקובץ התצורה:

    [wsfc]
    port=12345
    
  • במטא-נתונים בהתאמה אישית, מגדירים את wsfc-agent-port למספר היציאה.

הערות על גרסאות תמונה

בתמונות ישנות יותר לא נעשה שימוש בקובץ תצורה, ויש בהן רק חלק מהתכונות. גרסאות תמונה בין גרסה v20160112 לגרסה v20170509, או גרסת סוכן Windows בין 3.2.1.0 ל-4.0.0, מחייבות שימוש בערכי המטא-נתונים המותאמים אישית הבאים:

  • מגדירים את disable-account-manager לערך true במטא-נתונים של המכונה, כדי להשבית את מנהל החשבונות.
  • מגדירים את disable-address-manager לערך true במטא-נתונים של המכונה, כדי להשבית את הכלי לניהול כתובות.

המאמרים הבאים