- はじめに
- ■ 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-serve
restconf
- ドキュメント上は 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_IOSXE
DEVICE_PASSWORD_IOSXE
DEVICE_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