Azure Firewall NSG違いを構築で理解する|Terraformで両方セットアップ

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Azure > Azure Firewall NSG違いを構築で理解する|Terraformで両方セットアップ
「Azure FirewallとNSGの使い分けが分からない」「NSGだけで十分なのか、Azure Firewallまで入れるべきなのか」——Azureネットワーク設計を始めたエンジニアがほぼ全員ぶつかる疑問です。

両者は同じ「フィルタリング」に見えますが、動く場所もレイヤーも役割も全く違います。この記事では、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分かかる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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

実際のapply出力(サブスクリプションIDはマスク済み):

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.

Azure Firewallのデプロイには20分以上かかります。NSGは数秒で完了するので、この差だけでも「気軽に変更できる防御層」と「一度張ったら動かしにくい集中管理層」という役割の違いが実感できます。

通信フローで動作の違いを確認する

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 * *

この状態でAppSubnet上のVMの80番ポートに外部からアクセスすると、接続がタイムアウトします。Firewallのログには何も出ません。これがNSGがサブネットレベルで先に処理する証拠です——トラフィックはFirewallに届く前にブロックされています。

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ログにブロック記録あり)

NSGは「このポート番号を許可・拒否」という判断しかできません。対してAzure Firewallは「このFQDN(ドメイン名)への通信だけ許可し、それ以外は全拒否」という判断ができます。マルウェアのC2サーバーがIPを頻繁に変えてもFQDNが同じなら捕捉できる——これがAzure Firewallを使う最大の理由のひとつです。

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.

Azure Firewallの専用サブネットは/26(64アドレス)以上が必須です。/28や/27では作成できません。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
NSGとAzure Firewallは「どちらか一方」ではなく「両方を正しい場所に配置する」設計です。NSGはサブネットの基本防御として必ず使い、FQDNベースの出口制御や複数VNetの統制が必要になったらAzure Firewallを追加する、というステップで考えると整理しやすくなります。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Azureハンズオン実践コースの詳細はこちら。NSG・Azure Firewall・Terraformの組み合わせをクラウドネイティブな構築手順で体系的に学べます。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。