はじめに
本記事はNSSOL Advent Calendar 2025の15日目の記事になります。
今年のテーマを考えていたとき、今の案件で一緒に働いている方から「WSFC(Windows Server Failover Cluster)」というワードがふと出てきました。
私自身、WSFCを組んだことはなく、そこから「せっかくならAzure上でクラスタを構築してみよう」と思い立ち、さらに今年から取り組み始めたAnsibleも絡めて、今回のテーマに決めました。この記事では以下の3フェーズで構成しています。
| フェーズ | 内容 |
|---|---|
| フェーズ0:環境準備 | Azureリソース・仮想ネットワーク・AD DSの構築 |
| フェーズ1:WSFC構築 | 共有ディスク/クラウドウィットネス構成のクラスタ構築 |
| フェーズ2:Ansible自動化 | WSFCノードへの共通初期設定の自動化 |
前提知識
①WSFC
Windows Server Failover Cluster(WSFC) とは、Windows Serverに標準搭載されている高可用性機能の一つです。複数のノードで1つのクラスタを構成し、どれかのノードで障害が発生した場合に自動的に別ノードへサービスを引き継ぐことができます。
オンプレミス環境ではSQL Serverやファイルサーバーなどでよく利用されてきた仕組みですが、Azureでも同様に共有ディスクを使うことでクラウド上にWSFCを構築できます。
②クォーラム(Quorum)
WSFCでは、複数のノードが連携して1つのクラスタを構成します。しかし、ネットワーク障害などでノード間の通信が分断された場合、どのノードが「生き残っているクラスタ」として動き続けるか を判断する必要があります。
この「クラスタの過半数を決める仕組み」が クォーラム(Quorum) です。
言い換えると、「誰がリーダーかを投票で決める」ような仕組みです。
クォーラムについて具体例でイメージしてみる。
たとえば、3人で構成されたクラスタを考えます。
| ノード | 状態 |
|---|---|
| node1 | 生存 |
| node2 | 生存 |
| node3 | ネットワーク断 |
このとき、node1 と node2 の2票が残っているため、クラスタは過半数(2/3)を維持して稼働を続ける(=正常)と判断します。一方で、1ノードしか残らない場合(票が半分未満になる場合)は、クラスタは「不整合を防ぐために停止」します。これがスプリットブレイン(split-brain)防止の仕組みです。
③ウィットネス(Witness)
しかし、このクォーラムの「票」が偶数になる場合(例:ノードが2台構成など)、過半数の判定が難しくなります。そこで登場するのが ウィットネス(Witness) です。ウィットネスは「第三の投票者」として動作し、クラスタの可否判定をサポートします。今回は2ノードのため、下記の二種類のWitnessを実装します。
| 種類 | 概要 |
|---|---|
| Disk Witness | クラスタ共有ディスクに投票情報を格納します。オンプレミス構成などで利用される方式です。 |
| Cloud Witness | Azure Storage Account に投票情報を格納します。Azure環境で推奨される構成です。 |
フェーズ0 環境準備
Azure上でWSFCを構築するための基盤環境を整備していきます。
このフェーズでは以下を実施します:
- Azureリソースの作成
- 仮想マシン(クラスタノード、AD DS)のデプロイ
- ドメイン参加
- 共有ディスク(クラスタ用ストレージ)の準備
フェーズ0-1 Azure インフラの準備
まずは、WSFC用の仮想ネットワーク等をAzure上に構築します。
| 項目 | 内容 | 補足・ポイント |
|---|---|---|
| リージョン | Japan East | 同一リージョン内でVM・共有ディスクを配置 |
| リソースグループ名 | rg-wsfc-lab-east |
すべての関連リソースをまとめる |
| 仮想ネットワーク (VNet) | 名前:vnet-wsfc-labアドレス空間: 10.0.0.0/24
|
|
| サブネット | 名前:defaultアドレス範囲: 10.0.0.0/24
|
AD DS、WSFCノードを同一サブネット内に配置。 |
| Cloud Witness用 Storage Account | 名前:stwsfcwitness01SKU:Standard汎用v2 冗長性:LRS |
クォーラム用。アクセスキー(key1)を控える |
フェーズ0-2 仮想マシン(WSFC ノード)構築
次に、クラスタを構成する2台のノードをデプロイします。
同一サブネット内に配置し、後でドメインに参加させます。
| 項目 | 内容 | 備考 |
|---|---|---|
| ノード名 | wsfc-node01 / wsfc-node02 | 同一ドメインに参加させる |
| OS | Windows Server 2025 Datacenter Azure Edition | |
| リージョン | Japan East | 同一リージョン内配置 |
| VMサイズ | Standard B4ls v2 | |
| ストレージ | Premium SSD (共有ディスクは別途用意) | OSディスク用 |
| NIC | 各VMに1枚 | WSFCノードをサブネット"default"に配置(node1:10.0.0.4, node2: 10.0.0.5) |
フェーズ0-3 Active Directory(AD DS)構築・準備
同じサブネット内にAD DS用のVMをデプロイし、ドメインコントローラを構築します。
| 項目 | 内容 | 備考 |
|---|---|---|
| ホスト名 | wsfc-AD |
|
| ドメイン名 | wsfc.lab.local |
|
| IPアドレス | 10.0.0.6 | |
| DNS | 自身のIP(10.0.0.6) |
RDPで接続し、サーバーマネージャーから AD DS と DNS サーバー の役割を追加します。
(今回は日本語化は省略しています。)

右上の通知(黄色の三角アイコン)から「このサーバーをドメインコントローラーに昇格」を選択します。「新しいフォレストを追加」→ ルートドメイン名に wsfc.lab.local を指定し、DSRMパスワードを設定して進めます。再起動後、DNSが10.0.0.6を返すことを確認します。
(後でクラスタノードからも同様に名前解決できることを確認します。)


クラスタ管理用アカウント svc-wsfc-admin を作成しておきます。

フェーズ0-4 WSFC ノードのドメイン参加
仮想ネットワークのDNSを 10.0.0.6(AD DS) に変更します。

各ノードで sysdm.cpl を実行 → コンピュータ名の変更画面を開き、ドメイン名 wsfc.lab.local を入力してドメインに参加します。

アカウントは svc-wsfc-admin を使用し、参加後に再起動します。

フェーズ0-5 共有ディスクの作成
クラスタノードが同一ディスクに同時アクセスできるよう、共有ディスクを準備します。
このディスクは後に「クォーラムディスク」や「データディスク」として利用されます。
| 項目 | 内容 |
|---|---|
| ディスク名 | disk-wsfc-shared01 |
| サイズ | 128 GB |
| タイプ | Premium SSD(共有ディスク対応) |
| LUN | 1 |
| 接続対象 | wsfc-node01, wsfc-node02 |
| 使用形式 | NTFSフォーマット、ドライブレター:F:
|
作成した共有ディスクを、wsfc-node01 と wsfc-node02 の両方にアタッチします。LUN(Logical Unit Number)は、「このディスクをOSが何番目のディスクとして認識するか」を示す番号です。 WSFCでは、全ノードで「同じディスクを同じLUN番号で認識している」必要があるため、ここでは 1 に統一します。下図は node02 側での設定例です(node01 も同様に設定します)。

次に、wsfc-node01 にRDP接続し、OS上でディスクの初期化とフォーマットを行います。
この作業は必ず片方(node01)のみで実施してください。

「ディスクの管理」から以下のように設定します:
フェーズ1 WSFC構築
このフェーズでは、Azure 上に Windows Server Failover Cluster (WSFC) を実際に構築していきます。 2台のノード (wsfc-node01 / wsfc-node02) と、Active Directory ドメインに参加済みの環境を前提としています。
ここでのゴールは以下のとおりです。
- フェイルオーバークラスタ機能のインストール
- クラスタ構成の検証と作成
- DNN(Distributed Network Name)によるクラスタ名構成
- クォーラム設定(Disk Witness → Cloud Witness への切り替え)
フェーズ1-1 フェイルオーバークラスタ機能のインストール
まず、各ノード(wsfc-node01 / wsfc-node02)にフェイルオーバークラスタ機能をインストールします。
- サーバーマネージャーを起動
- メニューから 「Manage(管理)」→「Add Roles and Features(役割と機能の追加)」 を選択
- 「機能の選択」で 「Failover Clustering」 にチェック
- 「Include Management Tools(管理ツールを含む)」 にもチェック
- 「次へ」→「インストール」で完了
フェーズ1-2 クラスター構成の検証
Failover Cluster Managerを起動し、右ペインから 「Validate Configuration(構成の検証)」 を選択します。 両ノードを指定し、すべての検証テストを実行します。



結果が 「Success」 であれば正常です。
以下のようなネットワーク構成に関する Warning(警告) が出ても問題ありません。
Warnings
Node
wsfc-node02.wsfc.lab.localis reachable from Nodewsfc-node01.wsfc.lab.localby only one pair of network interfaces.
It is possible that this network path is a single point of failure for communication within the cluster.
Please verify that this single path is highly available, or consider adding additional networks to the cluster.Node
wsfc-node01.wsfc.lab.localis reachable from Nodewsfc-node02.wsfc.lab.localby only one pair of network interfaces.
It is possible that this network path is a single point of failure for communication within the cluster.
Please verify that this single path is highly available, or consider adding additional networks to the cluster.
この警告は、「ノード間の通信経路が1本しか存在しない=単一障害点になる可能性がある」ことを示すものです。MSのドキュメントにあるように警告は無視して問題ありません。オンプレミス環境では、ハートビート用ネットワークを別に設けて冗長化するのが推奨されていますが、Azure 環境では1本の仮想ネットワーク(VNet)内で十分に冗長性が確保されているためです。
参考:
Configure Windows Server Failover Cluster on Azure - Microsoft Learn
フェーズ1-3 クラスター構成の作成
構成検証が終わったら、同じくFailover Cluster Managerで 「Create Cluster(クラスターの作成)」 を選択します。
- ノードに以下を追加
wsfc-node01wsfc-node02
- クラスター名を指定
- WSFCCluster01
💡 WSFCでは共有ディスクの排他制御(Exclusive Access)を行うため、
どのノードが「所有者(Owner)」かが常に明確に決まります。
現在は wsfc-node01 が所有者なので、node02 からはディスクが「オフライン」として見えます。

補足: 管理用クラスターへの仮想 IP は不要(Windows Server 2019以降)
Azure 上で Windows Server 2019 以降の OS でクラスターを作成した場合、 Failover Cluster Manager からクラスターを作成すると DNN (Distributed Network Name) が自動的に構成されます。
Microsoft のサポートブログでも説明されているとおり、Windows Server 2019 以降では:
- Azure 上かどうかを OS が自動検出する
- 管理用クラスター(コアリソース)には 仮想 IP アドレス リソースが作成されない
- クラスター名は DNN として、各ノードの IP に DNS ラウンドロビンで登録される
そのため、管理用クラスターに接続するための仮想 IPは不要です。
参考:
例外:仮想 IP を使うファイルサーバーなどのアプリケーション ロール
一方で、仮想 IP アドレスを前提としたアプリケーション ロール を Azure VM 上の WSFC で公開する場合は話が変わります。
-
Azure の仮想ネットワークでは、VM に割り当てられた IP 以外のアドレスはそのままでは使えない
-
そのため、WSFC のアプリケーション ロールで使用する「仮想 IP アドレス」を
Azure Load Balancer のフロントエンド IP として割り当てる必要がある -
その際に フローティング IP を有効化 し、LB のフロントエンド IP が仮想 IP として機能するように構成します (Azure 上で DHCP サーバを構成する場合も同様の手法を用います)
-
LB のバックエンド プールには全ノードを登録し、
ProbePortパラメーターとヘルス プローブを組み合わせて 「仮想 IP の所有者ノードだけにトラフィックを流す」構成にします
つまり:
- 管理用クラスター名(コアリソース) → LB 不要(DNN で十分)
- アプリケーション ロールの仮想 IP(例:ファイルサーバーのクラスター IP) → LB 必要
という整理になります。
フェーズ1-4 クォーラム構成 (共有ディスク方式)
ここでは、まず Disk Witness(共有ディスクをWitnessとして使用する構成) を設定します。Disk Witness は、クラスター共有ディスク上に投票情報を格納する方式です。
共有ストレージを利用できる場合に選択します。
ポイント
- クォーラムの構成を確認するには、
Get-ClusterQuorumコマンドレットを使用します。- 共有ディスクの所有ノードが切り替わると、ディスクのオンライン状態も自動で移動します。
- Azure 共有ディスク(Shared Disk)を使用している場合、SCSI Persistent Reservation により排他制御が行われます。




Assigned to をみると、Disk Witnessになっていることが分かる。

フェーズ1-5 クォーラム構成 (クラウドウィットネス)
次に、Azure 上でシンプルに構成できる Cloud Witness へ切り替えます。
これは、Azure Storage Account 上にクラスター投票情報(witness blob)を格納する方式です。
「Select Quorum Configuration Option」で Advanced quorum configuration を選択
→ 「Select Quorum Witness」で Configure a cloud witness を選択
→ Azure Storage Account 名とアクセスキーを入力します。



設定後、共有ディスク(Cluster Disk 1)は「Available Storage」カテゴリに戻ります。
これは Disk Witness としての役割が解除されたことを示します。

ストレージの中身。

補足
- Cloud Witness は Azure Storage に対して HTTPS(TCP 443)で通信を行います。
- 閉域構成で利用する場合は、必要に応じてプロキシ/Firewall の通信許可設定が必要です。
フェーズ2 Ansibleで作業を自動化
ここからは、これまで手動で行ってきたクラスタ構築の一連の作業を Ansible で自動化していきます。
フェーズ2-1 WSFCノードへの共通初期設定をAnsibleで
このフェーズでは、クラスタノードとなる2台のVM(wsfc-node03 / wsfc-node04) を対象に、 Windows Server の初期構成から WSFC 機能のインストール、クォーラム構成までを自動化します。
前提として、以下の状態を準備しておきます。
- 2台のノードを Azure 上にデプロイ済み
- 共有ディスクは両ノードにアタッチ済み(事前に
node03側でフォーマット済み) - ドメイン未参加の状態からスタートする
- WinRMは有効化済み
💡 補足
Ansible で Windows Server を操作するためには、各ノードで WinRM (Windows Remote Management) を有効化しておく必要があります。この設定によって、Ansible コントロールノードから WinRM 経由で Windows ノードに接続できるようになります。
今回はHTTP(5985) 接続で実装します。
# 管理者権限でPowerShellを実行
# WinRMサービスを有効化
winrm quickconfig -q
# 自動起動に設定
Set-Service WinRM -StartMode Automatic
# ファイアウォールでポート5985を開放
netsh advfirewall firewall add rule name="WinRM HTTP" dir=in action=allow protocol=TCP localport=5985
# 設定内容を確認
winrm enumerate winrm/config/listener
Ansibleでは、この前提をもとに以下の処理を自動化します:
- WSFC機能のインストール
- クラスター構成(DNN設定含む)
- クォーラム構成(Cloud Witness 方式)
これにより、手動構築と同等の結果を再現性高く・短時間で再構築できることを目指します。
フェーズ2-2 インベントリファイルの作成と疎通確認
node03と04を追加したインベントリファイルをAnsibleのコントロールノード(10.0.0.9)上で作成します。
all:
hosts:
wsfc-node03:
ansible_host: 10.0.0.7
ansible_user: azureuser
ansible_password: "<デプロイ時に設定した管理者ユーザーのパスワード>"
ansible_connection: winrm
ansible_port: 5985
ansible_winrm_transport: ntlm
wsfc-node04:
ansible_host: 10.0.0.8
ansible_user: azureuser
ansible_password: "<デプロイ時に設定した管理者ユーザーのパスワード>"
ansible_connection: winrm
ansible_port: 5985
ansible_winrm_transport: ntlm
💡 補足
HTTPS(5986) 接続を使う場合は、ansible_winrm_scheme: httpsと証明書設定を追加します。
セキュリティ上、パスワードはansible-vaultで暗号化しておくのが推奨です
ansible.windows コレクションを入れたうえで、疎通確認コマンドを実行します。
[azureuser@wsfc-Ansible ~]$ ansible all -i inventory.yml -m win_ping
wsfc-node03 | SUCCESS => {
"changed": false,
"ping": "pong"
}
wsfc-node04 | SUCCESS => {
"changed": false,
"ping": "pong"
}
フェーズ2-3 ドメイン参加 Playbook
まず# Active Directory 用コレクションをインストールします。
ansible-galaxy collection install microsoft.ad
ドメイン参加用のPlaybookであるjoin_domain.ymlを作成します。作成したらplaybookを実行します。
---
- name: Join WSFC nodes to domain
hosts: all
gather_facts: no
# microsoft.ad コレクションを利用
collections:
- microsoft.ad
vars:
wsfc_domain: "wsfc.lab.local"
wsfc_domain_user: "wsfc\\svc-wsfc-admin"
wsfc_domain_password: "<フェーズ0で作成したドメインユーザーのパスワード>"
tasks:
- name: Join domain
membership:
dns_domain_name: "{{ wsfc_domain }}"
hostname: "{{ inventory_hostname }}"
domain_admin_user: "{{ wsfc_domain_user }}"
domain_admin_password: "{{ wsfc_domain_password }}"
state: domain
reboot: true
#playbook実行コマンド
ansible-playbook -i inventory.yml join_domain.yml
結果はこんな感じで出ればOKです。
[azureuser@wsfc-Ansible ~]$ ansible-playbook -i inventory.yml join_domain.yml
PLAY [Join WSFC nodes to domain] ****************************************************************************************************************************
TASK [Join domain] ******************************************************************************************************************************************
changed: [wsfc-node03]
changed: [wsfc-node04]
PLAY RECAP **************************************************************************************************************************************************
wsfc-node03 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
wsfc-node04 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
フェーズ2-4 WSFC 前提構成 Playbook
サーバマネージャーで行った「WSFC に必要な機能インストール 」を行うplaybookであるinstall_wsfc_prereqs.ymlを作成します。同じく作成したら実行します。
---
- name: Install WSFC prerequisites on cluster nodes
hosts: all
gather_facts: no
tasks:
- name: Install Failover Clustering feature
ansible.windows.win_feature:
name:
- Failover-Clustering
state: present
include_management_tools: yes
register: wsfc_feat
- name: Reboot if required
ansible.windows.win_reboot:
when: wsfc_feat.reboot_required
フェーズ2-5 クラスター構成 Playbook
ここでは、クラスターの構成(DNN構成+Cloud Witnessクォーラム)を自動化します。
Playbook名は create_wsfc_cluster.yml としました。
---
- name: Create WSFC cluster (Azure / DNN)
hosts: wsfc-node03
gather_facts: no
become: yes
become_method: runas
become_user: "wsfc\\svc-wsfc-admin"
vars:
ansible_become_password: "<クラスター管理用ユーザーのパスワード>"
tasks:
- name: Create cluster if not exists
ansible.windows.win_powershell:
script: |
$ErrorActionPreference = 'Stop'
$clusterName = 'WSFCCluster02'
$nodes = @('wsfc-node03','wsfc-node04')
Import-Module FailoverClusters
if (-not (Get-Cluster -Name $clusterName -ErrorAction SilentlyContinue)) {
New-Cluster `
-Name $clusterName `
-Node $nodes `
-NoStorage `
-AdministrativeAccessPoint Dns
}
register: cluster_create
- name: Set quorum to Cloud Witness
ansible.windows.win_powershell:
script: |
$ErrorActionPreference = 'Stop'
$clusterName = 'WSFCCluster02'
$saName = 'stwsfcwitness02'
$saKey = '<ストレージアカウントのアクセスキー>'
Import-Module FailoverClusters
$quorum = Get-ClusterQuorum -Cluster $clusterName
if ($quorum.QuorumResource -ne 'Cloud Witness') {
Set-ClusterQuorum `
-Cluster $clusterName `
-CloudWitness `
-AccountName $saName `
-AccessKey $saKey
}
when: cluster_create is succeeded
※実運用では
ansible_become_passwordや Storage Account のアクセスキーは
ansible-vaultなどで暗号化して管理することを推奨します。
💡 補足:権限エラーでハマったポイント
最初にこのPlaybookを実行した際、以下のようなエラーに遭遇しました。
You do not have administrative privileges on the server 'wsfc-node04.wsfc.lab.local'.
Requested registry access is not allowed.
つまり、クラスター作成に利用するドメインアカウントwsfc\svc-wsfc-adminが
各ノードのローカル Administrators グループに追加されていないのが原因でした。
これを解消するには、各ノードで次のコマンドを実行し、svc-wsfc-adminをローカル管理者に追加する必要があります。
Add-LocalGroupMember -Group "Administrators" -Member "wsfc\svc-wsfc-admin"
この追加後にPlaybookを再実行すると、クラスターが正常に構成されました。
Playbook 実行後、いずれかのノード(例:wsfc-node03)に RDP し、PowerShell からクラスタ状態を確認します。
PS C:\Windows\system32> Get-Cluster
Name
----
WSFCCluster02
PS C:\Windows\system32> Get-ClusterNode
Name State Type
---- ----- ----
wsfc-node03 Up Node
wsfc-node04 Up Node
PS C:\Windows\system32> Get-ClusterQuorum
Cluster QuorumResource
------- --------------
WSFCCluster02 Cloud Witness
クラスターマネージャーからも右ペインのConnect to Clusterを選択し、<CLuster on this server...>を選択して接続することでクラスターの状態を確認できます。

※最後に共有ディスクの追加だけGUIで行いました。

まとめ
実際にやってみて強く感じたのは、自動化するには「手作業でやっていること」を一度きちんと言語化して分解し、それをコードとして表現し直す必要があるということでした。
これは思っている以上に骨が折れる作業で、少なくとも準備フェーズだけ見れば、GUI でポチポチ構成した方が圧倒的に早いですし、わかりやすいなと思います。
それでも、一度 Playbook に落とし込んでしまえば、同じ構成を何度でも再現でき、「環境を壊してやり直す」「パラメータだけ変えて検証を繰り返す」といったことが怖くなくなるように思います。 初期の労力と引き換えに、長い目で見ればかなりの効率化と安心感を得られることを、この一年でAnsible等のIaCツールに触れて実感しました。
来年もadvent calenderに投稿できたらいいなと思っています!
ここまで読んでいただき、ありがとうございました!






