创建和管理 Windows Server 虚拟机


Compute Engine 提供 Windows Server 的公共映像,您可以使用这些映像来创建实例。如需了解如何创建预装了 SQL Server 的 Windows Server 实例,请参阅创建 SQL Server 实例

如需更广泛地了解可以在 Compute Engine 上运行的 Windows Server 实例和 Windows 应用,请参阅在 Compute Engine 上使用 Windows

价格

准备工作

  • 为您的项目启用结算功能
  • 设置身份验证(如果尚未设置)。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以按如下方式向 Compute Engine 进行身份验证。

    选择标签页以了解您打算如何使用本页面上的示例:

    控制台

    当您使用 Google Cloud 控制台访问 Google Cloud 服务和 API 时,无需设置身份验证。

    gcloud

    1. 安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init
    2. 设置默认区域和可用区

    Go

    如需从本地开发环境使用本页面上的 Go 示例,请安装并初始化 gcloud CLI,然后使用用户凭据设置应用默认凭据。

    1. 安装 Google Cloud CLI。
    2. 如需初始化 gcloud CLI,请运行以下命令:

      gcloud init
    3. 为您的 Google 账号创建本地身份验证凭据:

      gcloud auth application-default login

    如需了解详情,请参阅 为本地开发环境设置身份验证

    Java

    如需从本地开发环境使用本页面上的 Java 示例,请安装并初始化 gcloud CLI,然后使用用户凭据设置应用默认凭据。

    1. 安装 Google Cloud CLI。
    2. 如需初始化 gcloud CLI,请运行以下命令:

      gcloud init
    3. 为您的 Google 账号创建本地身份验证凭据:

      gcloud auth application-default login

    如需了解详情,请参阅 为本地开发环境设置身份验证

    Node.js

    如需从本地开发环境使用本页面上的 Node.js 示例,请安装并初始化 gcloud CLI,然后使用用户凭据设置应用默认凭据。

    1. 安装 Google Cloud CLI。
    2. 如需初始化 gcloud CLI,请运行以下命令:

      gcloud init
    3. 为您的 Google 账号创建本地身份验证凭据:

      gcloud auth application-default login

    如需了解详情,请参阅 为本地开发环境设置身份验证

    Python

    如需从本地开发环境使用本页面上的 Python 示例,请安装并初始化 gcloud CLI,然后使用用户凭据设置应用默认凭据。

    1. 安装 Google Cloud CLI。
    2. 如需初始化 gcloud CLI,请运行以下命令:

      gcloud init
    3. 为您的 Google 账号创建本地身份验证凭据:

      gcloud auth application-default login

    如需了解详情,请参阅 为本地开发环境设置身份验证

    REST

    如需在本地开发环境中使用本页面上的 REST API 示例,请使用您提供给 gcloud CLI 的凭据。

      安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

      gcloud init

创建一个 Windows Server 实例

如需创建 Windows Server 实例,请指定所需的特定 Windows 版本的映像系列。Compute Engine 提供了多个 Windows Server 版本,其中大部分以安全强化型虚拟机映像的形式提供。安全强化型虚拟机映像提供了诸多安全功能,例如符合 UEFI 标准的固件、安全启动以及受 vTPM 保护的测量启动。如需可用映像系列的列表,请参阅公共映像

如果您需要的并发远程桌面会话超过两个,则必须购买远程桌面会话 (RDS) 客户端访问许可 (CAL)。如需了解详情,请参阅使用客户端访问许可 (CAL) 来许可 RDS 部署

使用 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

创建使用外部 IP 进行激活的 Windows Server 实例

本部分介绍如何创建具有外部 IP 地址的 Windows Server 实例。您的 VPC 网络必须配置为允许访问 kms.windows.googlecloud.com

控制台

如需创建基本的 Windows 虚拟机,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到创建实例页面。

    打开“创建实例”

  2. 对于启动磁盘,选择更改,然后执行以下操作:

    1. 公共映像标签页上,选择 Windows Server 操作系统。
    2. 点击选择
  3. 如需创建虚拟机,请点击创建

如需创建安全强化型虚拟机 Windows 实例,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到创建实例页面。

    打开“创建实例”

  2. 对于启动磁盘,选择更改,然后执行以下操作:

    1. 公共映像标签页上,选择 Windows Server 操作系统。
    2. 要保存启动磁盘配置,请点击选择
  3. (可选)如需更改虚拟机的安全强化型虚拟机设置,请展开高级选项部分。之后,执行以下操作:

    1. 展开安全部分。
    2. 如果您要关闭安全启动,请清除开启安全启动复选框。安全启动功能可帮助保护您的虚拟机实例免受启动级和内核级恶意软件和 rootkit 的攻击。如需了解详情,请参阅安全启动
    3. 如果您要关闭虚拟可信平台模块 (vTPM),请清除开启 vTPM 复选框。vTPM 将启用测量启动,该功能可验证虚拟机预启动和启动完整性。如需了解详情,请参阅虚拟可信平台模块 (vTPM)

    4. 如果您要关闭完整性监控,请清除开启完整性监控复选框。借助完整性监控功能,您可以使用 Cloud Monitoring 监控安全强化型虚拟机的启动完整性。如需了解详情,请参阅完整性监控

  4. 如需创建虚拟机,请点击创建

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]

其中:

  • [INSTANCE_NAME] 是新实例的名称
  • [IMAGE_FAMILY] 是 Windows Server 映像的公共映像系列之一。
  • [MACHINE_TYPE] 是可用的机器类型之一。
  • [BOOT_DISK_SIZE] 是启动磁盘的大小(以 GB 为单位)。永久性磁盘越大,吞吐量越高
  • [BOOT_DISK_TYPE] 是您的实例的启动磁盘类型。例如 pd-ssd

如果选择支持安全强化型虚拟机的映像,则可以选择使用以下标志之一更改实例的安全强化型虚拟机设置:

  • --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

Go

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
	"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
  }]
}

其中:

  • [INSTANCE_NAME] 是新实例的名称
  • [IMAGE_FAMILY] 是 Windows Server 或 SQL Server 映像的公共映像系列之一。
  • [ZONE] 是此实例所在的地区
  • [MACHINE_TYPE] 是可用的机器类型之一。
  • [BOOT_DISK_SIZE] 是启动磁盘的大小(以 GB 为单位)。永久性磁盘越大,吞吐量越高
  • [BOOT_DISK_TYPE] 是您的实例的启动磁盘类型。例如 pd-ssd

如果选择支持安全强化型虚拟机的映像,则可以在请求正文中选择使用以下布尔值项更改实例的安全强化型虚拟机设置:

  • enableSecureBoot:启用或停用安全启动。安全启动功能可帮助保护您的虚拟机实例免受启动级和内核级恶意软件和 rootkit 的攻击。如需了解详情,请参阅安全启动
  • enableVtpm:启用或停用虚拟可信平台模块 (vTPM)。vTPM 将启用测量启动,该功能可验证虚拟机预启动和启动完整性。如需了解详情,请参阅虚拟可信平台模块 (vTPM)

  • enableIntegrityMonitoring:启用或停用完整性监控。借助完整性监控功能,您可以使用 Cloud Monitoring 报告监控和验证安全强化型虚拟机实例的运行时启动完整性。如需了解详情,请参阅完整性监控

如需详细了解如何创建实例,请参阅 instances.insert() 文档。

创建 Windows 或 SQL Server 实例后,为该实例设置初始密码,以便通过 RDP 连接该实例

此外,您还可以在创建虚拟机时或在创建虚拟机后,将虚拟机加入代管式 Microsoft AD 域。如需了解详情,请参阅将 Windows 虚拟机自动加入网域

创建使用内部 IP 地址进行激活的 Windows Server 实例

如需创建仅具有内部 IP 地址的 Windows Server 实例,您必须在 VPC 网络中验证或配置路由和防火墙规则,以允许访问 kms.windows.googlecloud.com。此外,您还必须为 VPC 网络中只有内部 IP 地址的 Windows 实例所在的子网启用专用 Google 访问通道

gcloud

当您使用 gcloud CLI 创建新实例时,可以使用 --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]

将下列占位符替换为有效值:

  • [INSTANCE_NAME] 是新实例的名称
  • [SUBNET_NAME] 是 VPC 网络中实例将使用的子网名称。子网必须与您为实例选择的地区位于同一区域。
  • [IMAGE_FAMILY] 是 Windows Server 映像的公共映像系列之一。
  • [MACHINE_TYPE] 是可用的机器类型之一。
  • [BOOT_DISK_SIZE] 是启动磁盘的大小(以 GB 为单位)。永久性磁盘越大,吞吐量越高
  • [BOOT_DISK_TYPE] 是您的实例的启动磁盘类型。例如 pd-ssd

Go

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
	"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 地址,因此无法直接通过互联网连接该实例。您可以使用 Cloud InterconnectCloud VPN 从已接入您的 VPC 网络的其他网络进行连接,也可以先通过 RDP 连接到堡垒实例,然后再连接到只有内部 IP 地址的实例。

此外,您还可以在创建虚拟机时或在创建虚拟机后,将虚拟机加入代管式 Microsoft AD 域。如需了解详情,请参阅将 Windows 虚拟机自动加入网域

配置对 kms.windows.googlecloud.com 的访问权限

如需激活和续订 Windows,您的 VPC 网络必须满足以下路由和防火墙规则要求。

路由要求

您的 Windows 实例必须能够通过其下一个跃点为默认互联网网关的路由访问 kms.windows.googlecloud.com (35.190.247.13)。您无法使用基于实例的 NAT 网关或 Cloud NAT 激活 Windows 实例,因为 kms.windows.googlecloud.com 拒绝来自未确认为 Compute Engine 实例的 IP 地址的激活请求。

您可以使用 VPC 网络中的默认路由将流量直接路由至 kms.windows.googlecloud.com。如果您要移除此路由或者计划将来进行移除,请创建自定义静态路由,该路由的目的地应为 35.190.247.13 且下一个跃点应设置为“默认互联网网关”

gcloud compute routes create [ROUTE_NAME] \
    --destination-range=35.190.247.13/32 \
    --network=[NETWORK] \
    --next-hop-gateway=default-internet-gateway

[ROUTE_NAME] 替换为此路由的名称,将 [NETWORK] 替换为您的 VPC 网络名称。

如上所述,默认路由或自定义静态路由将允许具有外部 IP 地址的实例访问 kms.windows.googlecloud.com。如果您的 Windows 实例没有外部 IP 地址或未使用 Cloud NAT,您还必须启用专用 Google 访问通道,这样仅具有内部 IP 地址的实例才能将流量发送到 kms.windows.googlecloud.com (35.190.247.13) 的外部 IP 地址。

防火墙规则要求

隐式允许出站防火墙规则允许实例发出请求并接收已建立的响应。除非您创建了拒绝出站的自定义防火墙规则,否则您的 Windows 实例可以与 kms.windows.googlecloud.com 通信。

如需自定义防火墙规则,最好创建一个明确允许与 35.190.247.13 通信的高优先级允许出站规则。这样,在修改防火墙规则时,您不会意外停用 Windows 激活。

以下 gcloud 示例创建了具有最高优先级的建议的允许出站规则:

gcloud compute firewall-rules create [RULE_NAME] \
    --direction=EGRESS \
    --network=[NETWORK] \
    --action=ALLOW \
    --rules=tcp:1688 \
    --destination-ranges=35.190.247.13/32 \
    --priority=0

[RULE_NAME] 替换为此防火墙规则的名称,将 [NETWORK] 替换为您的 VPC 网络名称。

验证实例已成功启动

由于 sysprep 进程,Windows 实例的启动时间较长。即使 sysprep 进程尚未完成,Google Cloud 控制台也可能会显示实例正在运行。如需检查实例是否已成功启动并已准备好使用,请使用以下命令检查串行端口输出:

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 故障切换集群

启用 Windows Server 故障切换集群代理:

  • 配置文件条目:

    [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 之间的映像版本或介于 3.2.1.04.0.0 之间的 Windows 代理版本要求您使用以下自定义元数据值:

  • 将实例元数据中的 disable-account-manager 设置为 true 即可停用账号管理器。
  • 将实例元数据中的 disable-address-manager 设置为 true 即可停用地址管理器。

后续步骤