- はじめに
- ■ 1. 共通 .tf ファイルの準備
- ■ 2. NTP サーバー設定0台から3台へ (POST)
- ■ 3. NTP サーバー設定3台から2台 (POSTのまま、エラー)
- ■ 4. NTP サーバー設定0台から3台へ (PUT)
- ■ 5. NTP サーバー設定3台から4台へ (PUT)
- ■ 6. NTP サーバー設定4台から1台へ (PUT)
- まとめ・所感
はじめに
Terraform の Cisco IOS XE 向けの Provider がリリースされたことを、先日知りました。
紹介ブログ https://blogs.cisco.com/developer/terraformiosxe01
内部的には CLI を実行するわけではく、RESTCONF ベースののようです。
Terraform はまだ慣れていないのですが、興味があったのでためしてみました。
- 環境
- Terraform v1.1.5
- IOS-XE 16.9.3 (Cisco DevNet Sandbox の IOS XE on CSR Recommended Code AlwaysOn)
- 以下関連コマンド投入済み)
ip http secure-serverestconf
- ドキュメント上は Cisco IOS XE 17.7 以降でサポートと記載あり
- 以下関連コマンド投入済み)
■ 1. 共通 .tf ファイルの準備
1.1. terraform.tf の作成
利用するプロバイダーの指定です。Registry 上の iosxe providerの USE PROVIDER をクリックして表示されたものを参考しました。
terraform { required_providers { iosxe = { source = "CiscoDevNet/iosxe" version = "0.1.1" } } }
1.2. provider.tf の作成
iosxe プロバイダーの各種設定です。
GitHub リポジトリ上のサンプルを参考しました。
provider "iosxe" { host = "https://ネットワーク機器のアドレス" device_username = "dummy_user" device_password = "dummy_password" insecure = true }
各種パラメーターの意味はこちらに記載があります。
接続先、ユーザー名、パスワードは、それぞれ以下の環境変数でも良いようです。
HOST_IOSXEDEVICE_PASSWORD_IOSXEDEVICE_USERNAME_IOSXE
insecure は、SSL/TLS 証明書の検証をしない指定です。デフォルトでも true です。
■ 2. NTP サーバー設定0台から3台へ (POST)
ここから、実際に実現したい処理の定義です。今回は、Terraform でネットワーク機器に参照先 NTP サーバーの設定(コマンドでいう ntp server)をしてみます。
ネットワーク機器側は NTP サーバーの設定がない状態から始めます。
csrv1000##sh run | inc ntp csrv1000#
この状態から 3台分追加する処理を試します。
2.1. ntp_post.tf の作成
Getting Startedによると、acl、bgp、ntp、ospf、vlan などさまざまな設定を扱えるようです。[こちらにサンプルがたくさん]8https://github.com/CiscoDevNet/terraform-provider-iosxe/tree/main/examples/examples_tf)あります。どのサンプルを見ても、リソースは iosxe_rest なので、RESTCONF で設定できるものは設定できると思ってもいいのかもしれません。
今回はお試しということで、シンプルに試せそうという点で ntp にしました。
以下のファイルは、NTP サーバー 10.0.0.1、10.0.0.2、10.0.0.3 を追加するものです。
resource "iosxe_rest" "ntp_post" { method = "POST" path = "/data/Cisco-IOS-XE-native:native/ntp/server" payload = jsonencode( { "Cisco-IOS-XE-ntp:server-list" : [ { "ip-address" : "10.0.0.1", }, { "ip-address" : "10.0.0.2", }, { "ip-address" : "10.0.0.3", } ] } }
| パラメーター名 | 説明 |
|---|---|
method |
POST、PUT、PATCH、DELETE` などのような RESTCONF のメソッドを指定 |
path |
設定するためのエンドポイントを指定 |
payload |
設定刷るための body を指定 |
ということで、ほとんど RESTCONF で叩くとき同じ感覚ですね。
2.2. terraform init の実行
ここまで以下のファイルを作りました。
ntp_post.tf provider.tf terraform.tf
同じディレクトリで、terraform init を実行します
% terraform init Initializing the backend... Initializing provider plugins... - Finding ciscodevnet/iosxe versions matching "0.1.1"... - Installing ciscodevnet/iosxe v0.1.1... - Installed ciscodevnet/iosxe v0.1.1 (self-signed, key ID F8EC53CAB70CD366) ...(略)...
2.3. terraform plan の実行
続いて terraform plan を実行します。3台分追加しますよ、とのことです。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# iosxe_rest.ntp_post will be created
+ resource "iosxe_rest" "ntp_post" {
+ id = (known after apply)
+ method = "POST"
+ path = "/data/Cisco-IOS-XE-native:native/ntp/server"
+ payload = jsonencode(
{
+ Cisco-IOS-XE-ntp:server-list = [
+ {
+ ip-address = "10.0.0.1"
},
+ {
+ ip-address = "10.0.0.2"
},
+ {
+ ip-address = "10.0.0.3"
},
]
}
)
+ response = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
2.4. terraform apply の実行
いよいよ terraform apply です。
% terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# iosxe_rest.ntp_post will be created
+ resource "iosxe_rest" "ntp_post" {
+ id = (known after apply)
+ method = "POST"
+ path = "/data/Cisco-IOS-XE-native:native/ntp/server"
+ payload = jsonencode(
{
+ Cisco-IOS-XE-ntp:server-list = [
+ {
+ ip-address = "10.0.0.1"
},
+ {
+ ip-address = "10.0.0.2"
},
+ {
+ ip-address = "10.0.0.3"
},
]
}
)
+ response = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes # yes を入力
iosxe_rest.ntp_post: Creating...
iosxe_rest.ntp_post: Creation complete after 1s [id=4155581422]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
ネットワーク機器側を確認すると無事に3台分が追加されていることが確認できました。
csrv1000(config)#do sh run | inc ntp ntp server 10.0.0.1 ntp server 10.0.0.2 ntp server 10.0.0.3
2.5. terraform apply の「再」実行
さて、この状態からもう一度 terraform apply を実行するとどうなるのだろうと思いました。
普通に RESTCONF で考えると、すでに設定が入ってる状態に 再度 POST すると 409 Conflict あたりになるかと思います。
ということで試しました。
% terraform plan iosxe_rest.ntp_post: Refreshing state... [id=4155581422] No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
No changes. なので、特に何もしない結果になりました。
これは、Terraform 側の処理として手元に保管されている state ファイルと比較した結果、一致しているのでリクエストすら出さなかった、という状態だと思います。このあたりは、Terraform らしさでしょうか。
■ 3. NTP サーバー設定3台から2台 (POSTのまま、エラー)
続いて、エラーになるかなと思いつつも、tf ファイルで4台目を追加して POST のまま実行にする、というのを試しました。
3.1. ntp_post.tf の修正
ntp_post.tf を以下ように、4台目の分を追記します。
resource "iosxe_rest" "ntp_post" { method = "POST" path = "/data/Cisco-IOS-XE-native:native/ntp/server" payload = jsonencode( { "Cisco-IOS-XE-ntp:server-list" : [ { "ip-address" : "10.0.0.1" }, { "ip-address" : "10.0.0.2" }, { "ip-address" : "10.0.0.3" }, { "ip-address" : "10.0.0.4" # 4台目追加 } ] } ) }
3.2. terraform plan の実行
terraform plan を実行します。
これだけ見ると 4台目だけいい感じに追加されるようにみえますが・・・。
% terraform plan
iosxe_rest.ntp_post: Refreshing state... [id=4155581422]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_post will be updated in-place
~ resource "iosxe_rest" "ntp_post" {
id = "4155581422"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-ntp:server-list = [
# (2 unchanged elements hidden)
{
ip-address = "10.0.0.3"
},
+ {
+ ip-address = "10.0.0.4"
},
]
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
3.3.terraform apply の実行
実際に terraform apply を実行するとエラーになります。
% terraform apply
iosxe_rest.ntp_post: Refreshing state... [id=4155581422]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_post will be updated in-place
~ resource "iosxe_rest" "ntp_post" {
id = "4155581422"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-ntp:server-list = [
# (2 unchanged elements hidden)
{
ip-address = "10.0.0.3"
},
+ {
+ ip-address = "10.0.0.4"
},
]
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
iosxe_rest.ntp_post: Modifying... [id=4155581422]
╷
│ Error: failed: status code: 409 - error: {
│ "errors": {
│ "error": [
│ {
│ "error-message": "object already exists: /ios:native/ios:ntp/ios-ntp:server/ios-ntp:server-list[ios-ntp:ip-address='10.0.0.1']",
│ "error-path": "/Cisco-IOS-XE-native:native/ntp/Cisco-IOS-XE-ntp:server",
│ "error-tag": "data-exists",
│ "error-type": "application"
│ }
│ ]
│ }
│ }
│
│
│ with iosxe_rest.ntp_post,
│ on ntp_post.tf line 1, in resource "iosxe_rest" "ntp_post":
│ 1: resource "iosxe_rest" "ntp_post" {
│
╵
Terraform 的に、差分ありと判断してリクエストを出した結果、すでにある設定を POST してるので、エラーになったということだと思います。ステータスコードは 409、また "error-tag": "data-exists" とあります。
3.4. 仕切り直し
さて、こうなると少々厄介です。 state ファイルには 4台目がある状態で、ネットワーク機器側3台分のままです。
今回は挙動をいろいろ確認するためなので、だいぶ乱暴ですがいったん state ファイル類を削除します(通常は推奨されることではないと思います)。
rm -fr .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
ネットワーク機器側も NTP サーバーの設定を削除します。
csrv1000(config)#no ntp server 10.0.0.1 csrv1000(config)#no ntp server 10.0.0.2 csrv1000(config)#no ntp server 10.0.0.3 csrv1000(config)#do sh run | inc ntp csrv1000(config)#
■ 4. NTP サーバー設定0台から3台へ (PUT)
Terraform 側もネットワーク機器側も状態をもとに戻したところで、仕切り直しです。
4.1. ntp_put.tf の作成
ntp_post.tf は削除して、別途 ntp_put.tf を作成します。ntp_post.tf と比較すると、method を "POST" にしている他、path や payload も微妙に変更しています。
resource "iosxe_rest" "ntp_put" { method = "PUT" path = "/data/Cisco-IOS-XE-native:native/ntp" payload = jsonencode( { "Cisco-IOS-XE-native:ntp" : { "Cisco-IOS-XE-ntp:server" : { "server-list" : [ { "ip-address" : "10.0.0.1" }, { "ip-address" : "10.0.0.2" }, { "ip-address" : "10.0.0.3" } ] } } } ) }
4.2. terraform init の実行
先程、自動生成されたファイルを削除したので、terraform init を実行します。
4.3. terraform plan の実行
PUT 版で terraform plan を実行します。
3台分追加するよ、と示される点は POST のときとだいたい同じように見えます。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be created
+ resource "iosxe_rest" "ntp_put" {
+ id = (known after apply)
+ method = "PUT"
+ path = "/data/Cisco-IOS-XE-native:native/ntp"
+ payload = jsonencode(
{
+ Cisco-IOS-XE-native:ntp = {
+ Cisco-IOS-XE-ntp:server = {
+ server-list = [
+ {
+ ip-address = "10.0.0.1"
},
+ {
+ ip-address = "10.0.0.2"
},
+ {
+ ip-address = "10.0.0.3"
},
]
}
}
}
)
+ response = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
4.4 terraform apply の実行
terraform apply を実行します。
% terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be created
+ resource "iosxe_rest" "ntp_put" {
+ id = (known after apply)
+ method = "PUT"
+ path = "/data/Cisco-IOS-XE-native:native/ntp"
+ payload = jsonencode(
{
+ Cisco-IOS-XE-native:ntp = {
+ Cisco-IOS-XE-ntp:server = {
+ server-list = [
+ {
+ ip-address = "10.0.0.1"
},
+ {
+ ip-address = "10.0.0.2"
},
+ {
+ ip-address = "10.0.0.3"
},
]
}
}
}
)
+ response = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
iosxe_rest.ntp_put: Creating...
iosxe_rest.ntp_put: Creation complete after 1s [id=4117814902]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
ネットワーク機器側の設定を確認すると無事に設定が入っていました。
csrv1000(config)#do sh run | inc ntp ntp server 10.0.0.1 ntp server 10.0.0.2 ntp server 10.0.0.3
■ 5. NTP サーバー設定3台から4台へ (PUT)
3台分設定されている状態から、PUTで4台分にします。
5.1. ntp_put.tf の修正
PUT で 3台追加したところで、今度は先程使った ntp_put.tf を編集して、4台の設定にします。
resource "iosxe_rest" "ntp_put" { method = "PUT" path = "/data/Cisco-IOS-XE-native:native/ntp" payload = jsonencode( { "Cisco-IOS-XE-native:ntp" : { "Cisco-IOS-XE-ntp:server" : { "server-list" : [ { "ip-address" : "10.0.0.1" }, { "ip-address" : "10.0.0.2" }, { "ip-address" : "10.0.0.3" }, { "ip-address" : "10.0.0.4" # 4台目 } ] } } } ) }
5.2. terraform plan の実行
terraform plan を実行します。4台目 10.0.0.4 を追加しますよ、と表示されます。
% terraform plan
iosxe_rest.ntp_put: Refreshing state... [id=4117814902]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be updated in-place
~ resource "iosxe_rest" "ntp_put" {
id = "4117814902"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-native:ntp = {
~ Cisco-IOS-XE-ntp:server = {
~ server-list = [
# (2 unchanged elements hidden)
{
ip-address = "10.0.0.3"
},
+ {
+ ip-address = "10.0.0.4"
},
]
}
}
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
5.3. terraform apply の実行
terraform apply を実行します。
% terraform apply
iosxe_rest.ntp_put: Refreshing state... [id=4117814902]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be updated in-place
~ resource "iosxe_rest" "ntp_put" {
id = "4117814902"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-native:ntp = {
~ Cisco-IOS-XE-ntp:server = {
~ server-list = [
# (2 unchanged elements hidden)
{
ip-address = "10.0.0.3"
},
+ {
+ ip-address = "10.0.0.4"
},
]
}
}
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
iosxe_rest.ntp_put: Modifying... [id=4117814902]
iosxe_rest.ntp_put: Modifications complete after 2s [id=477754463]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
無事に設定されました。
csrv1000(config)#do sh run | inc ntp ntp server 10.0.0.1 ntp server 10.0.0.2 ntp server 10.0.0.3 ntp server 10.0.0.4
■ 6. NTP サーバー設定4台から1台へ (PUT)
4台分設定されている状態から、PUTで1台分にします。
6.1. ntp_put.tf の修正
ntp_put.tf を以下のように、1台分(10.0.0.1)だけになるように修正します。method は "PUT" のままです。
resource "iosxe_rest" "ntp_put" { method = "PUT" path = "/data/Cisco-IOS-XE-native:native/ntp" payload = jsonencode( { "Cisco-IOS-XE-native:ntp" : { "Cisco-IOS-XE-ntp:server" : { "server-list" : [ { "ip-address" : "10.0.0.1" } ] } } } ) }
他の3台を削除する、といった指定はせず、とにかく指定した1台分(10.0.0.1)になってほしい、意味合いの指定です。
6.2. terraform plan の実行
terraform plan を実行します。10.0.0.2、10.0.0.3、10.0.0.4 は削除しますよと示されます。
% terraform plan
iosxe_rest.ntp_put: Refreshing state... [id=477754463]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be updated in-place
~ resource "iosxe_rest" "ntp_put" {
id = "477754463"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-native:ntp = {
~ Cisco-IOS-XE-ntp:server = {
~ server-list = [
{
ip-address = "10.0.0.1"
},
- {
- ip-address = "10.0.0.2"
},
- {
- ip-address = "10.0.0.3"
},
- {
- ip-address = "10.0.0.4"
},
]
}
}
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
6.3. terraform apply の実行
terraform apply を実行します。
% terraform apply
iosxe_rest.ntp_put: Refreshing state... [id=477754463]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
~ update in-place
Terraform will perform the following actions:
# iosxe_rest.ntp_put will be updated in-place
~ resource "iosxe_rest" "ntp_put" {
id = "477754463"
~ payload = jsonencode(
~ {
~ Cisco-IOS-XE-native:ntp = {
~ Cisco-IOS-XE-ntp:server = {
~ server-list = [
{
ip-address = "10.0.0.1"
},
- {
- ip-address = "10.0.0.2"
},
- {
- ip-address = "10.0.0.3"
},
- {
- ip-address = "10.0.0.4"
},
]
}
}
}
)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
iosxe_rest.ntp_put: Modifying... [id=477754463]
iosxe_rest.ntp_put: Modifications complete after 1s [id=1111786887]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
無事に 10.0.0.1 のみになりました。
csrv1000(config)#do sh run | inc ntp ntp server 10.0.0.1 csrv1000(config)#
method としては、他にも PATCH や DELETE もしてできますが、今回はこの辺にしたいと思います。
まとめ・所感
Terraform の IOS XE Provider を利用して、IOS XE の機器にのNTPサーバー設定を試してみました。
以下、まとめと所感です。
- RESTCONF ベースなのでCLIベースの自動化の悩み(noコマンドの生成)はなさそう
- その代わり RESTCONF の知識が必要
- 手続き型に考えが染まっているので、宣言型の良さをまだ活かせる自身がない
- リソースをどう扱えばよいか悩みそう
- method をどうすればよいか悩みそう。PUT が一番相性がいいように思う
- 本格的に使う上では考慮事項がそれなりにありそう
- 同じ内容のPOST を2回 実行してもエラーにならなかったのは Terraform らしさを感じた
- Terraform か RESTCONF のどちらかに慣れていないと、エラーが発生したときにどちらの問題か判断しにくい
- Terraform なのでWindowsからも実行できるはず(今回はmacOSから)
参考資料
紹介ブログ https://blogs.cisco.com/developer/terraformiosxe01
GitHub リポジトリ https://github.com/CiscoDevNet/terraform-provider-iosxe
tf ファイルのサンプル
https://github.com/CiscoDevNet/terraform-provider-iosxe/tree/main/examples/examples_tf
動画 Cisco IOS XE Terraform provider introduction and demo https://www.youtube.com/watch?v=oB_QZ2mDiW0
Terraform Registry https://registry.terraform.io/providers/CiscoDevNet/iosxe/latest