両者は同じ「フィルタリング」に見えますが、動く場所もレイヤーも役割も全く違います。この記事では、TerraformでNSGとAzure Firewallの両方を実際にデプロイし、通信制御の動作の差を手を動かしながら確認します。「どちらをいつ使うか」の判断基準もまとめます。
動作確認環境:Ubuntu 24.04 LTS、Terraform 1.9.x、Azure CLI 2.65.x
この記事のポイント
・NSGはL4(IP/ポート制御)、Azure FirewallはL7(FQDN/URLフィルタ)で役割が異なる
・TerraformではNSGはazurerm_network_security_group、Firewallはazurerm_firewallで定義
・本番環境は「NSGで東西トラフィック」「Azure Firewallで南北(インターネット)」が基本構成
・Azure FirewallはAzureFirewallSubnet(/26以上)が必須。デプロイに20~30分かかる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Azure FirewallとNSGの違いを一言で言うと
Azureのセキュリティ設計でよく出てくる疑問が「NSGで十分なのか、Azure Firewallまで必要なのか」です。結論から言うと、2つは補完関係にあり、どちらか一方を選ぶものではありません。NSGはサブネット・NICレベルで動くL4フィルタ、Azure FirewallはVNet全体の出口に置く集中管理型のL7ゲートウェイです。Linuxのiptablesとsquidの違いに近いと言えば分かりやすいかもしれません。
1. NSG(ネットワークセキュリティグループ)の役割
NSGはAzureの最も基本的なネットワーク制御です。ルールはIPアドレス、ポート、プロトコルの組み合わせで定義し、サブネットまたはNICに関連付けます。・適用レイヤー:L4(トランスポート層)
・制御できるもの:送信元/宛先IP、ポート番号、プロトコル(TCP/UDP/ICMP)
・制御できないもの:FQDN(ドメイン名)、URLパス、HTTPSの中身
・費用:NSG自体は無料。NICやサブネットへの関連付けも無料
・管理単位:サブネット単位またはNIC単位
NSGのルールには優先度(100~4096)があり、数値が小さいほど先に評価されます。デフォルトで「VNet内通信を許可」「Azureロードバランサーからの通信を許可」「その他すべて拒否」の3ルールが組み込まれています。
2. Azure Firewall(集中型ファイアウォール)の役割
Azure Firewallは、VNet全体のインターネット向けトラフィックを一か所で管理するフルステートフルなファイアウォールです。NSGでは制御できないFQDNベースのフィルタリングが最大の特徴です。・適用レイヤー:L4~L7(アプリケーション層まで)
・制御できるもの:IP/ポートに加え、FQDN、URL、IDPS(不正侵入検知)
・SNAT/DNAT:アウトバウンドSNAT・インバウンドDNATが標準搭載
・費用:Standardで約100円/時間(月額約72,000円~)
・必須サブネット名:AzureFirewallSubnet(名前固定、/26以上)
Azure Firewallには Standard と Premium の2グレードがあります。PremiumはTLSインスペクション(暗号化通信の中身まで検査)と高精度IDPSが追加されます。本記事ではStandardを使います。
3. NSGとAzure Firewallの違いを1枚のテーブルで整理する
| 比較軸 | NSG | Azure Firewall |
|---|---|---|
| 動作レイヤー | L4(IP/ポート) | L4 ~ L7(FQDN/URL含む) |
| 適用場所 | サブネット・NIC | 専用サブネット(AzureFirewallSubnet) |
| FQDNフィルタ | 不可 | 可能(アプリケーションルール) |
| IDPSサポート | なし | Premiumで対応 |
| SNAT/DNAT | なし | 標準搭載 |
| 費用 | 無料 | 約100円/時間(Standard) |
| 管理の集中化 | サブネット/NICごとに個別管理 | Firewall Policyで一元管理 |
| デプロイ時間 | 30秒~2分 | 20~30分 |
検証環境の構成(Terraform構築前の前提確認)
今回の検証では以下の構成をTerraformで構築します。・VNet:10.0.0.0/16(リージョン:japaneast)
・AzureFirewallSubnet:10.0.1.0/26(Azure Firewall専用、名前変更不可)
・AppSubnet:10.0.2.0/24(テスト用VM配置)
・NSG:AppSubnetに関連付け(L4フィルタ担当)
・Azure Firewall Standard:AzureFirewallSubnetに配置(L7フィルタ担当)
・ルートテーブル:AppSubnetからのデフォルトルートをAzure Firewallに向ける
Azureのサブスクリプション認証は
az login で完了させておいてください。Terraformのbackendはlocalを使います。TerraformでNSGとAzure Firewallを構築する
1. プロバイダ設定(main.tf)
# main.tf terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.110" } } } provider "azurerm" { features {} } variable "location" { default = "japaneast" } variable "rg_name" { default = "rg-fw-nsg-demo" }
2. VNetとサブネット(network.tf)
Azure Firewallのサブネット名はAzureFirewallSubnet と完全一致させる必要があります。名前を変えるとデプロイエラーになります(後述のエラー対処参照)。# network.tf resource "azurerm_resource_group" "main" { name = var.rg_name location = var.location } resource "azurerm_virtual_network" "main" { name = "vnet-demo" address_space = ["10.0.0.0/16"] location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name } # Azure Firewallには必ずこのサブネット名が必要(/26以上) resource "azurerm_subnet" "firewall" { name = "AzureFirewallSubnet" resource_group_name = azurerm_resource_group.main.name virtual_network_name = azurerm_virtual_network.main.name address_prefixes = ["10.0.1.0/26"] } # テスト用VMを配置するサブネット resource "azurerm_subnet" "app" { name = "AppSubnet" resource_group_name = azurerm_resource_group.main.name virtual_network_name = azurerm_virtual_network.main.name address_prefixes = ["10.0.2.0/24"] }
3. NSGの作成とAppSubnetへの適用(nsg.tf)
NSGのルールはpriorityが小さいほど先に評価されます。100から始めて100刻みで余裕を持たせておくと後から挿入しやすくなります。# nsg.tf resource "azurerm_network_security_group" "app" { name = "nsg-app" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name # SSH許可(内部ネットワークからのみ) security_rule { name = "Allow-SSH" priority = 100 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = "10.0.0.0/8" destination_address_prefix = "*" } # HTTP許可(動作確認用) security_rule { name = "Allow-HTTP" priority = 110 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "80" source_address_prefix = "*" destination_address_prefix = "*" } # その他のインバウンドを全拒否 security_rule { name = "Deny-All-Inbound" priority = 4096 direction = "Inbound" access = "Deny" protocol = "*" source_port_range = "*" destination_port_range = "*" source_address_prefix = "*" destination_address_prefix = "*" } } resource "azurerm_subnet_network_security_group_association" "app" { subnet_id = azurerm_subnet.app.id network_security_group_id = azurerm_network_security_group.app.id }
4. Azure FirewallとFirewall Policy(firewall.tf)
Azure FirewallはStatic割り当てのStandard SKUパブリックIPが必要です。ルールはFirewall Policyを経由して管理するのが現在の推奨方式です(Firewallリソースへの直接ルール記述は旧方式)。# firewall.tf resource "azurerm_public_ip" "firewall" { name = "pip-afw-demo" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name allocation_method = "Static" sku = "Standard" } # Firewall Policy(ルールの入れ物) resource "azurerm_firewall_policy" "main" { name = "fwpolicy-demo" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location sku = "Standard" } # ルールコレクショングループ(L7アプリケーションルール + L4ネットワークルール) resource "azurerm_firewall_policy_rule_collection_group" "main" { name = "DefaultRCG" firewall_policy_id = azurerm_firewall_policy.main.id priority = 100 # アプリケーションルール: FQDNでHTTPS通信を制御 application_rule_collection { name = "AllowOutbound-App" priority = 100 action = "Allow" rule { name = "Allow-Ubuntu-Updates" protocols { type = "Https" port = 443 } source_addresses = ["10.0.2.0/24"] destination_fqdns = [ "*.ubuntu.com", "security.ubuntu.com", "archive.ubuntu.com" ] } } # ネットワークルール: DNS(UDP/53)を許可 network_rule_collection { name = "AllowDNS" priority = 200 action = "Allow" rule { name = "Allow-DNS" protocols = ["UDP"] source_addresses = ["10.0.2.0/24"] destination_addresses = ["168.63.129.16"] destination_ports = ["53"] } } } # Azure Firewall本体 resource "azurerm_firewall" "main" { name = "afw-demo" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name sku_name = "AZFW_VNet" sku_tier = "Standard" firewall_policy_id = azurerm_firewall_policy.main.id ip_configuration { name = "fw-ipconfig" subnet_id = azurerm_subnet.firewall.id public_ip_address_id = azurerm_public_ip.firewall.id } }
5. ルートテーブルでAppSubnetの通信をFirewallに向ける
NSGとAzure Firewallは自動的に連携されません。AppSubnetからのアウトバウンド通信をFirewallに向けるには、ルートテーブルで0.0.0.0/0のnext hopをFirewallのプライベートIPに設定する必要があります。# route.tf resource "azurerm_route_table" "app" { name = "rt-app" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name route { name = "ForceToFirewall" address_prefix = "0.0.0.0/0" next_hop_type = "VirtualAppliance" next_hop_in_ip_address = azurerm_firewall.main.ip_configuration[0].private_ip_address } } resource "azurerm_subnet_route_table_association" "app" { subnet_id = azurerm_subnet.app.id route_table_id = azurerm_route_table.app.id }
6. terraform apply の実行と所要時間
$ terraform init $ terraform plan -out=tfplan $ terraform apply tfplan
azurerm_resource_group.main: Creating... azurerm_resource_group.main: Creation complete after 2s [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-fw-nsg-demo] azurerm_virtual_network.main: Creating... azurerm_virtual_network.main: Creation complete after 7s [id=.../virtualNetworks/vnet-demo] azurerm_subnet.firewall: Creating... azurerm_subnet.app: Creating... azurerm_subnet.firewall: Creation complete after 4s [id=.../subnets/AzureFirewallSubnet] azurerm_subnet.app: Creation complete after 5s [id=.../subnets/AppSubnet] azurerm_network_security_group.app: Creating... azurerm_public_ip.firewall: Creating... azurerm_network_security_group.app: Creation complete after 3s [id=.../networkSecurityGroups/nsg-app] azurerm_public_ip.firewall: Creation complete after 4s [id=.../publicIPAddresses/pip-afw-demo] azurerm_firewall_policy.main: Creating... azurerm_firewall_policy.main: Creation complete after 6s azurerm_firewall.main: Creating... azurerm_firewall.main: Still creating... [1m0s elapsed] azurerm_firewall.main: Still creating... [5m0s elapsed] azurerm_firewall.main: Still creating... [10m0s elapsed] azurerm_firewall.main: Still creating... [20m0s elapsed] azurerm_firewall.main: Creation complete after 22m14s [id=.../azureFirewalls/afw-demo] Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
通信フローで動作の違いを確認する
1. NSGルールで80番ポートをブロックする
azコマンドでAppSubnetのNSGのAllow-HTTPルールをDenyに書き換えます。# Allow-HTTPルールをDenyに変更してNSGだけでブロックを確認 $ az network nsg rule update --resource-group rg-fw-nsg-demo --nsg-name nsg-app --name Allow-HTTP --access Deny # ルール確認 $ az network nsg rule list --resource-group rg-fw-nsg-demo --nsg-name nsg-app --output table Name Priority Direction Access Protocol DestinationPortRanges ---------------- -------- --------- ------ -------- --------------------- Allow-SSH 100 Inbound Allow Tcp 22 Allow-HTTP 110 Inbound Deny Tcp 80 Deny-All-Inbound 4096 Inbound Deny * *
2. Azure FirewallのアプリケーションルールでFQDNを制御する
AppSubnet上のVMからubuntu.comへのHTTPS通信を試します。# AppSubnet上のVMにSSH後に実行 # FirewallのアプリケーションルールでFQDN許可済みのURL $ curl -s -o /dev/null -w "%{http_code}" https://security.ubuntu.com 200 # FirewallのルールにないFQDNはブロック $ curl -s -o /dev/null -w "%{http_code}" https://example-blocked.com 000 # 000 = 接続そのものが確立されない(Azure FirewallのDiagnosticsログにブロック記録あり)
AzureでのLinuxサーバー構築をTerraformで体系的に学びたい方は azure.linuxmaster.jp で実践的なハンズオンコースを紹介しています。
どちらをいつ使うべきか——判断基準
1. NSGのみで十分なケース
・コスト最優先・小規模構成:単一VNetに2~3サブネット、インターネット向けアウトバウンドはAzure NATゲートウェイ経由・閉域ネットワーク:ExpressRouteでオンプレのみ接続し、インターネット向けアウトバウンドがほぼない
・PoC・検証環境:本番移行前の動作確認。NSGだけ組んでおいて後でFirewallを追加する設計でも問題なし
2. Azure Firewallが必要なケース
・FQDNで許可リストを管理したい:「*.microsoft.com だけアウトバウンドを許可」などのホワイトリスト運用・複数VNetの出口を一元管理:ハブ&スポーク構成でHub VNetにFirewallを置き、全Spoke VNetのアウトバウンドを統制
・コンプライアンス要件:全通信ログの一元取得、IDPS(Premium)、TLSインスペクション(Premium)
・インバウンドDNATが必要:外部からVNet内へのポートマッピング(シンプルな公開構成)
3. 両方を組み合わせる本番パターン
エンタープライズの標準パターンは「NSGとAzure Firewallを重ねる多層防御」です。・NSGの担当:各サブネットの東西トラフィック(VNet内のサブネット間通信)を制御
・Azure Firewallの担当:VNet外へのアウトバウンド(南北トラフィック)を集中管理
この組み合わせにより、NSGが「最初の防衛ライン」としてVNet内の不審な横移動を止め、Azure Firewallが「出口の管制塔」としてインターネット向け通信を精密に制御できます。コストが気になる場合は、まずNSGだけで運用を始め、FQDNフィルタや一元ログが必要になった時点でAzure Firewallを追加するアプローチが現実的です。
よくあるエラーと対処
エラー1:「AzureFirewallSubnet requires a minimum size of /26」
Error: creating Firewall "afw-demo": The subnet AzureFirewallSubnet requires a minimum size of /26.
address_prefixes を ["10.0.1.0/26"] 以上に変更してください。エラー2:「PublicIPAddressCannotBeDeleted」
Error: deleting Public IP "pip-afw-demo": PublicIPAddressCannotBeDeleted Public IP address pip-afw-demo is referenced by Azure Firewall afw-demo.
terraform destroy はTerraformが依存関係を解決して適切な順序で削除しますが、手動でazコマンドを使う場合は az network firewall delete を先に実行してからパブリックIPを削除してください。エラー3:Firewall PolicyのルールがNSGより優先されない
「Firewall PolicyでDenyを設定したのにNSGのAllowが通ってしまう」という混乱はよく起きます。NSGとAzure Firewallは独立したコントロールプレーンで動いており、どちらかが上位という関係ではありません。処理順はInbound方向で:NSG(Inbound評価)→ VM → NSG(Outbound評価)→ ルートテーブルでFirewallへ → Azure Firewall。NSGがInboundをAllowしてもFirewallがDenyすればVMのアウトバウンドは遮断されます。逆にNSGがInboundをDenyした時点でパケットはFirewallに届かないため、FirewallのDiagnosticsログにも記録されません。
本記事のまとめ
| やりたいこと | 使うべき機能 |
|---|---|
| サブネット・NIC単位でIP/ポートを制御 | NSG(無料) |
| FQDNでアウトバウンドをホワイトリスト管理 | azurerm_firewall_policy のApplicationルール |
| 複数VNetの出口を一元ログ管理 | Azure Firewall(ハブ&スポーク構成) |
| VNet内サブネット間の東西トラフィック制御 | NSG(単体でOK) |
| TerraformでNSGを定義する | azurerm_network_security_group |
| TerraformでAzure Firewallを定義する | azurerm_firewall + azurerm_firewall_policy |
| AppSubnetの通信をFirewallに向ける | ルートテーブルで0.0.0.0/0のnext hopをFirewallのプライベートIPに設定 |
| 不正侵入検知・TLSインスペクション | Azure Firewall Premium |
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Azureハンズオン実践コースの詳細はこちら。NSG・Azure Firewall・Terraformの組み合わせをクラウドネイティブな構築手順で体系的に学べます。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:ypad・moss・azureの違いを徹底比較|Azure CLIで確認するネットワーク選定
- この記事の属するカテゴリ:Azureへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます