0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Regional NAT Gateway に private を指定したら public ができた

0
Last updated at Posted at 2026-09-02

こういう場面で読んでもらえたら、と思って書きました。

  • 既存の NAT Gateway を regional に変えようとしていて、Terraform のコードをどう書き換えるか調べている
  • 閉域環境で NAT Gateway を使っていて、regional にできるのか確かめたい
  • チームの誰かが出した availability_mode = "regional" を足すだけの PR を、レビューしている

2025年11月に追加された機能で、AZ ごとに NAT を作らなくてよくなります。便利です。ただ 移行しようとして最初に踏む罠が1つあります。

公式が「非対応」と書いているものが、コードから作るとエラーにならず、別のものができます。 マネジメントコンソールは止めてくれるのに、API と Terraform は止めてくれません。閉域が要件なら、ここだけは読んでいってください。

この記事で扱うのは、作る前に知っておきたい罠。展開に何分かかるか、切り替えで何秒止まるかといった実測値は Regional NAT Gateway の展開・縮小・切り替えを実測した にまとめました。

Regional NAT Gateway とは

従来の NAT Gateway (以下 zonal) は AZ ごとに作り、private サブネットのルートを AZ ごとに向ける必要がありました。Regional はこれを1つの ID にまとめ、ワークロードのいる AZ へ自動で広がります

公式: Regional NAT gateways for automatic multi-AZ expansion

主な違いは4点です。

zonal regional
public サブネット 必要(NAT の置き場) 不要
ルート AZ ごとに別の NAT を指す 全 AZ が同じ ID を指せる
IP 上限 8 32 / AZ
private NAT 対応 非対応(と公式には書いてある)

まずコンソールの作成画面を見る

Terraform の話に入る前に、画面がどうなっているかを見ておきます。ここが後半の伏線。

VPC コンソールの「NAT ゲートウェイを作成」を開くと、こうなっていました。

項目 見えたもの
アベイラビリティーモード 「リージョナル」と「ゾーナル」の2択。リージョナルが初期選択で、新規 バッジ付き
サブネットの指定 無い。代わりに VPC を選ぶ欄になっている
接続タイプ 「パブリック」が選択済み。プライベートはグレーアウトして選べない
Elastic IP の割り当て 「自動」と「マニュアル」の2択。自動が初期選択

2点、目を引きます。

1つめ。リージョナルが初期選択。 何も考えずに作れば regional になります。zonal を使いたいなら明示的に選び直す必要がある。

2つめ。プライベートが選べない。 公式の「Regional は private NAT 非対応」が、画面上でちゃんと効いています。警告文は出ません。ただ選べないだけ。

この「選べない」が後半で効いてきます。 コンソールで止められるものが、コードでは止まらないからです(3節)。

検証環境

項目 内容
リージョン ap-northeast-1
Terraform 1.9.4
AWS プロバイダ 6.62.0
aws-cli 2.35.6

プロバイダは 6.24.0 以降が必要です。availability_modePR #45240 で追加され、milestone は v6.24.0 (2025-12-02) でした。これ未満だと Unsupported argument になります。

1. まず書き方が3箇所変わる

zonal から regional にすると、変える必要があるのは availability_mode だけではありません。

# zonal (public。private NAT なら allocation_id は不要)
resource "aws_nat_gateway" "main" {
  subnet_id     = aws_subnet.public.id       # ← 必須
  allocation_id = aws_eip.nat.id             # ← public の場合必須
}
# regional
resource "aws_nat_gateway" "main" {
  availability_mode = "regional"
  vpc_id            = aws_vpc.this.id        # ← 必須になる
  # subnet_id     は設定不可
  # allocation_id は設定不可(必要なら availability_zone_address ブロックを使う)
}

上のコードだけでは足りません。regional NAT が available になるには、VPC に IGW が接続されている必要があります(3節で詳しく触れます)。無いと Gateway.NotAttachedfailed になります。

自分で IP を指定したい場合(manual モード)はこう書きます。

resource "aws_nat_gateway" "main" {
  availability_mode = "regional"
  vpc_id            = aws_vpc.this.id

  availability_zone_address {
    availability_zone = "ap-northeast-1a"
    allocation_ids    = [aws_eip.a.id]   # 複数形。単数の allocation_id ではない
  }
}

availability_zone_address を書くか書かないかでモードが変わります。書かなければ auto(AWS が IP と AZ 展開を管理、公式推奨)、書けば manual です。この切り替えもリソースの再作成になります。

2. 引数を間違えたとき、どこで止まるか

「3箇所変える」ということは、直し忘れが起きます。そこで7パターンをわざと間違えて validateplan に通しました。

間違い validate plan
regional + subnet_id を消し忘れ 通過 通過
regional + allocation_id を消し忘れ 通過 通過
regional で vpc_id を書き忘れ 通過 通過
regional + connectivity_type = "private" 通過 通過
zonal に戻して subnet_id を書き忘れ 通過 通過
allocation_ids を単数形で書いた NG 到達しない
AZ をどちらも書かなかった 通過 NG

7件中5件が plan を素通り。 プロバイダのドキュメントには「subnet_id は regional では設定してはいけない」と明記されているのに、クライアント側の検証が入っていません。

実際の plan 出力です。availability_mode = "regional"subnet_id が両立したまま、作成計画が出ます。

  # aws_nat_gateway.t will be created
  + resource "aws_nat_gateway" "t" {
      + availability_mode = "regional"
      + subnet_id         = "subnet-..."      ← ドキュメントは「設定不可」
      + vpc_id            = "vpc-..."
    }

Plan: 1 to add, 0 to change, 0 to destroy.

3. では apply すると何が起きるか

Terraform を介さず、AWS API に直接投げて確かめました。

aws ec2 create-nat-gateway --vpc-id vpc-xxx --availability-mode regional --subnet-id subnet-xxx
An error occurred (MissingParameter) when calling the CreateNatGateway operation:
SubnetId is not supported for a NAT gateway with availability mode regional.

vpc_id を書き忘れた場合も同様にエラーです。

VpcId is required for a NAT gateway with availability mode regional.

ここまでは想定どおり。 ところが残りの2件は違いました。

間違い API の応答
regional + subnet_id エラー
regional で vpc_id 無し エラー
regional + allocation_id 成功する
regional + connectivity_type=private 成功する

private を指定すると、警告なく public が作られる

--connectivity-type private を付けて regional NAT を作成し、できたものを確認しました。

aws ec2 create-nat-gateway --vpc-id vpc-xxx --availability-mode regional --connectivity-type private
# → 成功する

aws ec2 describe-nat-gateways --nat-gateway-ids nat-xxx
{ "AvailabilityMode": "regional", "ConnectivityType": "public" }

要求した private は無視され、public の NAT が、エラーも警告もなく作られました。

これは正直ぞっとしました。閉域前提の現場でこれをやったら、と考えると背筋が寒い。

公式ドキュメントには、はっきりこう書かれています。

Regional NAT gateways do not support private NAT. If you need private NAT, use zonal NAT gateways instead.

非対応と書いてありますが、実装はエラーにしません。できあがるのは public です。

ただし、マネジメントコンソールは止めてくれる

ここが分かれ目でした。同じことをコンソールの作成画面でやろうとすると、できません。

可用性モードで「リージョナル」を選ぶと、接続タイプの「プライベート」がグレーアウトして選べなくなります。 「パブリック」しか残りません。

つまり、経路によって扱いが違います。

どこから作るか private + regional
マネジメントコンソール 選べない(グレーアウト)
AWS API を直接 通る。public ができる
Terraform plan を通過する(apply そのものは未確認)

ガードレールはコンソールにしかありません。

これは実務でよくある落ち方だと思います。手作業のうちは画面が守ってくれていたのに、IaC に移した瞬間にその守りが外れる。 しかも外れたことに気づく機会がない。コンソールで作れなかった構成が、コードでは通ってしまうからです。

なお、コンソール側には警告文も注意書きも出ません。「グレーアウトしている」以上の説明はないので、なぜ選べないのかを知らないままの人は、コードで書けば通ると思ってしまいます。

そしてできあがるのは public NAT です(実際にインターネットへ出られるかは IGW とルートの有無次第。次項)。

connectivity_typepublic に化けていることは、create-nat-gateway のレスポンス自体に載っています。describe-nat-gateways を待つ必要すらありません。それでも plan の時点では気づけないのが問題です。regional に変える PR をレビューするときは、connectivity_type が明示されているか、作成後の実物が public になっていないかを必ず確認してください。

補足: allocation_id を渡した場合も同様に無視されたとみられます(作成された NAT に割当が反映されず、渡した EIP はそのまま release できました)。

IGW が無いと failed で止まる

この挙動は、全出力を保存したうえで2回実行し、どちらも同じ結果でした。create-nat-gateway のレスポンス自体が、送信直後から "ConnectivityType": "public" を返します。

2回目で分かったこともあります。IGW を接続していない VPC で試すと、Gateway.NotAttached (Network vpc-xxx has no Internet gateway attached) で failed になりました。 private NAT は本来 IGW を必要としません。IGW が無いと失敗するという事実自体が、「要求が public として扱われている」ことの傍証です。IGW を接続して再実行すると available まで進み、実際に public IP (AllocationId / PublicIp 付き) が払い出されました。

つまり 閉域要件の環境でこれをやると、IGW とルートが揃っていれば、そのまま外に出られる public NAT ができます。 IGW が無ければ failed で止まりますが、それは「private だから守られた」のではなく「public として作ろうとして IGW が無くて失敗した」だけです。

4. 書き換えは「置換」になる。そして3節の罠と重なる

既存の zonal NAT のコードで availability_moderegional に変えると、plan はこう出ます。

# aws_nat_gateway.main must be replaced
-/+ resource "aws_nat_gateway" "main" {
      ~ availability_mode = "zonal" -> "regional" # forces replacement

プロバイダのソースを見ると ForceNew: true です。

"availability_mode": {
    Type:     schema.TypeString,
    ForceNew: true,          // internal/service/ec2/vpc_nat_gateway.go
},

そして、そもそも NAT Gateway に modify 系の API がありません。

$ aws ec2 help | grep nat-gateway
  create-nat-gateway
  delete-nat-gateway
  describe-nat-gateways
  associate-nat-gateway-address
  disassociate-nat-gateway-address
  assign-private-nat-gateway-address
  unassign-private-nat-gateway-address

modify-nat-gateway が存在しません。 in-place の変換は原理的に不可能で、必ず作り直しになる。

ここが2節・3節と繋がる

Terraform の ForceNew は既定で削除してから作成します。そして3節のとおり、引数を直し忘れた plan は通ってしまいます。

この2つが重なると、こうなります。

  1. plan が通る(subnet_id を消し忘れていても)
  2. apply旧 NAT を削除する
  3. 新 NAT の作成が AWS API に拒否される
  4. NAT が無い状態で止まる

だから、公式の変換手順は「作ってからルートを切り替える」形になっています。

  1. Create a new regional NAT gateway
  2. Update route tables to point to the regional NAT gateway
  3. Delete the old zonal NAT gateways

引数を間違えなかった場合でも、削除してから作成する以上、経路が無い時間は必ず生まれる。 実際に何秒止まるのかも測りました。ルートを張り替える公式手順と比べると、はっきり差が出ます。数字は Regional NAT Gateway の展開・縮小・切り替えを実測した の5節に書いています。

availability_mode を1行書き換えるだけの PR は、公式手順とは別物です。 レビューで見つけたら止めてください。

5. Terraform プロバイダ側の未解決 issue

ここは AWS ではなく Terraform AWS Provider の問題です。2026-09-02 時点でどちらも open のままでした。

issue 内容
#46242 zonal しか使っていなくても踏みます。プロバイダを 6.24 以降に上げると regional_nat_gateway_address の差分が消えません。回避は lifecycle { ignore_changes } のみ
#45828 regional の availability_zone_address を減らすとき、NAT の更新完了を待たずに EIP を削除して失敗します。報告者によれば zonal では起きません(未修正 issue の報告ベースで、こちらでは再現していません)

補足: 今回 v6.62.0 で新規作成したリソースでは #46242 の差分は出ませんでした。既存リソースをプロバイダ更新後に扱う場合の問題のようです。

この記事で言いたかったこと

private を指定したら API と Terraform では警告なく public ができる。コンソールは選ばせない
引数の間違い 7件中5件が plan を通過
availability_mode の変更 必ず置換(ForceNew、modify API 自体が無い)

一番伝えたいのは1行目。公式が「非対応」と書いているものが、エラーにならずに別のものを作ります。

正直に言うと、この検証を始めたときの狙いは展開時間と切り替えのダウンタイムでした。private の件はついでに叩いたコマンドから出てきたもので、しかもそのとき自分は「エラーになるはず」と思い込んでいて、NAT を2本余計に作っています。 想定どおりに動くと思っていたところが一番危なかった、という話です。

閉域が要件の環境で regional を検討するなら、ここだけは押さえておきたいところ。

続き

展開に何分かかるのか、切り替えで何秒止まるのか、消し忘れをどう防ぐかは Regional NAT Gateway の展開・縮小・切り替えを実測した に書きました。本記事4節で触れた「置換すると何秒止まるのか」も、そちらで数字を出しています。

参考


本記事の内容は 2026-09-01 から 09-02 に ap-northeast-1 で実際に試したものです。掲載した ID や IP は置き換えています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?