יצירה וניהול של מכונות וירטואליות של 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. רשימה של משפחות התמונות הזמינות מופיעה בדף תמונות ציבוריות.

אם אתם צריכים יותר משתי פעילויות בו-זמניות של Remote Desktop, תצטרכו לרכוש רישיונות גישה ללקוח (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 במסוף Cloud de Confiance .

    כניסה לדף Create an instance

  2. בקטע Boot disk (דיסק אתחול), בוחרים באפשרות Change (שינוי) ומבצעים את הפעולות הבאות:

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

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

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

    כניסה לדף Create an instance

  2. בקטע Boot disk (דיסק אתחול), בוחרים באפשרות Change (שינוי) ומבצעים את הפעולות הבאות:

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

    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

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

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

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

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

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

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.s3nsapis.fr/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, או במהלך יצירת המכונה הווירטואלית או אחרי היצירה שלה. מידע נוסף זמין במאמר איך מצטרפים אוטומטית למכונה וירטואלית של 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, והצעד הבא מוגדר לשער ברירת המחדל של האינטרנט, באופן הבא:

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 מאפשר למופעים לשלוח בקשות ולקבל תשובות. אלא אם יצרתם כללים מותאמים אישית לחומת האש שדוחים תעבורת נתונים יוצאת (egress), מופעי Windows יכולים לתקשר עם kms.windows.googlecloud.com.

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

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

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. Cloud de Confiance יכול להיות שבמסוף יופיע שהמופע פועל, גם אם תהליך 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 או מסוף Cloud de Confiance :

  • קובץ תצורה:

    [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 של מופע איזון העומסים הפנימי עבור אשכולות מעבר לגיבוי. זוהי הגדרה מתקדמת שלא צריך להגדיר עבור אשכול ייעודי למעבר לגיבוי.

בדרך כלל משתמשים במופע של איזון עומסים פנימי כדי להפנות תעבורת נתונים ברשת למכונה וירטואלית אחת בכל פעם. אם מוסיפים מופע שני של איזון עומסים פנימי שמשתמש במכונות וירטואליות של אשכולות מעבר לגיבוי (failover) כחלק מקצה עורפי של אתר עם איזון עומסים, יהיו שתי כתובות 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 במטא-נתונים של המכונה, כדי להשבית את הכלי לניהול כתובות.

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