2
0
お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

Windows Server 2025でSMB over QUICを実装してみた(ドメイン環境)02 ~クライアントPC準備編~

Last updated at Posted at 2024-07-05

はい。
図らずもシリーズものとなってしまいました。
以前の記事のドメイン環境編です。
やっぱりエンタープライズ環境を準備するのは大変ですね。
めんどいわー
でもまぁこの面倒さになんやかんや言いながら楽しみを見出すあたりがインフラエンジニアかなと思います。

なんでクライアントPCをわざわざ準備する必要があんの?手元のWindows PCでやったらええやん

そうなんですよ。
その通りですよね。
いや、ホンマ、ごもっとも。
ちなみにWindows 11 Proが実機で2台と、Windows 10 Proが実機で1台、仮想環境上にWindows 10 Proが1台あります。
全部ちゃんとライセンス認証しています。
全部Proなんで、ドメイン参加できます。
もちろんドメインコントローラーも仮想環境上にあり、仮想環境上と実機のあるセグメントは分かれていますがAny Any Permitで接続可能なので、実機はドメイン参加できる環境です。

ほんならなんでわざわざクライアントPC準備するん?

いや、ホンマそうなんですよ。
ただ、この実機のWindows 11/10 Pro 3台、全部Microsoft Entra ID Joinしてるんですよ。
image.png

Microsoft Entra ID Joinを先に行うと、ドメイン参加できないんですよね。
これ、Windowsの仕様です。

もう赤字ばっかりでなんのこっちゃ分かんないですね。

いや、ほんならMicrosoft Entra ID Join外したらええやん

いや、ホンマそうなんですよ。
マジで。
ホンマその通り。
おっしゃる通りですわ。
でもね、我が家の環境、条件付きアクセスやってるんですが、その条件にBit Locker入れてるんですよね。
Bit Locker解除する(複合する)の、めっちゃ時間かかりますやん?
もうこのBit Locker解除が面倒すぎるんで諦めたんですよ。
もうね、Bit Locker解除とか条件付きアクセスの変更とか、Intuneの解除とか、もろもろ面倒すぎるんですよ・・・

1台Microsoft Entra ID Registeredあるやん

あ、気付きました?
そうなんですよ。
Win10Pro-KVMね。
もう1台Microsoft Entra ID Registeredの端末ありますけど、iPhoneなんで今回対象外ですよね。
このWin10Pro-KVMね、その名の通り仮想環境上のWin10 Proなんですよ。
なので今回の検証に使えないんですよ。
今回SMB over QUICなんで、ドメインに参加しつつ、ドメイン配下のファイルサーバーに社外からアクセスするクライアントPCを用意しなきゃいけないんで、これがドメコンと一緒に載ってる仮想環境上だと無理なんですよね。

仮想スイッチでセグメント変えることも考えましたけど、仮想サーバの物理NIC1枚で、しかもその1枚をBridgeでやってるんで無理っぽいんですよね。
Windows Firewallでファイルサーバと直接アクセス不可にするとかも考えたんですが、これだとちょっと不自然ですよね。
検証の客観性に欠けるかなって。(←めんどくさい以外で唯一まともな理由)

いや、一応一通り試したんですよ。
手元の実機使ってできへんかなって。

上述の通り全部無理でした。
もうね、心折れそうですよ。
しかも全部自分のせいですからね。

アホなん?個人の環境にこだわりすぎなんちゃうん?作り込みすぎなんちゃうん?

いや、ホンマ、そうなんですよ。
ぐうの音も出ませんわ。
マジで。
でも面倒なのは面倒なので実施したくないですし、検証終わってから切り戻すこと考えたらもうマジでこの検証やんのイヤになるんで、やりたくないです。

で、結局どうするん?

考え方を変えてAzure上にWindows 11 Pro構築して、そっちをオンプレミスのドメイン環境に参加させます。

はい。
今回もめっちゃ前提長かったですね。
ここまで来るのにいろいろトライ&エラー繰り返したのはなんとなくお分かりいただけたと思います。(伝わったかどうかは別として)

Windows 11 Proとドメイン参加のために仮想ネットワークゲートウェイ、ローカルネットワークゲートウェイ、VPN接続のそれぞれを全部Terraformでコード化する

はい。
やっと本記事の本編です。
自分で言うのもなんなんですが、私はQiita上でもよう喋りますね。
ではコードいってみましょう。

Terraform

変数設定

varriables.tf
# Import Subscription ID variable from OS's Enviroment variable
variable "ARM_SUBSCRIPTION_ID" {
  type        = string
}

# Import Tenant ID variable from OS's Enviroment variable
variable "ARM_TENANT_ID" {
  type        = string
}

# Import Client ID variable from OS's Enviroment variable
variable "ARM_CLIENT_ID" {
  type        = string
}

# Import Client Secret variable from OS's Enviroment variable
variable "ARM_CLIENT_SECRET" {
  type        = string
}

# Generate random text for a unique storage account name
resource "random_id" "random_id" {
  keepers = {
    # Generate a new ID only when a new resource group is defined
    resource_group = azurerm_resource_group.rg.name
  }
  byte_length = 8
}

# system name
variable "system_name" {
  type        = string
  default     = "win11-01"
}

# region
variable "region" {
  type        = string
  default     = "southeastasia"
}

# resource group (rg) name prefix
variable "rg_name_pre" {
  type        = string
  default     = "rg-"
}

# virtual network (vnet) name prefix
variable "vnet_name_pre" {
  type        = string
  default     = "vnet-"
}

# virtual network (vnet) name suffix for client
variable "vnet_name_suf01" {
  type        = string
  default     = "client01"
}

# vnet ip address prefix01 for client
variable "vnet_addr_pre01" {
  type        = string
  default     = "192.168."
}

# vnet ip address suffix for client
variable "vnet_addr_suf01" {
  type        = string
  default     = "200.0/23"
}

# subnet (sn) name prefix
variable "sn_name_pre" {
  type        = string
  default     = "sn-"
}

# sn for client name suffix for client
variable "sn_name_suf01" {
  type        = string
  default     = "client01"
}

# sn for clinet ip address suffix for client
variable "sn_addr_suf01" {
  type        = string
  default     = "201.0/24"
}

# sn for clinet ip address suffix for Virtual Network Gateway
variable "sn_addr_suf02" {
  type        = string
  default     = "200.0/24"
}

# Network Interface Card (nic) name prefix
variable "nic_name_pre" {
  type        = string
  default     = "nic-"  
}

# Network Security Group (nsg) name prefix
variable "nsg_name_pre" {
  type        = string
  default     = "nsg-"  
}

# Public IP Address (pip) prefix
variable "pip_name_pre" {
  type        = string
  default     = "pip-"
}

# IP Configuration (ipconf) suffix
variable "ipconf_suf" {
  type        = string
  default     = "-configuration"
}

# Private IP Address Allocation (priv_ip_alloc)
variable "priv_ip_alloc" {
  type        = string
  default     = "Static" # or Dynamic
}

# admin user (adminuser) name
variable "adminuser" {
  type        = string
  default     = "init-Admin001"
}

# Virtual Machine OS Disk (osdisk) name prefix
variable "vm_osdisk_pre" {
  type        = string
  default     = "osdisk-"
}

# Virtual Machine OS Disk SKU (osdisk_sku)
variable "vm_osdisk_sku" {
  type        = string
  default     = "StandardSSD_LRS"
}

# Virtual Machine SKU (vmsku) name
variable "vm_sku" {
  type        = string
  default     = "Standard_D4_v5"
}

# Virtual Machine (vm) name prefix
variable "vm_name_pre" {
  type        = string
  default     = "vm-"
}

# vm name suffix01
variable "vm_name_suf01" {
  type        = string
  default     = "client01"
}

# Virtual Machine OS Publisher (os_pub) name
# "MicrosoftWindowsDesktop" is Client OS 
# "MicrosoftWindowsServer" is Server OS
# "Canonical" is Ubuntu
# "almalinux" is Almalinux
variable "os_pub01" {
  type        = string
  default     = "MicrosoftWindowsDesktop"
}

# Virtual Machine OS Offer (os_offer) name
# "Windows-10" is Windows 10 
# "Windows-11" is Windows 11
# "WindowsServer" os Windows Server OS
# "0001-com-ubuntu-server-jammy" is Ubuntu
# "almalinux-x86_64" is Almalinux
variable "os_offer01" {
  type        = string
  default     = "Windows-11"
}

# Virtual Machine OS (os_sku) name
# 2016-Datacenter
# 2019-Datacenter
# 2022-datacenter-azure-edition
# 22_04-lts-gen2 (Ubuntu 22.04 LTS)
# 9-gen2 (Almalinux 9)
variable "os_sku01" {
  type        = string
  default     = "win11-22h2-pro"
}

# Virtual Machine OS Version (os_ver) name
variable "os_ver" {
  type        = string
  default     = "latest"
}

# Local Gateway name prefix
variable "LG_name_pre" {
  type        = string
  default     = "lg-"
}

# Local Gateway name suffix
variable "LG_name_suf" {
  type        = string
  default     = "RTX1300"
}

# Local Gateway DDNS FQDN
variable "LG_DDNS" {
  type        = string
  default     = "[ご自身の環境のDDNS]"
}

# VPN connection name prefix
variable "connection_name_pre" {
  type        = string
  default     = "con-"
}

もうね、あんまり言うことないですわ。
最後から8行目の[ご自身の環境のDDNS]と書いてある部分くらいです。
いつぞやの記事で書いている通りDDNSでAzureはVPN張れるので、今回もこれ使ってます。

プロバイダー

providers.tf
terraform {
  required_providers {
        azapi = {
      source  = "azure/azapi"
      version = "~>1.5"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~>3.0"
    }
  }
}

provider "azapi" {
}

provider "azurerm" {
  features {}
  subscription_id   = "${var.ARM_SUBSCRIPTION_ID}"
  tenant_id         = "${var.ARM_TENANT_ID}"
  client_id         = "${var.ARM_CLIENT_ID}"
  client_secret     = "${var.ARM_CLIENT_SECRET}"
}

Terraform実行時のTerraform自身のパラメータですね。

リソースグループ作成

resource_group.tf
# rg Create
resource "azurerm_resource_group" "rg" {
  location = var.region
  name     = "${var.rg_name_pre}${var.system_name}"
}

リソースグループ作成ですね。
大した事してません。
変数から全部呼んでるんで、ここだけだと何やってるかわかんないですね。

仮想ネットワーク作成

vnet_subnet.tf
#----------------------------------------
# client vnet Create
#----------------------------------------
resource "azurerm_virtual_network" "vnet-client" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name     = "${var.vnet_name_pre}${var.vnet_name_suf01}"
  address_space = [ "${var.vnet_addr_pre01}${var.vnet_addr_suf01}" ]
}

# sn-client01 Create
resource "azurerm_subnet" "sn-client01" {
  resource_group_name = azurerm_resource_group.rg.name
  name = "${var.sn_name_pre}${var.sn_name_suf01}"
  virtual_network_name = azurerm_virtual_network.vnet-client.name
  address_prefixes = [ "${var.vnet_addr_pre01}${var.sn_addr_suf01}" ]
  service_endpoints = [ "Microsoft.Storage" ]
}

ここも特筆すべき点は無いですね。

NSG作成

nsg.tf
#------------------------------------
# nsg-sn-client01 Create
#------------------------------------

resource "azurerm_network_security_group" "nsg-sn-client01" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name = "${var.nsg_name_pre}${azurerm_subnet.sn-client01.name}"

## InBound Rule
  security_rule  {
    name = "AllowVnetInBound"
    priority = 100
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "VirtualNetwork"
    destination_address_prefix = "VirtualNetwork"
  }

 security_rule  {
    name = "AllowAzureLoadBalancerInBound"
    priority = 101
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "AzureLoadBalancer"
    destination_address_prefix = "*"
  }

  security_rule {
    name = "AllowInBoundSSHOnPremises"
    priority = 1001
    direction = "Inbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "3389"
    source_address_prefix = "[ご自身のグローバルIPアドレス]"
    destination_address_prefix = "*"
  }

  security_rule {
    name = "DenyAllInBound"
    priority = 4096
    direction = "Inbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

## OutBound Rule
  security_rule  {
    name = "AllowVnetOutBound"
    priority = 100
    direction = "Outbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "VirtualNetwork"
    destination_address_prefix = "VirtualNetwork"
  }

 security_rule  {
    name = "AllowAzureFrontDoor.FirstPartyHttpOutBound"
    priority = 101
    direction = "Outbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "80"
    source_address_prefix = "*"
    destination_address_prefix = "AzureFrontDoor.FirstParty"
    description = "Allow Windows Update Rule01"
  }

 security_rule  {
    name = "AllowAzureUpdateDeliveryHttpsOutBound"
    priority = 102
    direction = "Outbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "443"
    source_address_prefix = "*"
    destination_address_prefix = "AzureUpdateDelivery"
    description = "Allow Windows Update Rule02"
  }

  security_rule {
    name = "AllowAllOutBound"
    priority = 4095
    direction = "Outbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name = "DenyAllOutBound"
    priority = 4096
    direction = "Outbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

}

# Connect nsg-sn-client01 to sn-client01
resource "azurerm_subnet_network_security_group_association" "nsg-sn-client01-to-sn-client01" {
  subnet_id =  azurerm_subnet.sn-client01.id
  network_security_group_id = azurerm_network_security_group.nsg-sn-client01.id
}

[ご自身のグローバルIPアドレス]
くらいですかね。

ストレージカウント作成

storage.tf
# Create storage account for boot diagnostics
resource "azurerm_storage_account" "storage_account" {
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  name                     = "stdiag${random_id.random_id.hex}"
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_account_network_rules" "for_multiple_subnet_id" {
  storage_account_id = azurerm_storage_account.storage_account.id
  default_action = "Deny"
  virtual_network_subnet_ids = [azurerm_subnet.sn-client01.id]
  ip_rules = ["[ご自身のグローバルIPアドレス]"]
}

ここも[ご自身のグローバルIPアドレス]くらいですかね。

Windows 11 Pro作成

windows_vm.tf
# --------------------------------------
# Create Windows 11
# --------------------------------------

# pip01 Create
resource "azurerm_public_ip" "pip-client01" {
  name                = "${var.pip_name_pre}${var.vm_name_pre}${var.vm_name_suf01}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku                 = "Standard"
  allocation_method   = "Static"
}

# nic01 (attached for vm01) Create
resource "azurerm_network_interface" "nic-client01" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf01}"

  ip_configuration {
    name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf01}${var.ipconf_suf}"
    subnet_id = azurerm_subnet.sn-client01.id
    private_ip_address_allocation = "${var.priv_ip_alloc}"
    private_ip_address = "${var.vnet_addr_pre01}201.11"
    public_ip_address_id = azurerm_public_ip.pip-client01.id
  }
}

# windows vm01 Create
resource "azurerm_windows_virtual_machine" "vm01" {
  name                  = "${var.vm_name_pre}${var.vm_name_suf01}"
  admin_username        = "${var.adminuser}"
#  admin_password        = random_password.password.result
  admin_password        = "P@ssw0rd0123"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.nic-client01.id]
  size                  = "${var.vm_sku}"
  priority              = "Spot" # or Regular
  eviction_policy       = "Deallocate"
  
  os_disk {
    name                 = "${var.vm_osdisk_pre}${var.vm_name_pre}${var.vm_name_suf01}"
    caching              = "ReadWrite"
    storage_account_type = "${var.vm_osdisk_sku}"
  }

  source_image_reference {
    publisher = "${var.os_pub01}"
    offer     = "${var.os_offer01}"
    sku       = "${var.os_sku01}"
    version   = "${var.os_ver}"
  }

  boot_diagnostics {
    storage_account_uri = azurerm_storage_account.storage_account.primary_blob_endpoint
  }
}

全然なんて言うことは無いですね。

仮想ネットワークゲートウェイ作成

vnetgateway.tf
# ------------------------------------------
# Create Virtual Network Gateway Subnet
# ------------------------------------------

resource "azurerm_subnet" "GatewaySubnet" {
  resource_group_name = azurerm_resource_group.rg.name
  name = "GatewaySubnet"
  virtual_network_name = azurerm_virtual_network.vnet-client.name
  address_prefixes = [ "${var.vnet_addr_pre01}${var.sn_addr_suf02}" ]
}

# ------------------------------------------
# Create Virtual Network Gateway
# ------------------------------------------

resource "azurerm_public_ip" "pip-vgw" {
  name                = "pip-vgw01"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  allocation_method = "Dynamic"
}

resource "azurerm_virtual_network_gateway" "vgw01" {
  name                = "vgw01"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  type     = "Vpn"
  vpn_type = "RouteBased"

  active_active = false
  enable_bgp    = false
  sku           = "Basic"

  ip_configuration {
    name                          = "vnetGatewayConfig"
    public_ip_address_id          = azurerm_public_ip.pip-vgw.id
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.GatewaySubnet.id
  }
}

はい!
今回のメインディッシュ!!
仮想ネットワークゲートウェイ!!!

でも全然簡単でした!
typeVpnです。
これがVpnじゃなくてExpressRouteだとExpress Route用の仮想ネットワークゲートウェイを構築してくれます。
vpn_typeRouteBasePolicyBaseも指定できます。
しかしMicrosoftさんの推奨はRouteBaseなので、おとなしくRouteBaseにしておきましょう!
あとはskuBasicくらいでしょうか。
ここで言及したsku以外のパラメーターは間違って構築してしまうと変更不可で、再構築が必要になりますので気を付けましょう。
もう一点注意点は、この仮想ネットワークゲートウェイ、構築完了するのに結構時間かかるんですよね。
待ちましょう。

ローカルネットワークゲートウェイ構築

localgateway.tf
# --------------------------------------
# Create Local Network Gateway
# --------------------------------------

resource "azurerm_local_network_gateway" "lgw" {
  name                = "${var.LG_name_pre}${var.LG_name_suf}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  gateway_fqdn     = "${var.LG_DDNS}"
  address_space       = ["192.168.11.0/24","172.16.0.0/24"]
}

はい!
今回の2皿目のメインディッシュ!!
ローカルネットワークゲートウェイ!!!
もうおなか一杯!

さっきDDNSで接続してますと言うてましたが、それがこのコードの下から3行目、gateway_fqdnです。
固定IPアドレス(じゃなくて動的IPアドレスでも動作しますが)の場合はgateway_addressです。
HashiCorpさんのサンプルコードにはgateway_addressしかなかったのでやり方がわかんなかったですが、適当にgateway_fqdnとしてみたら動きました。
またHashiCorpさんのサンプルコードにはaddress_spaceが1つしかなかったので我が家みたいに複数セグメントがある場合はどうしたらいいのかわからなかったのですが、適当に上述の通りに記載してみたら動作しました。
良かった良かった。

「接続」リソース構築

vpn_connection.tf
resource "azurerm_virtual_network_gateway_connection" "vpn-connection" {
    name                       = "${var.connection_name_pre}${var.vnet_name_pre}${var.vnet_name_suf01}-${var.LG_name_pre}${var.LG_name_suf}"
    location                   = azurerm_resource_group.rg.location
    resource_group_name        = azurerm_resource_group.rg.name
    type                       = "IPsec"
    virtual_network_gateway_id = azurerm_virtual_network_gateway.vgw01.id
    local_network_gateway_id   = azurerm_local_network_gateway.lgw.id
    shared_key                 = "P@ssw0rd0123" #-Provided at run time
}

これは瞬殺かと思いきや一番時間がかかりました。
全然うまいこと動きませんでしたね。
サンプルコードがなかなか見つからなかったです。

実行結果

terraform appy

からの

yes

で、しばらく待ったら
image.png
こうなっておしまい!

あ、もう日付が変わってる!

いや、本記事の最初の方はめっちゃグダグダ言ってますけど、VPN張るところまで全部コードで作成できるって素晴らしいですね!

本日はここまで!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0