JavaScriptを有効にしてください

Hub Vnet の DNS 管理を自動化し隊

 ·   14 分で読めます  ·   Kento

以下の記事を参考に、Azure の Private Link と DNS の統合に関するベストプラクティスを実際に検証してみます
Private Link と DNS の大規模な統合 - Cloud Adoption Framework | Microsoft Learn

大規模なハブ&スポークの環境の問題

Azure では、ハブ&スポークというアーキテクチャで、ネットワークを分離し、セキュリティや管理性を高めることが多いです

---
title: ハブ&スポークの例
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
    end
    DNS1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
        VM1("fa:fa-desktop VM")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
        PE2("Private Endpoint")
        VM2("fa:fa-desktop VM")
    end
    WA1{{"fa:fa-globe App Services"}}
end

subgraph sub3[Subscription 3]
    subgraph Spoke3["Spoke 3"]
    end
end
 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" --> Spoke1
hub_Vnet <-- "ピアリング" --> Spoke2
hub_Vnet <-- "ピアリング" --> Spoke3
PE1 -.-> ST1
PE2 -.-> WA1

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class OU1 userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNS1 DNSG
 
classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

classDef SCP fill:#e83,color:#fff,stroke:none
class VM1,VM2 SCP

このときハブ用 Vnet には各スポークで利用できる機能を集約させます
DNS の役割も ハブ用 Vnet に集約する場合があります
このハブ用 Vnet が所属するサブスクリプションは様々な機能を集約させるため、管理者だけが権限を持ちます
その結果、スポーク Vnet で Private Endpoint が作成されるたびに 管理者 (ネットワーク管理者) が DNS の A レコードを追記する手間が発生します

---
title: 新しい Private Endpoint が作成されると
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
    end
    DNS1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
        VM1("fa:fa-desktop VM")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
        PE2("Private Endpoint")
        VM2("fa:fa-desktop VM")
    end
    WA1{{"fa:fa-globe App Services"}}
end

subgraph sub3[Subscription 3]
    subgraph Spoke3["Spoke 3"]
        PE3("新しい Private Endpoint")
    end
    ST2{{"fa:fa-folder Storage Account"}}
end
 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" --> Spoke1
hub_Vnet <-- "ピアリング" --> Spoke2
hub_Vnet <-- "ピアリング" --> Spoke3
PE1 -.-> ST1
PE2 -.-> WA1
PE3 -.-> ST2

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class OU1 userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNS1 DNSG
 
classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,ST2,WA1 STG

classDef SCP fill:#e83,color:#fff,stroke:none
class VM1,VM2 SCP
---
title: DNS の A レコードを追加
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
    end
    subgraph DNS1["fa:fa-server DNS Zone"]
        record("新しい A レコード")
    end
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
        VM1("fa:fa-desktop VM")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
        PE2("Private Endpoint")
        VM2("fa:fa-desktop VM")
    end
    WA1{{"fa:fa-globe App Services"}}
end

subgraph sub3[Subscription 3]
    subgraph Spoke3["Spoke 3"]
        PE3("新しい Private Endpoint")
    end
    ST2{{"fa:fa-folder Storage Account"}}
end
 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" --> Spoke1
hub_Vnet <-- "ピアリング" --> Spoke2
hub_Vnet <-- "ピアリング" --> Spoke3
PE1 -.-> ST1
PE2 -.-> WA1
PE3 -.-> ST2

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class OU1 userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNS1 DNSG
 
classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,ST2,WA1 STG

classDef SCP fill:#e83,color:#fff,stroke:none
class VM1,VM2 SCP

公開情報にも以下の記載があります

プライベート DNS ゾーンは通常、ハブ VNet がデプロイされている同じ Azure サブスクリプションで、一元的にホストされます。 この中央ホスティング プラクティスは、クロスプレミス DNS 名前解決や、Active Directory などの中央の DNS 解決に対するその他のニーズによって推進されます。 ほとんどの場合、ゾーンで DNS レコードを管理するアクセス許可を持つのは、ネットワークと ID の管理者のみです。
アプリケーション チームは、所有するサブスクリプションに Azure リソースを作成するアクセス許可を持っています。 彼らには、プライベート DNS ゾーンの DNS レコードの管理を含む、中央ネットワーク接続サブスクリプションでのアクセス許可が与えられていません。 このアクセス制限は、プライベート エンドポイントでの Azure PaaS サービスのデプロイ時に、必要な DNS レコードを作成できないことを意味します。

Private Link と DNS の大規模な統合 - Cloud Adoption Framework | Microsoft Learn

今回はこの「Private Endpoint が作成されるたびに DNS を管理者が手動更新する」課題を Azure Policy を使って解決するのが目的です

実装イメージ

今回はここに書かれている内容を参考にします
Private Link と DNS の大規模な統合 - Cloud Adoption Framework | Microsoft Learn

具体的には以下のような手順です。

事前準備

  1. ハブに DNS Private Resolver をデプロイし、スポーク Vnet の DNS 設定を向けておきます
    • Azure 内に限定するなら転送ルールセットを使った実装でも構いません
    • DNS のフォワーダーが欲しいだけなので、IaaS VM に DNS サーバーを立てる方法でも構いません
  2. Private Endpoint を利用するサービスごとに専用の Azure Private DNS Zone を作成します。
    • 例えば、Azure SQL Databaseを利用する場合は privatelink.database.windows.net という名前のDNSゾーンを作成します。
  3. 作成した Private DNS Zone をすべてハブ用仮想ネットワーク内にリンク
    • これにより、ハブ用仮想ネットワーク内から Private DNS Zone が利用可能です
    • DNS Private Resolver を経由した Private DNS Zone の利用も可能です
  4. Azure Policy をスポーク Vnet に割り当てます

利用時

  1. 利用したい PaaS サービスをデプロイ
  2. Vnet 内から利用できるようにスポーク Vnet に Private Endpoint を作成
  3. Azure Policy により DNS レコードがデプロイされるのを待機
  4. (不要になったら) Private Endpoint の削除

検証

実際に上記の手順で実装してみました。以下の図は検証環境の概要です。

---
title: 検証環境の全体像
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
        PE2("Private Endpoint")
    end
    WA1{{"fa:fa-globe App Services"}}
end

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
PE1 -.-> ST1
PE2 -.-> WA1

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class OU1 userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG
 
classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

classDef SCP fill:#e83,color:#fff,stroke:none
class VM1,VM2 SCP

事前準備 1. ハブ用 DNS Private Resolver の作成

Azure DNS Private Resolver を作成します
今回は特に受信エンドポイントを使用します
専用サブネットが必要になるので注意してください

---
title: 事前準備 1. ハブ用 DNS Private Resolver の作成
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
end

subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
end

%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル 
portal02
DNS Private Resolver のデプロイ:

作成した Private DNS Resolver を利用するように、各スポーク VNet の設定を変更します

portal03
Vnet の DNS サーバーの設定:

事前準備 2. Azure Private DNS Zone の作成

---
title: 事前準備 2. Azure Private DNS Zone の作成
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server 各 DNS Zone")
end

subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
end
 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

以下の環境を作成します
Private DNS Zone は Azure プライベート エンドポイントのプライベート DNS ゾーンの値 | Microsoft Learn を参考に以下の 2 つを作成しておきます

プライベート リンク リソースの種類 サブリソース プライベート DNS ゾーンの名前 パブリック DNS ゾーンのフォワーダー
Storage アカウント (Microsoft.Storage/storageAccounts) blob privatelink.blob.core.windows.net blob.core.windows.net
Azure App Services - Azure Function Apps (Microsoft.Web/sites) sites privatelink.azurewebsites.net azurewebsites.net

事前準備 3. Azure Private DNS Zone をハブ用 Vnet にリンク

---
title: 事前準備 3. Azure Private DNS Zone をハブ用 Vnet にリンク
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server 各 DNS Zone")
end

subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
end
 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

Private DNS Zone の作成およびハブ用 Vnet へのリンクが完了するとこのような状態になります

portal01
Private DNS Zone のデプロイ:

事前準備 4. スポーク用サブスクリプションに Azure Policy を割り当て

---
title: 事前準備 4. スポーク用サブスクリプションに Azure Policy を割り当て
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server 各 DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
end

Policy{{"Azure Policy"}}

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
Policy -.-> sub1
Policy -.-> sub2

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class Policy userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

以下の Azure Policy をスポークサブスクリプションに割り当てます

Azure Policy の目的 ポリシー例 リンク 必須か
PaaS のパブリック エンドポイントを拒否・修正 パブリック ネットワーク アクセスを無効にするようにストレージ アカウントを構成する リンク ×
PaaS のパブリック エンドポイントを監査 イニシアティブ定義 “パブリック ネットワーク アクセスの監査” リンク ×
Private DNS Zone の作成を拒否 許可されないリソースの種類 リンク ×
ハブの Private DNS Zone に DNS レコードを自動的に作成 BLOB groupID 用のプライベート DNS ゾーン ID を構成する リンク

参考:組み込みのポリシー定義の一覧 - Azure Policy | Microsoft Learn

各ポリシーを簡単に説明しておきます

  • PaaS のパブリック エンドポイントを拒否・修正
    • Private Endpoint を使用する場合はパブリック エンドポイントをブロックする場合が多いです。ユーザーの設定忘れを防ぐために使用します
    • PaaS サービスごとにポリシーの割り当てが必要です

例えばストレージアカウントは以下のポリシーがあります
効果を Modify で適用すると、デプロイ時に自動的にパブリック アクセスが無効化されます

portal06
Azure Policy: パブリック ネットワーク アクセスを無効にするようにストレージ アカウントを構成する

App Service では以下のポリシーがあります
こちらは Deny で設定しておくことで、パブリックアクセスを有効化のままデプロイすることが拒否されます

portal17
Azure Policy: パブリック ネットワーク アクセスが有効化だと作成できない
portal18
Azure Policy: 強制的にパブリックアクセスが無効化されている
  • PaaS のパブリック エンドポイントを監査
    • パブリック エンドポイントをすべてブロックすると不都合が発生する場合は、監査だけに留めておきます
    • 拒否の設定で実装漏れがあったとき用に監査ポリシーを設定しておくことも重要です
    • PaaS サービスごとにポリシーの割り当てが必要です
    • 様々な PaaS サービスの Azure Policy がまとまっているイニシアティブが存在していて便利です
portal07
Azure Policy: パブリック ネットワーク アクセスを監査するイニシアティブ定義
  • Private DNS Zone の作成を拒否
    • ハブ用サブスクリプションに DNS を集約させるため、ユーザーに自由に Private DNS Zone を作成させるのを防ぎます

“許可されないリソースの種類” という名前の Azure Policy はブラックリスト方式で作成を許可しないサービスを定義できます
これを使って Private DNS Zone の作成を防ぎます

portal04
Azure Policy: Private DNS Zone の作成を拒否

portal08
Azure Policy: Private DNS Zone の作成を拒否
  • ハブの Private DNS Zone に DNS レコードを自動的に作成
    • スポーク Vnet に Private Endpoint がデプロイされるとハブの集約 Private DNS Zone に A レコードをデプロイします
    • PaaS サービスごとにポリシーの割り当てが必要です

Blob の Private Endpoint を作成する場合は、以下のようにポリシーを割り当てます
このときパラメータにはハブ用サブスクリプションにある Private DNS Zone (事前準備2 で作成したもの) を指定します

portal05
Azure Policy: Blob の A レコードを作成するポリシー

App Services の場合は以下のポリシーになります

portal16
Azure Policy: App Services の A レコードを作成するポリシー

App Service の Private Endpoint 用に「プライベート DNS ゾーンを使用するように App Service アプリを構成する」というポリシーを割り当てました
しかし DeployIfNotExit の効果がうまく適用されておらず、以下のエラーが表示されました

{“code”:“DeploymentFailed”,“message”:“At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.”,“details”:[{“code”:“LinkedAuthorizationFailed”,“message”:“The client ‘d9e539e4-88b2-45b0-9a11-499116e53123’ with object id ‘d9e539e4-88b2-45b0-9a11-499116e53123’ has permission to perform action ‘Microsoft.Network/privateEndpoints/privateDnsZoneGroups/write’ on scope ‘/subscriptions/xxxx/resourcegroups/free1-rg/providers/Microsoft.Network/privateEndpoints/webapp-pe/privateDnsZoneGroups/deployedByPolicy’; however, it does not have permission to perform action(s) ‘Microsoft.Network/privateDnsZones/join/action’ on the linked scope(s) ‘/subscriptions/xxxx/resourcegroups/dnszone-rg/providers/microsoft.network/privatednszones/privatelink.azurewebsites.net’ (respectively) or the linked scope(s) are invalid.”}]}

内容を確認すると権限が足りてないと書かれています
確認するとポリシーのマネージド ID は Private DNS Zone に対して適切な権限を持っていませんでした
そのため手動でネットワーク共同作成者を割り当てることで問題を回避しました

利用時 1. PaaS のデプロイ

---
title: 利用時 1. PaaS のデプロイ
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
    WA1{{"fa:fa-globe App Services"}}
end

Policy{{"Azure Policy"}}

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
Policy -.-> sub1
Policy -.-> sub2

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class Policy userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

図の通り Storage Account と App Services を作成します
ポリシーの効果を確認するために Storage Account はあえてパブリックアクセスを有効化にしてみます

portal09
Storage Account の作成: パブリック アクセスを有効化

デプロイされた Storage Account を確認すると、ポリシーが適用された結果パブリック アクセスが無効になるように修正されています

portal10
作成した Storage Account: パブリック アクセスが無効になっている

利用時 2. Private Endpoint の作成

---
title: 利用時 2. Private Endpoint の作成
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
            PE2("Private Endpoint")
    end
    WA1{{"fa:fa-globe App Services"}}
end

Policy{{"Azure Policy"}}

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
Policy -.-> sub1
Policy -.-> sub2
PE1 -.-> ST1
PE2 -.-> WA1

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class Policy userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

次に Private Endpoint を作成します
事前に設定済みのポリシーによって Private DNS Zone の作成は禁止されています
そのため、Private DNS Zone との統合で [はい] 選択すると検証に失敗しエラーになります

portal11
Private Endpoint の作成: Private DNS Zone との統合を [はい] にした場合

そのためユーザーは Private DNS Zone の統合は必ず [いいえ] にする必要があります

portal12
Private Endpoint の作成: Private DNS Zone との統合を [いいえ] にした場合

デプロイを実行すると何も問題なく Private Endpoint の作成ができました

portal13
Private Endpoint の作成: Storage Account

App Service の Private Endpoint も同様に作成しておきます

portal19
Private Endpoint の作成: App Service

利用時 3. A レコードの確認

---
title: 利用時 3. A レコードの確認
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    subgraph DNSzone1["fa:fa-server DNS Zone"]
        record("A レコード")
    end
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
        PE1("Private Endpoint")
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
            PE2("Private Endpoint")
    end
    WA1{{"fa:fa-globe App Services"}}
end

Policy{{"Azure Policy"}}

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
Policy -.-> sub1
Policy -.-> sub2
PE1 -.-> ST1
PE2 -.-> WA1

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class Policy userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

classDef PEG fill:#e83,color:#fff,stroke:none
class PE1,PE2,PE3 PEG

Private Endpoint の作成に成功して、少し時間をおいて Private DNS Zone を確認してみると無事に A レコードの作成が完了していました

portal14
A レコードの追加: Blob

Storage Account に関するアクティビティログを確認すると Private Endpoint の作成から約 10 分で A レコードの作成に成功していました

portal15
A レコードの作成までの時間:

App Service も同様に A レコードの作成ができています

portal21
A レコードの追加: App Service

Spoke 2 にある VM からアクセスすると Private Endpoint 経由でアクセスできました

portal24
App Service の接続確認:

利用時 4. Private Endpoint の削除

---
title: 利用時 4. . Private Endpoint の削除
---
graph TB;
 
%%グループとサービス
subgraph hubsub[hub Subscription]
    subgraph hub_Vnet[hub-Vnet]
        DNS[DNS Private Resolver]
    end
    DNSzone1("fa:fa-server DNS Zone")
end
 
subgraph sub1[Subscription1]
    subgraph Spoke1["Spoke 1"]
    end
    ST1{{"fa:fa-folder Storage Account"}}
end
 
subgraph sub2[Subscription 2]
    subgraph Spoke2["Spoke 2"]
    end
    WA1{{"fa:fa-globe App Services"}}
end

Policy{{"Azure Policy"}}

 
%%サービス同士の関係
hub_Vnet <-- "ピアリング" ---> Spoke1
hub_Vnet <-- "ピアリング" ---> Spoke2
hub_Vnet -- "リンク" --> DNSzone1
Policy -.-> sub1
Policy -.-> sub2

 
%%グループのスタイル
classDef subG fill:none,color:#345,stroke:#345
class hubsub,sub1,sub2,sub3 subG
 
classDef VnetG fill:none,color:#0a0,stroke:#0a0
class hub_Vnet,Spoke1,Spoke2,Spoke3 VnetG
 
%%サービスのスタイル
classDef userG fill:#aaa,color:#fff,stroke:#fff
class Policy userG
 
classDef DNSG fill:#006699,color:#fff,stroke:#fff
class DNSzone1 DNSG

classDef STG fill:#46d,color:#fff,stroke:#fff
class ST1,WA1 STG

Private Endpoint の利用が終了して削除した場合を確認します
画像の通り Storage Account と App Service の Private Endpoint を削除しました

portal22
Private Endpoint の削除:

無事に当該の A レコードも削除されています

portal23
A レコードも削除されている:

まとめ

Azure の Private Link と DNS の統合に関するベストプラクティスを実際に検証してみました
Azure Policy を利用することで Private Endpoint が作成されると自動で A レコードが作成されました
Private Endpoint が不要になった場合もきちんと削除されていました
これによりAzure 管理者 (ネットワーク管理者) が手動で管理をしなくても済むため運用負荷が下がることが期待できます

ただし、すべての Private Endpoint でポリシーが用意されているわけではないので、使いたい Private Endpoint が対応していない場合は Github からフィードバックをあげてみてください

ご自身の状況には組み込みのポリシーを使用できない場合は、Azure Policy GitHub リポジトリの新しい組み込みポリシー提案のプロセス に従って、azure-policy フィードバック サイトの「Azure Governance」 · コミュニティ でイシューを作成することを検討してください。

Private Link と DNS の大規模な統合 - Cloud Adoption Framework | Microsoft Learn

参考

共有

Kento
著者
Kento
2020年に新卒で IT 企業に入社. インフラエンジニア(主にクラウド)として活動中