【Terraform】 Microsoft Entra ID認証のPoint-to-Site (P2S) VPNを実装してみた

【Terraform】 Microsoft Entra ID認証のPoint-to-Site (P2S) VPNを実装してみた

Clock Icon2025.05.13

こんにちは、コンサルティング部の神野です。

AzureでPoint-to-Site (P2S) VPNを、Microsoft Entra ID認証で、Terraformを使って構築してみました。Azure Portal上から構築するケースは記事としてインターネットにあると思いますが、Terraformでやった例はあまりなかったので実際にやってみました。
Terraformのコード例なんかも交えながら、構築の手順やポイントをご紹介いたします。

Point-to-Site VPNとは?

Point-to-Site VPN(P2S VPN)は、個々のクライアント(例えば皆さんのノートPCなど)から、Azure上の仮想ネットワーク(VNet)に対して、安全な暗号化されたトンネルを確立して接続するためのサービスです。

例えば、どのような場合に便利かと言いますと、以下のようなケースが考えられます。

  • リモートワーク中の社員が、社内システムが稼働するAzure VNetに安全にアクセスしたい場合
  • 開発者が、開発・テスト環境のAzure VNetに接続して作業を行いたい場合
  • 運用管理者が、本番環境のプライベートなリソース(データベースなど)にメンテナンスでアクセスしたい場合

よく比較されるサイト間(Site-to-Site)VPNとの主な違いとしては、クライアント側に専用のVPNデバイスが不要である点や、クライアント側で固定のパブリックIPアドレスが必須ではない点が挙げられます。基本的には、各クライアントPCにVPNクライアントソフトウェアをインストールするだけで利用開始できる手軽さがあるかと思います。

Azure P2S VPNでは、主に以下の認証方式がサポートされています。

  1. 証明書認証: 自己署名証明書や、企業内の認証局(CA)が発行した証明書を使用して認証します。
  2. RADIUS認証: 既存のRADIUSサーバーと連携して認証を行います。
  3. Microsoft Entra ID認証: Azure ADのユーザーアカウントを使用して認証します。これが今回の主題です。

3に記載したようにMicrosoft Entra ID認証による接続も可能で、多要素認証(MFA)を必須にしたり、条件付きアクセスといったMicrosoft Entra IDの優れた認証機能をフル活用できるのが嬉しいポイントかなと思います。
今回はMicrosoft Entra IDを使った認証方法でP2S VPNを実現してみたいと思います。

前提・準備

実際に環境を構築していく前に、お手元に以下のものをご用意いただけると、この後の手順がスムーズに進められるかと思います。

  • Azureサブスクリプション
    • サブスクリプションレベルで共同作成者 (Contributor) ロールがあれば問題ないかと思います。一部アプリケーションの同意部分は別途権限が必要になります。
  • Microsoft Entra ID(旧Azure AD)テナント
    • Freeエディションでも今回の構成は可能
  • Terraformがインストールされた環境
    • 本記事ではTerraform v1.11.4 on darwin_arm64を使用しました

鍵の作成

今回のコードでは、仮想マシンへのSSH接続のために公開鍵認証を使用します。
SSH鍵を用意していない場合は、以下のコマンドで生成してください。
下記ではazure_keyといった名前で作成しています。実際に使われる際はパスフレーズを含めたり、任意の名前をお使いください。

実行コマンド
# SSH鍵を生成(パスフレーズなし)
ssh-keygen -t rsa -b 2048 -f ~/.ssh/azure_key -N ""

# 鍵が生成されたことを確認
ls -la ~/.ssh/azure_key*

アプリケーションの同意

Microsoft Entra ID認証でP2S VPNを構築する上でAzure VPNアプリケーションに対する管理者の同意が必要なため行います。

同意の手順

  1. 以下の形式のURLをブラウザで開きます。YOUR_TENANT_ID の部分をご自身のMicrosoft Entra IDテナントIDに置き換えてください。
    https://login.microsoftonline.com/YOUR_TENANT_ID/adminconsent?client_id=41b23e61-6c1e-4545-b367-cd054e0ed4b4
    • Microsoft Entra IDテナントIDはAzure Portal上から確認可能です。
      CleanShot 2025-05-12 at 08.52.32@2x
    • client_id=41b23e61-6c1e-4545-b367-cd054e0ed4b4 は、Azure VPNを表すグローバルなアプリケーションIDです。
  2. Microsoft Entra IDの全体管理者アプリケーション管理者など、アプリケーションに組織全体の同意を与える権限を持つアカウントでサインインします。
  3. 「組織の代理として同意する」といった内容の画面が表示されます。要求されているアクセス許可(通常はユーザープロファイルの読み取りなど)を確認し、「承諾」または「Accept」ボタンをクリックします。

CleanShot 2025-05-12 at 08.51.40@2x

この操作により、「Azure VPN」アプリケーションが、テナント内のユーザープロファイル情報(VPN認証に必要)にアクセスできるようになります。Terraformでリソースをデプロイする前に行っておきましょう!

承諾するとエンタープライズ アプリケーションにAzure VPNが追加されています。

CleanShot 2025-05-12 at 08.54.29@2x

認証するユーザーを限定する場合

Microsoft Entra IDテナントの限られたユーザーやグループに対してのみ、認証してVPNを接続するケースの場合は下記から設定を行います。

エンタープライズ アプリケーションの一覧を開きAzure VPNのリンクをクリック

CleanShot 2025-05-12 at 08.54.29@2x

プロパティから割り当てが必要ですか?はいに切り替えて保存

CleanShot 2025-05-12 at 09.00.22@2x

ユーザーとグループメニューを選択し、+ 追加したいユーザーまたはグループの追加を選択して追加したいユーザーやグループを選択すればOKです!

CleanShot 2025-05-12 at 09.00.47@2x

今回構築するシステムの構成イメージ

言葉だけでは分かりにくいかもしれませんので、今回Terraformで構築するシステムの簡単な構成図を以下に示します。また、Terraformで作成するスコープも図に示しています。

CleanShot 2025-05-12 at 09.30.06@2x

主要なコンポーネントは以下の通りです。

  • 仮想ネットワーク(VNet): VPNで接続したいリソースが存在するプライベートなネットワーク空間です。
  • 仮想ネットワークゲートウェイ: P2S VPN接続の「窓口」となるAzureのリソースです。
  • サブネット: ネットワークセグメントです。
    • VM Subnet: テスト用の仮想マシン(VM)などを配置するためのサブネットです。
    • GatewaySubnet: VPNゲートウェイ専用のサブネットです。この名前は固定となります。
  • 仮想マシン(VM): VPN接続後、通信テストを行うためのLinux VMを一台配置します。
  • Microsoft Entra ID: ユーザー認証の基盤となります。

このような環境をTerraformで構築していきます。

Terraformでの実装

さて、お待たせしました。ここからは実際にTerraformのコードを見ながら、P2S VPNを構築していく流れをご紹介します。

Terraformのファイル構成

今回は、以下のようなファイル構成で進めました。機能ごとにファイルを分割しておくと、後から見直した際に構成を把握しやすいかと考えています。

ディレクトリ構成
.
├── main.tf        # リソースグループなど、基本的なリソースを定義
├── network.tf     # VNetやサブネットなど、ネットワーク関連リソース
├── vm.tf          # 接続テスト用のVM
├── vpn.tf         # 今回の主役、VPNゲートウェイ関連の設定
└── variables.tf   # 各種設定値を外部から渡せるように変数定義

それぞれのファイルの中身を、ポイントを絞って見ていきましょう。

1. リソースグループの作成 (main.tf)

まずは、Terraform本体とAzureプロバイダ(azurerm)のバージョンを指定します。
また、すべてのリソースのコンテナとなるリソースグループを作成します。

main.tf
# main.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.27.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = var.subscription_id
} 

# リソースグループ
resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

変数resource_group_namelocationsubscription_idvariables.tfで定義され、terraform.tfvarsから具体的な値が読み込まれます。

既存のリソースグループを使用する場合は、dataブロックで参照する方法も可能です。イメージとしては下記です。

data "azurerm_resource_group" "rg" {
  name = "任意のリソースグループ名"
}

2. ネットワークリソースの設定 (network.tf)

次に、VNetとサブネットを設定します。

network.tf
# network.tf
# 仮想ネットワークとサブネット
resource "azurerm_virtual_network" "vnet" {
  name                = "p2s-vpn-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "default_subnet" {
  name                 = "default"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.0.0/24"]
}

resource "azurerm_subnet" "gateway_subnet" {
  # VPN Gatewayには "GatewaySubnet" という名前のサブネットが必須
  name                 = "GatewaySubnet"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

ここで重要なポイントが一つあります。VPNゲートウェイを配置するためのサブネットは、必ず「GatewaySubnet」という名前にする必要があります。これはAzure側の制約ですので、留意しましょう。アドレス範囲も、他のサブネットと重複しないように注意してください。

3. VPNゲートウェイの設定 (vpn.tf)

いよいよ今回の主要部分、VPNゲートウェイの設定です。Microsoft Entra ID認証を利用するための設定が鍵となります。

vpn.tf
# vpn.tf
# 仮想ネットワークゲートウェイ関連リソース

# VPNゲートウェイ用パブリックIPアドレス
resource "azurerm_public_ip" "vpngw_pip" {
  name                = "p2s-vpn-gateway-pip"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

# 仮想ネットワークゲートウェイ
resource "azurerm_virtual_network_gateway" "vpngw" {
  name                = "p2s-vpn-gateway"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  type                = "Vpn"
  vpn_type            = "RouteBased"
  sku                 = "VpnGw1"
  generation          = "Generation1"
  active_active       = false # 「アクティブ/アクティブ モード: 無効」
  enable_bgp          = false # BGPは特に設定されていないため無効 

  # private_ip_address_enabled = false # 「ゲートウェイ プライベート IP: 無効」

  ip_configuration {
    name                          = "vpngw-ipconfig"
    public_ip_address_id          = azurerm_public_ip.vpngw_pip.id
    private_ip_address_allocation = "Dynamic" # GatewaySubnet上のIP
    subnet_id                     = azurerm_subnet.gateway_subnet.id
  }

  # P2S VPN設定
  vpn_client_configuration {
    address_space = var.vpn_client_address_space

    # Microsoft Entra ID認証の設定
    aad_tenant   = "https://login.microsoftonline.com/${var.tenant_id}/"
    aad_audience = var.vpn_app_id
    aad_issuer   = "https://sts.windows.net/${var.tenant_id}/"

    # OpenVPNプロトコルを有効化
    vpn_client_protocols = ["OpenVPN"]

    # OpenVPNの認証タイプ
    vpn_auth_types = ["AAD"]
  }
}

このVPNゲートウェイの設定には下記点に注意しました。

  1. sku: Microsoft Entra ID認証を利用する場合、VpnGw1以上のSKUを選択する必要があります。Basic SKUは対応していないため注意が必要です。SKUによって性能や料金も変動するため、要件に合わせて選択しましょう。
  2. vpn_client_configurationブロック: ここがMicrosoft Entra ID認証設定の中心です。
    • aad_tenant: ご自身のMicrosoft Entra IDテナントのURLを指定します。${var.tenant_id} の部分にはテナントIDが入ります。
    • aad_audience: 「Azure VPN」という名前のAzure ADエンタープライズアプリケーションのアプリケーションIDを指定します。通常は 41b23e61-6c1e-4545-b367-cd054e0ed4b4 という固定値になります。
    • aad_issuer: トークン発行者のURLです。これもテナントIDを含みます。
    • vpn_client_protocols: ["OpenVPN"] を指定します。前述の通り、Microsoft Entra ID認証はOpenVPNプロトコルと組み合わせて利用します。
    • vpn_auth_types: ["AAD"] を指定し、Azure ADで認証することを明示します。

これらの設定値、特に aad_audience は最初は少し分かりにくいかもしれませんが、Azureの公式ドキュメントにも記載されている値ですので、基本的にはこの値で問題ないはずです。

https://learn.microsoft.com/ja-jp/azure/vpn-gateway/openvpn-azure-ad-tenant

実際に使用される場合はもう少しVPN Gatewayの設定を注視・検討する必要があるかと思いますが、今回は簡易的に接続することをメインに記載しているので、省略いたします。

4. テスト用仮想マシンの設定 (vm.tf)

VPN接続後、VNet内のリソースに正しくアクセスできるか確認するために、簡単なLinux仮想マシンを一台構築しておきます。接続が楽なように今回は10.0.0.4のプライベートIPで起動するようにします。

vm.tf
# vm.tf
# 仮想マシン関連リソース

# VM用ネットワークインターフェース (NIC)
resource "azurerm_network_interface" "vm_nic" {
  name                = "test-nic"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "vm-ipconfig"
    subnet_id                     = azurerm_subnet.default_subnet.id
    private_ip_address_allocation = "Static"
    private_ip_address            = "10.0.0.4"
  }
}

# 仮想マシン (Linux)
resource "azurerm_linux_virtual_machine" "vm" {
  name                            = "test-1"
  computer_name                   = "test-1" # VM内のホスト名
  resource_group_name             = azurerm_resource_group.rg.name
  location                        = azurerm_resource_group.rg.location
  size                            = "Standard_B2s"
  admin_username                  = "azureuser"    
  disable_password_authentication = true            
  network_interface_ids           = [azurerm_network_interface.vm_nic.id]

  admin_ssh_key {
    username   = "azureuser"
    public_key = file("~/.ssh/azure_key.pub") # ご自身のSSH公開鍵ファイルパスに置き換えてください
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }
}

SSH公開鍵のパスは、ご自身の環境に合わせて修正してください。本記事例では事前に生成したazure_key.pubを利用しています。

5. 変数の設定 (variables.tf)

Terraformのコード内で直接値を記述する代わりに、変数として外部から渡せるようにしておくと再利用性が高まります。あくまで参考レベルなのでご自身で好きな構成にしてください!

variables.tf
# variables.tf

variable "tenant_id" {
  description = "Microsoft Entra ID テナントID"
  type        = string
}

variable "vpn_app_id" {
  description = "Azure VPN アプリケーションID"
  type        = string
}

variable "vpn_client_address_space" {
  description = "VPNクライアントに割り当てるアドレス空間"
  type        = list(string)
  default     = ["172.16.0.0/24"]
}

variable "subscription_id" {
  description = "AzureのサブスクリプションID"
  type        = string
}

variable "resource_group_name" {
  description = "リソースグループ名"
  type        = string
  default     = "p2s-vpn-test-rg"
}

variable "location" {
  description = "リソースのリージョン"
  type        = string
  default     = "Japan East"
}

変数の値は、terraform.tfvarsファイルを使って設定します。
各種Azure Portalから確認するものや、ご自身で任意の名前を設定いただくものがございます。
特にsubscription_idtenant_idはAzure Portal上から確認して設定いただく必要があります。

terraform.tfvars
# terraform.tfvars

# サブスクリプションID Azure Portalから確認可能
subscription_id     = "YOUR_SUBSCRIPTION_ID" 
# 任意の名前を設定可能
resource_group_name = "p2s-vpn-test-rg"
# 任意のlocationを設定可能
location            = "Japan East"
# Entra IDのテナントID Azure Portalから確認可能
tenant_id           = "YOUR_TENANT_ID"
# Azure VPNアプリのID(固定値)
vpn_app_id          = "41b23e61-6c1e-4545-b367-cd054e0ed4b4"

デプロイと接続手順

コードの準備が整ったら、いよいよデプロイです。大まかには以下の流れで進めていきます。
「前提・準備」セクションの「アプリケーションの同意」が完了していることが前提です!

1. Terraformの初期化と実行

まず、Terraformのコマンドを使用してリソースをデプロイします。

実行コマンド
# Terraformの作業ディレクトリに移動します
# cd /path/to/your/terraform-azure-p2s-entra-auth

# 1. Terraformを初期化します (プロジェクトの初回のみ)
terraform init

# 2. (任意) デプロイ計画を確認します
terraform plan

# 3. リソースをデプロイします
terraform apply

変数はterraform.tfvarsファイルから自動的に読み込まれるため、コマンドライン引数で指定する必要はありません。
terraform apply を実行すると、作成されるリソースの一覧が表示され、最後に yes と入力することでデプロイが開始されます。

なお、VPNゲートウェイの作成には時間を要する場合があります(30分以上かかることもあります)。処理が完了するまで、しばらくお待ちください。

2. VPNクライアント設定ファイルのダウンロード

次に、VPNクライアントソフトウェアが接続情報を読み込むための設定ファイルをダウンロードします。これはAzureポータルから行うのが分かりやすいでしょう。

  1. Azureポータル(https://portal.azure.com/)にサインインします。
  2. Terraformで作成した仮想ネットワークゲートウェイ(例: p2s-vpn-gateway)のリソースページを開きます。
  3. 左側のメニューから「ポイント対サイトの構成」を選択します。
  4. ページの上部にある「VPNクライアントのダウンロード」ボタンをクリックします。
  5. p2s-vpn-gateway.zip{VPN Gateway名称}.zip) というファイルがダウンロードされるので、これを任意の場所に解凍します。

CleanShot 2025-05-12 at 10.57.24@2x

解凍すると、AzureVPN というフォルダなどが展開されます。

CleanShot 2025-05-12 at 10.59.09@2x

3. Azure VPNクライアントのインストールと設定

Microsoft Entra ID認証でP2S VPNを使用する場合、クライアントPCには専用の「Azure VPN Client」をインストールする必要があります。これはMicrosoft Storeから入手可能です。

  1. Windows PCでMicrosoft Storeを開き、「Azure VPN Client」を検索してインストールします。
    (Macユーザーの場合はApp Storeからダウンロードできます)
  2. インストール後、Azure VPN Clientを起動します。
  3. 左下のインポートボタンを押下します。
    CleanShot 2025-05-12 at 11.00.20@2x
  4. ファイル選択ダイアログが表示されるので、先ほど解凍した p2s-vpn-gateway.zip の中にある AzureVPN フォルダを開き、その中にある azurevpnconfig.xml ファイルを選択して開きます。
    CleanShot 2025-05-12 at 11.01.23@2x
  5. 接続プロファイルがインポートされるので、名前などを確認して「保存」をクリックします。
    CleanShot 2025-05-12 at 11.01.41@2x

下記のように一覧に接続プロファイルが追加されたら、クライアント側の準備も完了です!

CleanShot 2025-05-12 at 11.02.14@2x

4. VPN接続のテスト

これでようやくVPN接続の準備が整いました。実際に接続できるか試してみましょう。

  1. Azure VPN Clientを起動し、先ほどインポートした接続プロファイルを選択します。
  2. 「接続」ボタンをクリックします。
    CleanShot 2025-05-12 at 11.03.51@2x
  3. Microsoft Entra IDのサインイン画面が表示されるので、VPN接続を許可されたユーザーアカウントでサインインします。
    (MFAが有効になっている場合は、MFAの認証も求められます)
  4. 認証が成功すると、接続状態がステータスの色が接続済みの黄緑色に変わるはずです。

CleanShot 2025-05-12 at 11.05.23@2x

無事接続が確認できました。プライベートIPは172.16.0.2 が割り当たっていますね!

CleanShot 2025-05-12 at 11.09.24@2x

5. 接続確認テスト(VMへのSSH)

VPNに接続できたら、VNet内のリソース、今回はテスト用に作成したVMにアクセスできるか確認してみましょう。Terraformの出力で test_vm_private_ip が表示されていたと思いますので、そのIPアドレス(例: 10.0.0.4)に対してSSH接続を試みます。

# VPN接続が確立された状態で、ターミナルやPowerShellからSSH接続
ssh -i ~/.ssh/azure_key azureuser@10.0.0.4

無事にSSHでログインできれば、P2S VPN経由でVNet内のリソースに下記のようにアクセスできていることが確認できました!

CleanShot 2025-05-12 at 11.07.42@2x

かなりお手軽に接続できましたね!!

まとめ

今回はTerraformを使って、Microsoft Entra ID認証によるAzure Point-to-Site VPNを構築する手順をご紹介しました。

本記事が、これからAzureでP2S VPNを試してみようと考えている方の何かしらの参考になれば幸いです。
今後はAzure P2Sを活用してAzure OpenAI Studioのプレイグラウンドなどに、閉域網からより安全かつ簡易的にアクセスする方法などもご紹介できればと考えています!

最後までご覧いただきありがとうございました!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.