Windows Server VM 만들기 및 관리


Compute Engine은 인스턴스를 만드는 데 사용할 수 있는 공개 이미지를 Windows Server에 제공합니다. SQL Server가 사전 설치된 Windows Server 인스턴스를 만드는 방법에 대한 지침은 SQL Server 인스턴스 만들기를 참조하세요.

Compute Engine에서 실행할 수 있는 Windows 애플리케이션과 Windows Server 인스턴스에 대한 일반적인 내용은 Compute Engine으로 구동하는 Windows를 참조하세요.

가격 책정

  • Windows Server 이미지는 프리미엄 이미지이므로 사용 시 추가 요금이 발생합니다.

  • Windows Server VM은 무료 체험판에 포함되지 않습니다.

시작하기 전에

  • 프로젝트에 결제를 사용 설정합니다.
  • 아직 인증을 설정하지 않았다면 설정합니다. 인증은 Google Cloud 서비스 및 API에 액세스하기 위해 ID를 확인하는 프로세스입니다. 로컬 개발 환경에서 코드 또는 샘플을 실행하려면 다음과 같이 Compute Engine에 인증하면 됩니다.

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

    Console

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

    gcloud

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

      gcloud init
    2. Set a default region and zone.
    3. Go

      이 페이지의 Go 샘플을 로컬 개발 환경에서 사용하려면 gcloud CLI를 설치 및 초기화한 다음 사용자 인증 정보로 애플리케이션 기본 사용자 인증 정보를 설정하세요.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      자세한 내용은 다음을 참조하세요: Set up authentication for a local development environment.

      자바

      이 페이지의 Java 샘플을 로컬 개발 환경에서 사용하려면 gcloud CLI를 설치 및 초기화한 다음 사용자 인증 정보로 애플리케이션 기본 사용자 인증 정보를 설정하세요.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      자세한 내용은 다음을 참조하세요: Set up authentication for a local development environment.

      Node.js

      이 페이지의 Node.js 샘플을 로컬 개발 환경에서 사용하려면 gcloud CLI를 설치 및 초기화한 다음 사용자 인증 정보로 애플리케이션 기본 사용자 인증 정보를 설정하세요.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      자세한 내용은 다음을 참조하세요: Set up authentication for a local development environment.

      Python

      이 페이지의 Python 샘플을 로컬 개발 환경에서 사용하려면 gcloud CLI를 설치 및 초기화한 다음 사용자 인증 정보로 애플리케이션 기본 사용자 인증 정보를 설정하세요.

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      자세한 내용은 다음을 참조하세요: Set up authentication for a local development environment.

      REST

      로컬 개발 환경에서 이 페이지의 REST API 샘플을 사용하려면 gcloud CLI에 제공하는 사용자 인증 정보를 사용합니다.

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

        gcloud init

      자세한 내용은 Google Cloud 인증 문서의 REST 사용 인증을 참조하세요.

Windows Server 인스턴스를 만들기

Windows Server에서 인스턴스를 만들려면 필요한 특정 Windows 버전의 이미지 계열을 지정합니다. Compute Engine은 여러 버전의 Windows Server를 제공하며 그 중 대부분이 보안 VM 이미지로 제공됩니다. 보안 VM 이미지는 UEFI 규격 펌웨어, 보안 부팅, vTPM 보호가 적용된 신중한 부팅 같은 보안 기능을 제공합니다. 사용할 수 있는 이미지 계열의 목록은 공개 이미지를 참조하세요.

세 개 이상의 동시 실행 원격 데스크톱 세션이 필요한 경우 원격 데스크톱 세션(RDS) 클라이언트 액세스 라이선스(CAL)를 구매해야 합니다. 자세한 내용은 클라이언트 액세스 라이선스(CAL)로 RDS 배포 라이선스를 참조하세요.

Microsoft Active Directory 작업

새 인스턴스에 Microsoft Active Directory(AD)를 사용하려는 경우 표시된 시스템의 최대 이름 길이 제한을 충족하도록 인스턴스 이름이 15자(영문 기준) 이하여야 합니다.

AD에서는 인스턴스 이름이 15자로 잘린 상태로 생성된 머신의 NetBIOS 이름이 사용됩니다. 따라서 도메인 사용자로 로그인하려고 시도할 때 다음 오류가 발생할 수 있습니다. The Security Database on the Server does not have a Computer Account for this Workstation Trust Relationship.

외부 IP를 사용하여 정품 인증하는 Windows Server 인스턴스 만들기

이 섹션에서는 외부 IP 주소가 있는 Windows Server 인스턴스를 만드는 방법을 설명합니다. kms.windows.googlecloud.com에 대한 액세스를 허용하도록 VPC 네트워크를 구성해야 합니다.

콘솔

기본 Windows VM을 만들려면 다음 안내를 따르세요.

  1. Google Cloud 콘솔에서 인스턴스 만들기 페이지로 이동합니다.

    인스턴스 만들기로 이동

  2. 부팅 디스크에서 변경을 선택하고 다음을 수행합니다.

    1. 공개 이미지 탭에서 Windows Server 운영체제를 선택합니다.
    2. 선택을 클릭합니다.
  3. VM을 만들려면 만들기를 클릭합니다.

보안 VM Windows 인스턴스를 만들려면 다음을 수행합니다.

  1. Google Cloud 콘솔에서 인스턴스 만들기 페이지로 이동합니다.

    인스턴스 만들기로 이동

  2. 부팅 디스크에서 변경을 선택하고 다음을 수행합니다.

    1. 공개 이미지 탭에서 Windows Server 운영체제를 선택합니다.
    2. 부팅 디스크 구성을 저장하려면 선택을 클릭합니다.
  3. 원하는 경우 VM의 보안 VM 설정을 변경하려면 고급 옵션 섹션을 펼칩니다. 그런 후 다음 작업을 수행합니다.

    1. 보안 섹션을 펼칩니다.
    2. 보안 부팅을 중지하려면 보안 부팅 설정 체크박스를 선택 취소합니다. 보안 부팅을 사용하면 부팅 수준 및 커널 수준의 멀웨어와 루트킷으로부터 VM 인스턴스를 보호할 수 있습니다. 자세한 내용은 보안 부팅을 참조하세요.
    3. vTPM(Virtual Trusted Platform Module)을 중지하려면 vTPM 설정 체크박스를 선택 취소합니다. vTPM은 신중한 부팅을 사용 설정하여 VM 사전 부팅 및 부팅 무결성을 검증합니다. 자세한 내용은 Virtual Trusted Platform Module(vTPM)을 참조하세요.

    4. 무결성 모니터링을 중지하려면 무결성 모니터링 사용 설정 체크박스를 선택 취소합니다. 무결성 모니터링을 사용하면 Cloud Monitoring을 통해 보안 VM의 VM 부팅 무결성을 모니터링할 수 있습니다. 자세한 내용은 무결성 모니터링을 참조하세요.

  4. VM을 만들려면 만들기를 클릭합니다.

gcloud

compute images list 명령어를 사용하여 사용 가능한 Windows Server 이미지의 목록을 표시합니다.

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

이미지가 보안 VM 기능을 지원하는지 확인하려면 다음 명령어를 실행하고 출력에 UEFI_COMPATIBLE이 표시되는지 확인합니다.

gcloud compute images describe [IMAGE_NAME] --project windows-cloud

여기서 [IMAGE_NAME]은 보안 VM 기능 지원을 확인할 이미지의 이름입니다.

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입니다.

보안 VM을 지원하는 이미지를 선택한 경우 다음 플래그 중 하나를 사용하여 인스턴스의 보안 VM 설정을 선택적으로 변경할 수 있습니다.

  • --no-shielded-secure-boot: 보안 부팅을 중지합니다. 보안 부팅을 사용하면 부팅 수준 및 커널 수준의 멀웨어와 루트킷으로부터 VM 인스턴스를 보호할 수 있습니다. 자세한 내용은 보안 부팅을 참조하세요.
  • --no-shielded-vtpm: vTPM(Virtual Trusted Platform Module)을 사용 중지합니다. vTPM을 사용하면 신중한 부팅을 통해 VM 사전 부팅 및 부팅 무결성을 검증할 수 있습니다. 자세한 내용은 vTPM(Virtual Trusted Platform Module)을 참조하세요.

  • --no-shielded-integrity-monitoring: 무결성 모니터링을 중지합니다. 무결성 모니터링을 사용하면 Cloud Monitoring을 통해 보안 VM 인스턴스의 부팅 무결성을 모니터링할 수 있습니다. 자세한 내용은 무결성 모니터링을 참조하세요.

다음 예시에서는 보안 부팅이 사용 중지된 상태로 Windows 2022 보안 VM 인스턴스를 만듭니다.

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
}

자바


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입니다.

보안 VM을 지원하는 이미지를 선택한 경우에는 다음 부울 요청 본문 항목을 사용하여 인스턴스의 보안 VM 설정을 변경할 수도 있습니다.

  • enableSecureBoot: 보안 부팅을 사용 설정 또는 중지합니다. 보안 부팅을 사용하면 부팅 수준 및 커널 수준의 멀웨어와 루트킷으로부터 VM 인스턴스를 보호할 수 있습니다. 자세한 내용은 보안 부팅을 참조하세요.
  • enableVtpm: vTPM(Virtual Trusted Platform Module)을 사용 설정 또는 사용 중지합니다. vTPM을 사용하면 신중한 부팅을 통해 VM 사전 부팅 및 부팅 무결성을 검증할 수 있습니다. 자세한 내용은 vTPM(Virtual Trusted Platform Module)을 참조하세요.

  • enableIntegrityMonitoring: 무결성 모니터링을 사용 설정 또는 중지합니다. 무결성 모니터링을 사용하면 Cloud Monitoring 보고서를 통해 보안 VM 인스턴스의 런타임 부팅 무결성을 모니터링하고 확인할 수 있습니다. 자세한 내용은 무결성 모니터링을 참조하세요.

인스턴스 만들기에 대한 자세한 내용은 instances.insert() 문서를 참조하세요.

Windows Server 인스턴스나 SQL Server 인스턴스를 만든 후에는 RDP를 통해 인스턴스에 연결할 수 있도록 인스턴스의 초기 비밀번호를 설정합니다.

또한 VM을 만드는 동안 또는 VM을 만든 후 관리형 Microsoft AD 도메인에 VM을 조인할 수 있습니다. 자세한 내용은 도메인에 Windows VM 자동 가입을 참조하세요.

내부 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
}

자바


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 Interconnect 또는 Cloud VPN을 사용하여 VPC 네트워크에 연결된 다른 네트워크에서 연결하거나 먼저 RDP를 통해 배스천 인스턴스에 연결한 후 내부 IP 주소만 있는 인스턴스에 연결할 수 있습니다.

또한 VM을 만드는 동안 또는 VM을 만든 후 관리형 Microsoft AD 도메인에 VM을 조인할 수 있습니다. 자세한 내용은 도메인에 Windows VM 자동 가입을 참조하세요.

kms.windows.googlecloud.com에 대한 액세스 구성

Windows 정품 인증 및 갱신을 위해서는 VPC 네트워크가 다음 라우팅 및 방화벽 규칙 요구사항을 충족해야 합니다.

라우팅 요구사항

Windows 인스턴스는 다음 홉이 기본 인터넷 게이트웨이인 경로를 통해 kms.windows.googlecloud.com(35.190.247.13)에 도달할 수 있어야 합니다. kms.windows.googlecloud.com은 Compute Engine 인스턴스로 확인되지 않은 IP 주소의 정품 인증 요청을 거부하므로 인스턴스 기반 NAT 게이트웨이 또는 Cloud NAT를 사용하여 Windows 인스턴스의 정품 인증을 진행할 수 없습니다.

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에 도달하도록 허용합니다. 외부 IP 주소가 없거나 Cloud NAT를 사용하는 Windows 인스턴스가 있을 경우 내부 IP 주소만 있는 인스턴스가 kms.windows.googlecloud.com의 외부 IP 주소(35.190.247.13)로 트래픽을 전송할 수 있도록 비공개 Google 액세스를 사용 설정해야 합니다.

방화벽 규칙 요구사항

묵시적인 이그레스 허용 방화벽 규칙은 인스턴스가 요청을 수행하고 설정된 응답을 수신하도록 허용합니다. 이그레스를 거부하는 커스텀 방화벽 규칙을 만들지 않았다면 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 네트워크 이름으로 바꿉니다.

인스턴스가 시작되었는지 확인

Windows 인스턴스는 sysprep 프로세스로 인해 시작 시간이 비교적 많이 걸립니다. 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 인스턴스 기능 사용 및 사용 중지

이미지 버전이 v20170509 이상이거나 에이전트 버전이 4.1.0 이상인 Windows 인스턴스인 경우 구성 파일이나 프로젝트 또는 인스턴스 커스텀 메타데이터에서 인스턴스 구성을 설정할 수 있습니다. 구성 파일은 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-managertrue로 설정합니다.

주소 관리자 사용 중지

  • 구성 파일 항목:

    [addressManager]
    disable=true
    
  • 커스텀 메타데이터에서 메타데이터의 disable-address-managertrue로 설정합니다.

Windows Server 장애 조치 클러스터링

Windows Server 장애 조치 클러스터링 에이전트 사용 설정:

  • 구성 파일 항목:

    [wsfc]
    enable=true
    
  • 커스텀 메타데이터에서 메타데이터의 enable-wsfctrue로 설정합니다.

여러 내부 부하 분산기 사용

장애 조치 클러스터링을 위한 내부 부하 분산 인스턴스의 IP 주소를 지정합니다. 이는 전용 장애 조치 클러스터에는 설정할 필요가 없는 고급 구성입니다.

일반적으로 내부 부하 분산 인스턴스를 사용하여 한 번에 하나의 VM 인스턴스로 네트워크 트래픽을 전달합니다. 부하 분산된 웹사이트 백엔드의 일부로 장애 조치 클러스터링 VM 인스턴스를 사용하는 또 하나의 내부 부하 분산 인스턴스를 추가하는 경우 내부 부하 분산 IP 주소가 2개 생깁니다. 장애 조치 클러스터링에서 10.0.0.10을 사용하고 웹사이트의 부하 분산기에서 10.0.0.11을 사용하는 경우 장애 조치 클러스터링에 사용하는 부하 분산기의 IP 주소를 지정해야 합니다. 이렇게 하면 클러스터에 사용되는 주소를 구별할 수 있습니다.

  • 구성 파일 항목:

    [wsfc]
    addresses=10.0.0.10
    
  • 커스텀 메타데이터에서 wsfc-addrs10.0.0.10로 설정합니다.

클러스터링 에이전트 포트 변경

장애 조치 클러스터링 에이전트 포트를 설정합니다. 기본 포트는 59998입니다. 다른 포트를 사용하려는 경우에만 포트를 지정하면 됩니다.

  • 구성 파일 항목:

    [wsfc]
    port=12345
    
  • 커스텀 메타데이터에서 wsfc-agent-port를 포트 번호로 설정합니다.

이미지 버전에 대한 참고사항

이전 이미지에는 구성 파일이 사용되지 않으며 기능이 하위 집합만 존재합니다. 이미지 버전 v20160112부터 v20170509까지 또는 Windows 에이전트 버전 3.2.1.0부터 4.0.0까지의 경우 커스텀 메타데이터 값을 다음과 같이 사용해야 합니다.

  • 계정 관리자를 사용 중지하려면 인스턴스 메타데이터에서 disable-account-managertrue로 설정합니다.
  • 주소 관리자를 사용 중지하려면 인스턴스 메타데이터에서 disable-address-managertrue로 설정합니다.

다음 단계