Azure CLIでIPアドレスからVMを特定する話

## はじめに
本記事はAzure Advent Calendar 2019　6日目の記事です。
https://qiita.com/advent-calendar/2019/azure

Azureを長く使っているとリソースがどんどん増えて行くと思います。
リソースグループやタグを設計して管理するのが王道ですが、それでもある特定の属性を引っ張りたい、というシチュエーションだとあまり有効に機能しません。
例えば「Azureで使ってるIPアドレスから変なアクセスあったんだけど、このIPどのVMに紐付いてるんだっけ？」みたいな状況です（無いかな？私はそれなりの頻度であります）。

Azure Portalではフィルターできる属性が限られており、IPアドレスからVMを逆引きすることが難しいためAzure CLIを使いましょう。今回は手っ取り早くCloud Shellを利用します。

## Azure CLI
お馴染みの黒い画面でAzureリソースを操作するやつです。まんまですね！
コマンドリファレンスはこちら。
https://docs.microsoft.com/ja-jp/cli/azure/reference-index?view=azure-cli-latest

本記事は2019年12月時点の環境で書かれています。アップデートでコマンド変わる可能性もありますので、エラー出るなーなんてときはリファレンスをチェックしてください。

## NICの情報を取得する
AzureのVMにおいて、IPアドレスはネットワークインターフェースに紐付いていますのでまずはNICの一覧を取得してみましょう。
ネットワーク関連のコマンドは`az network`以下に集約されていますのでつらつら眺めてみると…ありました。`az network nic list`がそれっぽいです。
リファレンスでは何が出力されるかわからないので、とりあえず検証環境で投入してみます。

```
$ az network nic list
[
  {
    "dnsSettings": {
      "appliedDnsServers": [],
      "dnsServers": [],
      "internalDnsNameLabel": null,
      "internalDomainNameSuffix": null,
      "internalFqdn": null
    },
    "enableAcceleratedNetworking": false,
    "enableIpForwarding": false,
    "etag": "",
    "hostedWorkloads": [],
    "id": "/subscriptions/...",
    "ipConfigurations": [
      {
        "applicationGatewayBackendAddressPools": null,
        "applicationSecurityGroups": null,
        "etag": "",
        "id": "/subscriptions/...",
        "loadBalancerBackendAddressPools": null,
        "loadBalancerInboundNatRules": null,
        "name": "ipconfig1",
        "primary": true,
        "privateIpAddress": "10.0.0.4",
        "privateIpAddressVersion": "IPv4",
        "privateIpAllocationMethod": "Dynamic",
        "privateLinkConnectionProperties": null,
        "provisioningState": "Succeeded",
        "publicIpAddress": null,
        "resourceGroup": "Defalut-RG",
        "subnet": {
          "addressPrefix": null,
          "addressPrefixes": null,
          "delegations": null,
          "etag": null,
          "id": "/subscriptions/...",
          "ipConfigurationProfiles": null,
          "ipConfigurations": null,
          "name": null,
          "natGateway": null,
          "networkSecurityGroup": null,
          "privateEndpointNetworkPolicies": null,
          "privateEndpoints": null,
          "privateLinkServiceNetworkPolicies": null,
          "provisioningState": null,
          "purpose": null,
          "resourceGroup": "Defalut-RG",
          "resourceNavigationLinks": null,
          "routeTable": null,
          "serviceAssociationLinks": null,
          "serviceEndpointPolicies": null,
          "serviceEndpoints": null
        },
        "type": "Microsoft.Network/networkInterfaces/ipConfigurations",
        "virtualNetworkTaps": null
      }
    ],
    "location": "japaneast",
    "macAddress": "00-0D-3A-50-D9-DD",
    "name": "vm-test496",
    "networkSecurityGroup": null,
    "primary": true,
    "privateEndpoint": null,
    "provisioningState": "Succeeded",
    "resourceGroup": "Defalut-RG",
    "resourceGuid": "",
    "tags": {
      "Name": "Defalut"
    },
    "tapConfigurations": [],
    "type": "Microsoft.Network/networkInterfaces",
    "virtualMachine": {
      "id": "/subscriptions/.../VM-test",
      "resourceGroup": "Defalut-RG"
    }
  }
]
```

こんな感じで出力されます。NIC一つでこの情報量…！

とはいえ、このままだと流石に見にくいです。欲しい情報だけ抜き出したいですね。
こういうときは`--query`オプションを使いましょう。JMESPath形式で欲しい情報だけを指定できます。

```
$ az network nic list  --query '[].{name:name,privateIP:ipConfigurations[].privateIpAddress}'
[
  {
    "name": "vm-test496",
    "privateIP": [
      "10.0.0.4"
    ]
  },
  {
    "name": "vm-test497",
    "privateIP": [
      "10.0.0.5"
    ]
  }
]
```

だいぶ見やすくなりました。

いや1NIC1行で出力したいんだよ…なんてときはjsonパーサーの出番です。
Cloud SHellではデフォルトで`jq`コマンドが使えます。

```
$ az network nic list  --query '[].{name:name,privateIP:ipConfigurations[].privateIpAddress}' | jq -c .[]
{"name":"vm-test496","privateIP":["10.0.0.4"]}
{"name":"vm-test497","privateIP":["10.0.0.5"]}
```

……どうせ`jq`使うなら`--query`使わなくてもいいですよね。

```
$ az network nic list | jq -c '.[] | {(.name): .ipConfigurations[].privateIpAddress}'
{"vm-test496":"10.0.0.4"}
{"vm-test497":"10.0.0.5"}
```
おお見やすい。情報を一行にまとめてしまえば、あとは`grep`なりなんなりでいい感じに検索できそうです。


## VMとIPアドレスのペアを出す
いやNICじゃなくてVM名が知りたいんだよ……なんてとき、`az network nic list`のjson内にある`.virtualMachine[].id`を取得しても良いのですが`az vm list-ip-address`コマンドを使ったほうが簡単そうです。

```
$ az vm list-ip-addresses
[
  {
    "virtualMachine": {
      "name": "VM-test",
      "network": {
        "privateIpAddresses": [
          "10.0.0.4"
        ],
        "publicIpAddresses": []
      },
      "resourceGroup": "Defalut-RG"
    }
  }
]
```

一行に整形するなら

```
$ az vm list-ip-addresses | jq -c '.[] | {(.virtualMachine.name): .virtualMachine.network.privateIpAddresses}'
{"VM-test":["10.0.0.4"]}
```
こんな感じ。

## テーブル形式にする
ここまでjson形式で取り扱ってきましたが、カッコとかダブルクォーテーションとか、ちょっと邪魔ですよね。
別のプログラムに渡すとかであればjsonで良いのですが、人が見るのならもうちょっと見やすい形式にしたいところです。

Azure CLIでは`--output`、`-o`オプションで出力形式を変えられますのでテーブル形式にしてみましょう。
出力形式の一覧は`az (コマンド) --help`で確認できます。

```
$ az vm list-ip-addresses -o table
VirtualMachine    PrivateIPAddresses
----------------  --------------------
VM-test           10.0.0.4
```

……めっちゃ見やすいですね……　頑張ってjq使ったのは何だったのか。
NIC一覧も取得してみましょう。

```
$ az network nic list -o table
EnableAcceleratedNetworking    EnableIpForwarding    Location    MacAddress         Name        Primary    ProvisioningState    ResourceGroup    ResourceGuid
-----------------------------  --------------------  ----------  -----------------  ----------  ---------  -------------------  ---------------  ------------------------------------
False                          False                 japaneast   00-0D-3A-50-D9-DD  vm-test496  True       Succeeded            Defalut-RG       ...
False                          False                 japaneast                      vm-test497             Succeeded            Defalut-RG       ...
```
jsonのときより情報が減ってしまいました。また不要な情報も多く、横長でちょっと見にくいです。
こういうときは`--query`オプションで出力する情報を明示的に指定してやりましょう。

```
$ az network nic list --query '[].{name:name,PrivateIP:ipConfigurations[0].privateIpAddress}' -o table
Name        PrivateIP
----------  -----------
vm-test496  10.0.0.4
vm-test497  10.0.0.5
```


## まとめ
いかがでしたでしょうか（一回書いてみたかった）。
例がめちゃくちゃニッチになってしまいましたが、Azure CLIのコマンドは豊富なので困ったときはまずリファレンスを眺めてみましょう。IaaSに限らず様々なリソースを取得できます。もちろん情報取得だけでなくリソース作成/削除などの操作も行えます。