特定のサブネットにある AWS リソースの一覧と IP アドレスを CSV で出力してみた

VPC上でIPアドレスを持つリソースはネットワークインターフェイスを持っており、それを EC2 のサービス画面から確認できることをご存知でしょうか。並べて見ると楽しいです。

コンバンハ、千葉(幸)です。

特定のサブネット上にあるリソース一覧を確認したい、というケースがあるかと思います。存在するのが EC2 インスタンスだけであることが分かっているならその一覧を確認すればいいですが、ロードバランサーや RDS インスタンス、インターフェース型の VPC エンドポイントなど、サブネット上で IP アドレスを使用するリソースは多々あります。

サービスの種別に関係なく一覧で確認したい、という場合の手順を確認してみました。

マネジメントコンソールからネットワークインターフェイスを確認する

一番手っ取り早いのはマネジメントコンソールから確認する方法です。

VPC 上に作成され IP アドレスを持つリソースは必ずネットワークインターフェイス(ENI)が割り当てられています。ネットワークインターフェイスは EC2 のサービス画面から一覧を確認できるため、そこを参照することでリソースが確認できます。

一覧画面でフィルタリングもできるため、確認したいサブネット ID を入力することで要件を満たせます。

ENI_EC2

EC2 インスタンス用の ENI であれば「インスタンス ID」からアタッチされたインスタンスが確認できますし、それ以外のリソースが使用するものであれば「説明」から確認できます。ちょっとした確認であればこの方法が適しているでしょう。

こちらのエントリでもう少し詳細な手順が記載されていますので合わせてご参照ください。

AWS CLI でネットワークインターフェイス一覧を出力する

上記の手法の場合、対象の数が多いと確認するのが難しいこともあります。また、報告用に別の形式にエクスポートしたい、ということもあるかもしれません。

そんな時には AWS CLI を使ってみましょう。該当するコマンドは以下です。

リファレンスから出力例を引用すると以下の通りです。

{
  "NetworkInterfaces": [
      {
          "Status": "in-use",
          "MacAddress": "02:2f:8f:b0:cf:75",
          "SourceDestCheck": true,
          "VpcId": "vpc-a01106c2",
          "Description": "my network interface",
          "Association": {
              "PublicIp": "203.0.113.12",
              "AssociationId": "eipassoc-0fbb766a",
              "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com",
              "IpOwnerId": "123456789012"
          },
          "NetworkInterfaceId": "eni-e5aa89a3",
          "PrivateIpAddresses": [
              {
                  "PrivateDnsName": "ip-10-0-1-17.ec2.internal",
                  "Association": {
                      "PublicIp": "203.0.113.12",
                      "AssociationId": "eipassoc-0fbb766a",
                      "PublicDnsName": "ec2-203-0-113-12.compute-1.amazonaws.com",
                      "IpOwnerId": "123456789012"
                  },
                  "Primary": true,
                  "PrivateIpAddress": "10.0.1.17"
              }
          ],
          "RequesterManaged": false,
          "Ipv6Addresses": [],
          "PrivateDnsName": "ip-10-0-1-17.ec2.internal",
          "AvailabilityZone": "us-east-1d",
          "Attachment": {
              "Status": "attached",
              "DeviceIndex": 1,
              "AttachTime": "2013-11-30T23:36:42.000Z",
              "InstanceId": "i-1234567890abcdef0",
              "DeleteOnTermination": false,
              "AttachmentId": "eni-attach-66c4350a",
              "InstanceOwnerId": "123456789012"
          },
          "Groups": [
              {
                  "GroupName": "default",
                  "GroupId": "sg-8637d3e3"
              }
          ],
          "SubnetId": "subnet-b61f49f0",
          "OwnerId": "123456789012",
          "TagSet": [],
          "PrivateIpAddress": "10.0.1.17"
      },
      ...

ここから必要な情報のみフィルタリングし、見やすい形式で出力してみましょう。

今回使用している AWS CLI のバージョンは以下です。

% aws --version
aws-cli/2.2.39 Python/3.8.8 Darwin/20.6.0 exe/x86_64 prompt/off

手軽に AWS CLI を実行する環境がない、という場合には CloudShell を利用するのもいいと思います。

--filters の指定

aws ec2 describe-network-interfacesは数多くのフィルターに対応しています。

今回の例では、--filters Name=subnet-id,Values="$SUBNETID"という形式で特定のサブネット上の ENI のみをフィルタできます。AWS CLI を実行する前に、以下のように変数に ID を格納しておくと実行しやすいです。

% SUBNETID=subnet-07f30be336c9c53be

他にもName=addresses.private-ip-address,Values=xxxで特定の IP アドレスを持つ ENI をフィルタしたり、Name=description,Values="*ELB*"で ELB に関する ENI をフィルタする、という使い方もできます。

使用できるフィルタの種類は上記のリファレンスを確認してください。

テーブル形式で出力する

手元の環境で実行してみた例は以下です。

--queryで出力する項目を絞っています。必要に応じて、出力する項目をカスタマイズしてください。:の左側のラベルは任意のものを指定できます。

% SUBNETID=subnet-07f30be336c9c53be
% aws ec2 describe-network-interfaces\
 --filters Name=subnet-id,Values="$SUBNETID"\
 --query 'NetworkInterfaces[].{
     PrivateIpAddress:PrivateIpAddress,
     PublicIp:Association.PublicIp
     Name:TagSet[?Key==`Name`]|[0].Value,
     Description:Description,
     InstanceId:Attachment.InstanceId,
     NetworkInterfaceId:NetworkInterfaceId,
     Status:Status
 }'\
 --output table
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|                                                                           DescribeNetworkInterfaces                                                                           |
+---------------------------------------------------------------------+----------------------+-------+------------------------+-------------------+---------------+-------------+
|                             Description                             |     InstanceId       | Name  |  NetworkInterfaceId    | PrivateIpAddress  |   PublicIp    |   Status    |
+---------------------------------------------------------------------+----------------------+-------+------------------------+-------------------+---------------+-------------+
|  datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2) |  None                |  None |  eni-06cd6ac4ca2b052a4 |  10.0.0.135       |  None         |  available  |
|  EFS mount target for fs-da6e8cfa (fsmt-4f95036e)                   |  None                |  None |  eni-042cf905c95d96f5b |  10.0.0.172       |  None         |  available  |
|  datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2) |  None                |  None |  eni-079eaf85d12025bbb |  10.0.0.10        |  None         |  available  |
|  datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2) |  None                |  None |  eni-075d6b28fa00c1799 |  10.0.0.128       |  None         |  available  |
|  datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2) |  None                |  None |  eni-09d65d1eb69412d25 |  10.0.0.63        |  None         |  available  |
|  ELB app/test-alb/371a627e2432eb10                                  |  None                |  None |  eni-043c2c52fc22bf943 |  10.0.0.100       |  13.113.99.51 |  in-use     |
|  Primary network interface                                          |  i-0e9ce656e12f8ee8c |  Test |  eni-0999adbe1d47ec2d8 |  10.0.0.22        |  3.112.71.93  |  in-use     |
+---------------------------------------------------------------------+----------------------+-------+------------------------+-------------------+---------------+-------------+

列がアルファベット順になってしまっているのが残念ですが、だいぶ見やすい形式で出力できました。マネジメントコンソールのスクリーンショットを取得する、よりは使い回しが効く形式かと思います。

jq を使用して CSV 出力する

一覧情報を連携したい、という場合には単なるテキストより CSV の方が都合がよいでしょう。jq を使用することで、 CSV 形式での出力ができます。

% aws ec2 describe-network-interfaces\
 --filters Name=subnet-id,Values="$SUBNETID"\
 --query 'NetworkInterfaces[].{
     PrivateIpAddress:PrivateIpAddress,
     PublicIp:Association.PublicIp
     Name:TagSet[?Key==`Name`]|[0].Value,
     Description:Description,
     InstanceId:Attachment.InstanceId,
     NetworkInterfaceId:NetworkInterfaceId,
     Status:Status
 }'\
  --output json\
  | jq -r '
    .[] | [.PrivateIpAddress, .PublicIp, .Description, .Name,  .InstanceId, .NetworkInterfaceId, .Status] | @csv'\
  | sort -V -t "," -k 1
"10.0.0.10",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-079eaf85d12025bbb","available"
"10.0.0.22","3.112.71.93","Primary network interface","Test","i-0e9ce656e12f8ee8c","eni-0999adbe1d47ec2d8","in-use"
"10.0.0.63",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-09d65d1eb69412d25","available"
"10.0.0.100","13.113.99.51","ELB app/test-alb/371a627e2432eb10",,,"eni-043c2c52fc22bf943","in-use"
"10.0.0.128",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-075d6b28fa00c1799","available"
"10.0.0.135",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-06cd6ac4ca2b052a4","available"
"10.0.0.172",,"EFS mount target for fs-da6e8cfa (fsmt-4f95036e)",,,"eni-042cf905c95d96f5b","available"

加えて、せっかくなので IP アドレスの昇順にソートさせてみました。

プライベート IP アドレスが 1 列目にあるのならsort -Vだけで事足りますが、例えば 3 列目になるような構成であればsort -V -t "," -k 3のように指定してください。

CSV にヘッダーをつける

それぞれの列が何を表すのか、あらかじめヘッダー行を指定しておきたいこともあるでしょう。

ハイライト部のような記述を加えることで実現できます。

% aws ec2 describe-network-interfaces\
 --filters Name=subnet-id,Values="$SUBNETID"\
 --query 'NetworkInterfaces[].{
     PrivateIpAddress:PrivateIpAddress,
     PublicIp:Association.PublicIp
     Name:TagSet[?Key==`Name`]|[0].Value,
     Description:Description,
     InstanceId:Attachment.InstanceId,
     NetworkInterfaceId:NetworkInterfaceId,
     Status:Status
 }'\
  --output json\
  | jq -r '
   ["プライベートIP","パブリックIP","説明","Name","インスタンスID","インタフェースID","ステータス"],
   (.[] | [.PrivateIpAddress, .PublicIp, .Description, .Name,  .InstanceId, .NetworkInterfaceId, .Status]) | @csv'\
  | sort -V -t "," -k 1
"10.0.0.10",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-079eaf85d12025bbb","available"
"10.0.0.22","3.112.71.93","Primary network interface","Test","i-0e9ce656e12f8ee8c","eni-0999adbe1d47ec2d8","in-use"
"10.0.0.63",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-09d65d1eb69412d25","available"
"10.0.0.100","13.113.99.51","ELB app/test-alb/371a627e2432eb10",,,"eni-043c2c52fc22bf943","in-use"
"10.0.0.128",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-075d6b28fa00c1799","available"
"10.0.0.135",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-06cd6ac4ca2b052a4","available"
"10.0.0.172",,"EFS mount target for fs-da6e8cfa (fsmt-4f95036e)",,,"eni-042cf905c95d96f5b","available"
"プライベートIP","パブリックIP","説明","Name","インスタンスID","インタフェースID","ステータス"

とは言え、先ほどのソートが聞いているので一番下の行に来てしまっていますね。。

CSV のヘッダー行を除いてソートする

ヘッダー行を除き、それ以外の行でソートさせたいです。以下の形式で実現できます。

% aws ec2 describe-network-interfaces\
 --filters Name=subnet-id,Values="$SUBNETID"\
 --query 'NetworkInterfaces[].{
     PrivateIpAddress:PrivateIpAddress,
     PublicIp:Association.PublicIp
     Name:TagSet[?Key==`Name`]|[0].Value,
     Description:Description,
     InstanceId:Attachment.InstanceId,
     NetworkInterfaceId:NetworkInterfaceId,
     Status:Status
 }'\
  --output json\
  | jq -r '
   ["プライベートIP","パブリックIP","説明","Name","インスタンスID","インタフェースID","ステータス"],
   (.[] | [.PrivateIpAddress, .PublicIp, .Description, .Name,  .InstanceId, .NetworkInterfaceId, .Status]) | @csv'\
  | awk 'NR==1;NR>1{print $0 | "sort -V -t , -k 1"}'
"プライベートIP","パブリックIP","説明","Name","インスタンスID","インタフェースID","ステータス"
"10.0.0.10",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-079eaf85d12025bbb","available"
"10.0.0.22","3.112.71.93","Primary network interface","Test","i-0e9ce656e12f8ee8c","eni-0999adbe1d47ec2d8","in-use"
"10.0.0.63",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-09d65d1eb69412d25","available"
"10.0.0.100","13.113.99.51","ELB app/test-alb/371a627e2432eb10",,,"eni-043c2c52fc22bf943","in-use"
"10.0.0.128",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-075d6b28fa00c1799","available"
"10.0.0.135",,"datasync client for loc-0eadaf8daa74e75ca (task-06f9813cd23052fb2)",,,"eni-06cd6ac4ca2b052a4","available"
"10.0.0.172",,"EFS mount target for fs-da6e8cfa (fsmt-4f95036e)",,,"eni-042cf905c95d96f5b","available"

これは以下のページを参考にしました。awk はこんなこともできるんですね、、

CSV をスプレッドシートに貼り付けて確認すると、いい感じです。リソース数が多い環境では見応えがあるのではないでしょうか。

IPAddressList

終わりに

特定のサブネットでフィルタした ENI の一覧をマネジメントコンソールと AWS CLI で確認してみました。

お手軽にやるならマネジメントコンソールから、量が多かったり一覧として出力したければ AWS CLI から行いましょう。 CSV で出力したものは簡単な IP アドレス管理表のように使えていいかもしれません。頑張って Excel に手打ちしていた頃の気持ちが蘇ります。

最近 AWS CLI による一覧の CSV 出力にハマっており、いくつか関連シリーズを書いています。あわせてご参照ください。

以上、 チバユキ (@batchicchi) がお送りしました。