【Zabbix 7.0】Zabbix API でホストを登録してみた

# はじめに
こんにちは！登山大好き 7Mountains です。
今まで敬遠してきた Zabbix API に向き合う時がきてしまった・・・。
気持ちを切り替えて、検証するからには運用も見据えて Script 化していきましょう！

# 検証環境
今回の検証環境は下表の通りです。
Zabbix Server はすでに構築済み（まだの方は [こちら](https://qiita.com/7mountain/items/c9de52dc247043a6df13) の記事を参照してください）で、API の実行サーバは Zabbix Server とは別のサーバーで行っています。

|項目|値|備考|
|:-:|:-:|:-|
|OS|AlmaLinux 9.5|Zabbix Server、API を実行するサーバも同じOSで構築|
|IP アドレス|192.168.1.1|Zabbix Server の IP アドレス|
|Zabbix フロントエンドのURL|https:\//192.168.1.1/zabbix|https 有効化済み、証明書は自己証明書|
|Zabbix API の URL|https:\//192.168.1.1/zabbix/api_jsonrpc.php|API でリクエストを送信するURL|

# やってみた
## Zabbix API を使用してホストを登録するまでの流れ
Zabbix API を使用してホストを登録するには、いくつか事前準備が必要です。

今回の検証では、
1．テンプレートの作成
2．ホストグループの登録
3．ホストの登録
という順番で検証を行います

なお、テンプレートの作成については、フロントエンドから実行することが多いと思いますので、本記事では触れません

## 認証トークンの取得
さっそく聞き慣れない言葉がでてきましたが、フロントエンドだとブラウザ上でユーザ名とパスワードを入力してログインする作業を、Zabbix APIでは、JSON クエリの header に認証トークンを指定して操作を行います

### 認証トークン取得コマンド
今回はデフォルトで存在している特権管理者のユーザ Admin の認証トークンを取得します
認証トークンを取得するには、Zabbix APIの user.login メソッドを使います
Zabbix Server の IP アドレス、Admin ユーザ のパスワードは環境に合わせて変更してください
Zabbix API を実行するサーバへ root でログインして、下のコマンドを実行してください

:::note
バージョンいくつからなのか定かではないのですが、Zabbix 7.0 の API で "username":"Admin" と指定しているところが、以前は "user":"Admin" だった様です
:::

```shell-session:user.login
curl --request POST --url 'https://192.168.1.1/zabbix/api_jsonrpc.php' --header 'Content-Type: application/json-rpc' --data '{"jsonrpc":"2.0","method":"user.login","params":{"username":"Admin","password":"zabbix"},"id":1}' --insecure
```
上のコマンドを実行すると下記の結果が返ってきます
result の右隣に表示されている32文字の英数字が認証トークンです
```shell-session:Result user.login
{"jsonrpc":"2.0","result":"4fbba1999c682aa807d636266d6c6bca","id":1}
```

上のコマンドのオプション --data で指定している各パラメータを JSON ファイルに記述するとこんな感じ
```json:token_get.json
{
           "jsonrpc": "2.0",
           "method": "user.login",
           "params": {
               "username": "Admin",
               "password": "zabbix"
           },
           "id": 1
       }
```
この JSON ファイルを指定して認証トークンを取得することもできます
コマンドはこんな感じで、実行結果は先のコマンドと同じです
```shell-session:user.login
curl --request POST --url 'https://192.168.1.1/zabbix/api_jsonrpc.php' --header 'Content-Type: application/json-rpc' --data @token_get.json --insecure
```

### 認証トークン取得 Script
運用を見据えて Script 化していきましょうと冒頭でお話ししましたので、Script を作成していきます
先ほどのコマンドに gawk コマンドを付けて、実行結果から32文字の認証トークンだけを取得します

```bash:token_get.sh
# !bin/bash

# 単体テストするときは、下の行のコメントを外してね
# ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
curl --request POST --url ${ZabbixUrl} --header 'Content-Type: application/json-rpc' --data @token_get.json --insecure | gawk -F '"' '{print $8}'
```

## ホストグループの登録
続いてホストグループの登録を行います
Zabbix で監視対象ホストを登録する際に、1つ以上のホストグループを指定する必要があります
※既存のグループを指定する場合は、このステップは読み飛ばしてください

### ホストグループリスト
複数のホストグループを一度に登録できるように、ホストグループリストを作成し、そちらを読み込んでいく Script を作成します
ホストグループリストには、登録する**ホストグループ名**を記述します
```shell-session:hostgroup_create_list
Test_Linux_Servers
Test_Windows_Servers
Test_Network_Devices
```

### ホストグループ登録用 Script
ホストグループを登録するには hostgroup.create メソッドを使用します
最終行の token_remove.sh は認証トークンを破棄する Script です
Script の内容等は後述します

```bash:hostgroup_create.sh
# !bin/bash

export ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
token=`/bin/bash /root/token_get.sh`
export auth="Authorization: Bearer ${token}"

PRE_IFS=$IFS
IFS=$'\n'

for line in `cat "$1" | grep -v ^#`
do
        HostGroupName=`echo ${line} | cut -d ';' -f 1`

        data() {
                cat << EOS
{
           "jsonrpc": "2.0",
           "method": "hostgroup.create",
           "params": {
               "name": "${HostGroupName}"
           },
           "id": 2
       }
EOS
}
curl --request POST --url "${ZabbixUrl}" --header "${auth}" --header 'Content-Type: application/json-rpc' --data "$(data)" --insecure

done

IFS=$PRE_IFS

`/bin/bash /root/token_remove.sh`
```
:::note
**TIPS**
変数の先頭に **export** を付けている変数は、呼び出した Script でも使用できるように環境変数としてエクスポートしています
また、Script で 変数に JSON を埋め込むために、ヒアドキュメントを使用しています（14行目（data() {）～25行目（}）の部分）
比較的長いJSONを書いても苦にならないのですが、変数の渡し方に苦労した(^_^;)
:::

## ホストの登録
こちらもホストグループと同様1度に複数のホストを登録できる様にホストリストを作成し、そちらを読み込んでいく Script を作成します

### ホストリスト
設定する項目が多いので、1行目にヘッダーを付けています
各項目の区切り文字はセミコロン （;） にしました
カンマやスペースといった区切り文字はIPアドレスやホスト名などに含まれる可能性があるためです

```shell-session:host_create_list
HostGroupName;HostName;IFType;IFDefaultOrNot;UseIP;IPAddress;DNSName;Port;TemplateName
Test_Linux_Servers;almalinux003;1;1;1;192.168.1.180;;10050;Template-icmpping
Test_Linux_Servers;almalinux004;1;1;1;192.168.1.181;;10050;Template-icmpping
Test_Linux_Servers;almalinux005;1;1;1;192.168.1.182;;10050;Template-icmpping
```

ホストリストは下の表の通りに記述しています（詳細は Zabbix の[公式ドキュメント](https://www.zabbix.com/documentation/7.0/en/manual/api/reference/hostinterface/object)を参照してください）
|ヘッダー名|フロントエンドの項目名|説明|
|:-:|:-:|:-|
|HostGroupName|ホストグループ|監視対象ホストを所属させるホストグループ名を記載する|
|HostName|ホスト名|監視対象ホストの名前を記載する|
|IFType|インターフェース|1:Zabbixエージェント、2:SNMP、3:IPMI、4:JMX|
|IFDefaultOrNot|標準|0:使用しない、1:使用する|
|UseIP|接続方法|0:DNS、1:IP|
|IPAddress|IPアドレス|接続方法で 1 を指定した場合、ここに IP アドレスを記述する|
|DNSName|DNS名|接続方法で 0 を指定した場合、ここにDNS名を記述する<br>今回は使用しないのでなにも設定しない|
|Port|ポート|Zabbixエージェントで監視する場合は 10050<br>SNMPの場合は 161|
|TemplateName|テンプレート|割り当てるテンプレート名を記述する|

### ホストグループ名から ホストグループ ID に変換する Script
ホストの登録にはホストグループの指定が必須とお話ししましたが、Zabbix API ではホストグループ名ではなく、ホストグループ ID を指定します
そのため、ホストグループ名からホストグループ ID を調べる Script を作成します
ホストグループを取得するには hostgroup.get メソッドを使用します

```bash:hostgroup_name2id.sh
# !bin/bash

# 単体テストするときは、下の3行のコメントを外してね
# export ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
# token=`/bin/bash /root/token_get.sh`
# export auth="Authorization: Bearer ${token}"

HGName=${1}

PRE_IFS=$IFS
IFS=$'\n'

data() {
cat << EOS
{
        "jsonrpc": "2.0",
        "method": "hostgroup.get",
        "params": {
                "output": "extend",
                "filter": {
                        "name": "${HGName}"
                        }
                },
                "id": 2
        }
EOS
}

curl --request POST --url "${ZabbixUrl}" --header "${auth}" --header 'Content-Type: application/json-rpc' --data "$(data)" --insecure | gawk -F '"' '{print $10}'

# 単体テストするときは、下の行のコメントを外してね
# `/bin/bash /root/token_remove.sh`
```

### Template 名から Template ID に変換する Script
ホストグループ同様に、テンプレートも ID で指定する必要があるため、こちらも変換用 Script を作成します
テンプレートを取得するには template.get メソッドを使用します

```bash:template_name2id.sh
# !bin/bash

# 単体テストするときは、下の3行のコメントを外してね
# export ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
# token=`/bin/bash /root/token_get.sh`
# export auth="Authorization: Bearer ${token}"

TLName=${1}

PRE_IFS=$IFS
IFS=$'\n'

data() {
cat << EOS
{
           "jsonrpc": "2.0",
           "method": "template.get",
           "params": {
               "output": "extend",
               "filter": {
                   "host": [
                        "${TLName}"
                        ]
               }
           },
           "id": 2
       }
EOS
}

curl --request POST --url "${ZabbixUrl}" --header "${auth}" --header 'Content-Type: application/json-rpc' --data "$(data)" --insecure | gawk -F '"' '{print $62}'

# 単体テストするときは、下の行のコメントを外してね
# `/bin/bash /root/token_remove.sh`
```

### ホスト登録
ようやくホストの登録が行えますね
ホストを登録するには host.create メソッドを使用します
ホストリストで指定したインターフェース、ホストグループ、タグ、テンプレートを指定して登録します

```bash:host_create.sh
# !bin/bash
export ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
token=`/bin/bash /root/token_get.sh`
export auth="Authorization: Bearer ${token}"

PRE_IFS=$IFS
IFS=$'\n'

for line in `cat "$1" | tail -n +2 | grep -v ^#`
do
        HostGroupName=`echo ${line} | cut -d ';' -f 1`
        HostName=`echo ${line} | cut -d ';' -f 2`
        IFType=`echo ${line} | cut -d ';' -f 3`
        IFDefaultOrNot=`echo ${line} | cut -d ';' -f 4`
        UseIP=`echo ${line} | cut -d ';' -f 5`
        IPAddress=`echo ${line} | cut -d ';' -f 6`
        DNSName=`echo ${line} | cut -d ';' -f 7`
        Port=`echo ${line} | cut -d ';' -f 8`
        TemplateName=`echo ${line} | cut -d ';' -f 9`
        HGID=`/bin/bash /root/hostgroup_name2id.sh "${HostGroupName}"`
        TLID=`/bin/bash /root/template_name2id.sh "${TemplateName}"`

        data() {
                cat << EOS

{
           "jsonrpc": "2.0",
           "method": "host.create",
           "params": {
               "host": "${HostName}",
               "interfaces": [
                        {
                        "type": ${IFType},
                        "main": ${IFDefaultOrNot},
                        "useip": ${UseIP},
                        "ip": "${IPAddress}",
                        "dns": "${DNSName}",
                        "port": ${Port}
                        }
                ],
                "groups": [
                        {
                        "groupid": ${HGID}
                        }
                ],
                "tags": [
                        {
                        "tag": "host-name",
                        "value": "${HostName}"
                        }
                ],
                "templates": [
                        {
                        "templateid": ${TLID}
                        }
                ]
           },
           "id": 2
       }
EOS
}

curl --request POST --url "${ZabbixUrl}" --header "${auth}" --header 'Content-Type: application/json-rpc' --data "$(data)" --insecure
done

IFS=$PRE_IFS

`/bin/bash /root/token_remove.sh`
```

## 認証トークンの破棄
今までの Script の一番最後に実行している認証 Token を破棄する Script です
一定期間残ってしまうので、セキュリティの観点からも毎度破棄することをオススメします
使用するのは user.logout メソッドです

```bash:token_remove.sh
# !bin/bash

curl --request POST --url "${ZabbixUrl}" --header "${auth}" --data @token_remove.json --insecure
```
オプション --data で指定している token_remove.json はこちら
```json:token_remove.json
{
           "jsonrpc": "2.0",
           "method": "user.logout",
           "params": [],
           "id": 1
       }
```

# Script の微調整
ホストを登録する際に実行する Script は **host_create.sh** のみとなります
token_get.sh、hostgroup_name2id.sh、template_name2id.sh を単体で実行する場合は、最初の方で変数定義している行と、認証 Token を破棄する Script を実行している行のコメントを外してください
```bash:コメントを外す行
export ZabbixUrl='https://192.168.1.1/zabbix/api_jsonrpc.php'
token=`/bin/bash /root/token_get.sh`
export auth="Authorization: Bearer ${token}"
～～～～～～～～～～省略～～～～～～～～～～
`/bin/bash /root/token_remove.sh`
```

# さいごに
認証トークンを取得する、破棄する Script で使用したコマンドには JSON ファイルを指定したので、ホスト登録も同じ形式にしたかったのですが、変数の渡し方がわかりませんでした。
そもそも渡せないのかもしれないなぁと思い、Script の中でヒアドキュメントを使用して変数に格納しました。
ディスカバリ機能もありますし、Zabbix API を利用する機会は少なくなっていくのかもしれませんね。
それでも、Zabbix Server のバージョンアップ作業では有効活用できますし、今回検証して記事にもできてよかったです！