本記事について
本記事では、Red Hat から提供されている構成管理ツールである Ansible を使用して、ネットワーク機器である Cisco IOS 機器を設定する方法の一例を説明します。
動作確認環境
本記事は以下の環境にて検証した結果に基づき作成されています。
- Ansible 環境
- CentOS Stream release 9
- Python 3.12.6
- pip 24.2
- ansible-core 2.17.4
- Ansible community version 10.4.0
- cisco.ios 8.0.0
- ターゲット機器環境
- Cisco C891FJ-K9
- Cisco IOS 15.8(3)M9
- Cisco Catalyst2960-8TC-L
- Cisco IOS 12.2(55)SE12
- Cisco C891FJ-K9
前提
本記事では、Cisco IOS 機器を Ansible を使用して設定する方法について記載します。Ansible でターゲット機器を操作するためには、Ansible からターゲット機器に対して、SSH 接続できる必要があります。このため、Cisco IOS 機器側では以下の最低限の設定がされていることを前提とします。
- ホスト名設定
- ドメイン名設定
- RSA鍵の作成
- SSHログイン用ユーザの設定
- enableパスワードの設定
- SSH接続を受け付けるインターフェースのIPアドレス設定
- ルーティング設定(必要な場合)
- line vty の設定
- login local
- transport input ssh/all
ここでは Cisco IOS 機器を対象としています。同じ Cisco 機器でも OS の異なる Nexus や ASA 等は対象外となります。
また Ansible では Cisco IOS 機器に SSH 接続するための初期設定が完了していることを前提とします。
CentOS Stream 9 での Ansible のインストール方法についてはこちらの記事を参照してください。
想定するネットワーク構成
ここでは以下のようにターゲットとなる Cisco IOS 機器と Ansible マシンが L2 ネットワークで接続されている環境で操作を行うことを想定します。

使用する Ansible コレクション及びモジュール
本記事では Cisco IOS 機器を設定するために、cisco.ios コレクションの ios_config モジュールを使用します。
cisco.ios コレクションは、ansible パッケージをインストールするとデフォルトで含まれています。
cisco.ios コレクションがインストールされているかどうかはansible-galaxy collection listコマンドで確認できます。以下のようにcisco.iosが表示されていればインストールされています。
(venv) [root@CentOSST9 ~]# ansible-galaxy collection list
# /root/venv/lib/python3.12/site-packages/ansible_collections
Collection Version
---------------------------------------- -------
amazon.aws 8.2.1
ansible.netcommon 6.1.3
ansible.posix 1.5.4
ansible.utils 4.1.0
ansible.windows 2.5.0
(中略)
cisco.aci 2.10.1
cisco.asa 5.0.1
cisco.dnac 6.18.0
cisco.intersight 2.0.17
cisco.ios 8.0.0
cisco.iosxr 9.0.0
cisco.ise 2.9.3
(以下略)なお、cisco.ios コレクションには Cisco IOS の特定コンフィグ項目を設定することに特化したモジュールがいくつかありますが、一般的なネットワーク構築案件にてキッティングを行う場合コンフィグを丸ごと設定するため、特定のコンフィグ項目を対象としたモジュールを使用することは効率の面でメリットがありません。
本記事では、インプット情報として設定投入に使える機器コンフィグ全体があることを前提として、そのコンフィグを単純にそのまますべて機器に投入する方式を取ることとします。このためには ios_config モジュールを使用します。
インベントリの作成
ターゲット機器の情報を記載したインベントリファイルを作成します。
本記事では hosts という名前で以下内容のファイルを作成します。
[ios]
RT01 ansible_host=10.1.1.1 ansible_user=admin ansible_password=admin ansible_become_password=admin configfile=rt.conf
SW01 ansible_host=10.1.1.50 ansible_user=admin ansible_password=adminadmin ansible_become_password=admin configfile=sw.conf
[ios:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_become=yes
ansible_become_method=enable- [ios]
- iosという名前のターゲット機器グループを定義し、RT01 と SW01 という名前のメンバーを定義すると同時に機器固有の変数を定義。変数は半角スペース区切りで複数設定できます
- ansible_host:ターゲット機器のIPアドレス
- ansible_user:SSH ログインユーザ名。変数名変更不可
- ansible_password:SSH ログインユーザパスワード。変数名変更不可
- ansible_become_password:Cisco IOS の enable パスワード。変数名変更不可
- configfile:設定するコンフィグを記載したファイル。変数名変更可能(後に作成するPlaybook内で変数名を使用)
- iosという名前のターゲット機器グループを定義し、RT01 と SW01 という名前のメンバーを定義すると同時に機器固有の変数を定義。変数は半角スペース区切りで複数設定できます
- [ios:vars]
- iosグループ内の共通変数を定義
- ansible_connection
- ターゲット機器への接続方法の設定
- ターゲット機器が Cisco IOS 機器の場合 ansible.netcommon.network_cli を指定
- ansible_network_os
- ターゲット機器がどのネットワークプラットフォームに対応しているかの設定
- ターゲット機器が Cisco IOS 機器の場合 cisco.ios.ios を指定
- ansible_become=yes
- Cisco IOS 機器で特権モードに移行するために必要
- ansible_become_method=enable
- Cisco IOS 機器で特権モードに移行するために必要
Playbook の作成
Cisco IOS 機器を設定変更するための Playbook を作成します。
コンフィグファイルの用意
本記事ではあらかじめ用意しておいた投入コマンドを記載したコンフィグファイルを Playbook から読み込む方式とするため、コンフィグファイルをインベントリに記載した変数 configfile で指定したファイル名で用意します。
本記事の例ではルータ用のコンフィグファイルとして「rt.conf」、スイッチ用のコンフィグファイルとして「sw.conf」を用意します。
version 15.8
service timestamps debug datetime msec
service timestamps log datetime msec localtime show-timezone year
no service password-encryption
!
hostname Router01
(中略)
!
!
endversion 12.2
no service pad
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname Switch01
(中略)
line vty 5 15
login local
transport input all
!
end本記事では設定後の想定コンフィグをそのまま投入コンフィグとして使用します。このコンフィグファイルは、設定後の差分確認処理でも使用します。
Playbook の作成
本記事では ios_configure.yml というファイル名で Playbook を作成します。
Playbook の処理内容は以下の通りとします。
- cisco.ios.ios_config モジュールを使用して設定変更
- src オプションを使用して外部のコンフィグファイルを読み込みます
- cisco.ios.ios_config モジュールを使用して設定後のコンフィグと想定コンフィグの差分確認
- diff_against、intended_config オプションを使用します
- cisco.ios.ios_config モジュールを使用して startup-config に設定を保存します
- save_when: always オプションを使用します
作成する Playbook の内容は以下の通りです。
- name: Playbook Test
hosts: ios
gather_facts: no
tasks:
- name: Configure IOS
cisco.ios.ios_config:
src: "{{ configfile }}"
- name: Check the running-config against master config
cisco.ios.ios_config:
diff_against: intended
intended_config: "{{ lookup('file', '{{ configfile }}') }}"
diff: yes
- name: Save running to startup
cisco.ios.ios_config:
save_when: always- hosts: ios
- ターゲット機器として ios グループを指定しています
- src: “{{ configfile }}”
- 設定変更で使用するコンフィグファイルとして対象機器の変数 configfile で指定したファイルを使用します
- diff_against: intended
- 外部ファイルとのコンフィグ比較をする場合に指定します
- intended_config: “{{ lookup(‘file’, ‘{{ configfile }}’) }}”
- 比較で使用するファイルとして対象機器の変数 configfile で指定したファイルを使用します
- diff: yes
- コンフィグ比較を行うためにモジュール単位で diff を有効化します
- save_when: always
- cisco.ios.ios_config モジュールで設定を保存する場合はこのオプションを使用します
Playbook の実行
Playbook の実行コマンドは以下です。
- ansible-playbook -i <インベントリ> <Playbook>
以下は実行例です。
(venv) [root@CentOSST9 ansible]# ansible-playbook -i hosts ios_configure.yml
PLAY [Playbook Test] ************************************************************************************************
TASK [Configure IOS] ************************************************************************************************
ok: [RT01]
ok: [SW01]
TASK [Check the running-config against master config] ***************************************************************
ok: [RT01]
ok: [SW01]
TASK [Save running to startup] **************************************************************************************
changed: [SW01]
changed: [RT01]
PLAY RECAP **********************************************************************************************************
RT01 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
SW01 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=02つ目のタスクの差分確認で差分が無かった場合上記例の様に単に ok と表示されますが、差分があった場合は以下のようにステータスが changed になり差分が表示されます。
(venv) [root@CentOSST9 ansible]# ansible-playbook -i hosts ios_configure.yml
PLAY [Playbook Test] ************************************************************************************************
TASK [Configure IOS] ************************************************************************************************
ok: [RT01]
ok: [SW01]
TASK [Check the running-config against master config] ***************************************************************
ok: [RT01]
--- before
+++ after
@@ -11,7 +11,6 @@
no aaa new-model
system mtu routing 1500
vtp mode transparent
-no ip domain-lookup
ip domain-name test.com
spanning-tree mode pvst
spanning-tree extend system-id
@@ -20,8 +19,6 @@
vlan 10,20,30,179
ip tftp source-interface Vlan1
ip ssh version 2
-interface Loopback0
- ip address 2.2.2.2 255.255.255.255
interface FastEthernet0/1
switchport access vlan 10
switchport mode access
changed: [SW01]
TASK [Save running to startup] **************************************************************************************
changed: [SW01]
changed: [RT01]
PLAY RECAP **********************************************************************************************************
RT01 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
SW01 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
事後コンフィグをファイルに保存する処理を追加する
設定変更後のコンフィグをファイルに保存したいということも考えられますので、以下の処理を Playbook に追加します。
- cisco.ios.ios_facts モジュールでコンフィグ含む機器情報を取得する
- copy モジュールで取得したコンフィグをファイルに保存する
更新後の Playbook の内容は以下の通りです。
- name: Playbook Test
hosts: ios
gather_facts: no
tasks:
- name: Configure IOS
cisco.ios.ios_config:
src: "{{ configfile }}"
- name: Check the running-config against master config
cisco.ios.ios_config:
diff_against: intended
intended_config: "{{ lookup('file', '{{ configfile }}') }}"
diff: yes
- name: Save running to startup
cisco.ios.ios_config:
save_when: always
- name: Gather facts
cisco.ios.ios_facts:
gather_subset: config
- name: save file
copy:
content: "{{ ansible_net_config }}"
dest: ./config/{{ ansible_net_hostname }}_{{ now(False, '%Y%m%d') }}.txt17行目以降が追加した内容です。
- cisco.ios.ios_facts:Cisco 機器の様々な情報を取得できるモジュールです
- gather_subset: config
- running-config を含む情報を取得します
- gather_subset: config
- copy:指定した内容を指定したパスに保存するモジュールです
- content: “{{ ansible_net_config }}”
- 保存する内容を指定しています。ansible_net_config は cisco.ios.ios_facts で取得した running-config が保存される変数です
- dest:
- 保存先ファイルパスを指定しています。上記例では Playbook と同フォルダ内にある config フォルダ配下のパスを指定しています
- ansible_net_hostname は cisco.ios.ios_facts で取得したホスト名が保存される変数です
- now(False, ‘%Y%m%d’) では現在の年月日を指定しています
- content: “{{ ansible_net_config }}”
以下は実行結果です。
(venv) [root@CentOSST9 ansible]# ansible-playbook -i hosts ios_configure.yml
PLAY [Playbook Test] ************************************************************************************************
TASK [Configure IOS] ************************************************************************************************
ok: [RT01]
ok: [SW01]
TASK [Check the running-config against master config] ***************************************************************
ok: [RT01]
ok: [SW01]
TASK [Save running to startup] **************************************************************************************
changed: [SW01]
changed: [RT01]
TASK [Gather facts] *************************************************************************************************
ok: [RT01]
ok: [SW01]
TASK [save file] ****************************************************************************************************
changed: [SW01]
changed: [RT01]
PLAY RECAP **********************************************************************************************************
RT01 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
SW01 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0最後の copy タスクについては、ファイルが新規作成された、または既存ファイルの内容が変更された場合、ステータスは changed になります。
実行後以下のようにコンフィグを記録したファイルが保存されます。
(venv) [root@CentOSST9 ansible]# ls -l config
合計 8
-rw-r--r-- 1 root root 2462 9月 23 15:45 Router01_20240923.txt
-rw-r--r-- 1 root root 1557 9月 23 15:45 Switch01_20240923.txt保存されたファイルの内容は以下のようになっています。
Building configuration...
Current configuration : 2399 bytes
!
! Last configuration change at 12:14:48 JST Mon Sep 23 2024
!
version 15.8
service timestamps debug datetime msec
service timestamps log datetime msec localtime show-timezone year
no service password-encryption
!
hostname Router01
(中略)
!
!
!
end対話的メッセージが表示される設定変更への対応
IOS 機器で、例えば既存ユーザを削除するコマンドを実行した場合、以下のような確認メッセージが表示されキー押下待ちの状態になります。
This operation will remove all username related configurations with same name.Do you want to continue? [confirm]
この仕様がモジュールの実行結果に不具合を起こす場合があります。
cisco.ios.ios_config モジュールで設定変更を行う場合、設定コマンド1行実行毎にコンフィグモードのプロンプトを待ってから次のコマンドを実行するという流れになるため、上記のような確認メッセージが表示される場合プロンプト待ち状態から処理が進まなくなり以下のようにタイムアウトエラーになってしまいます。
TASK [Configure IOS] *****************************************************************************
fatal: [RT01]: FAILED! => {“changed”: false, “module_stderr”: “command timeout triggered, timeout value is 30 secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide.”, “module_stdout”: “”, “msg”: “MODULE FAILURE\nSee stdout/stderr for the exact error”}
cisco.ios.ios_config モジュールではこのような対話的な操作には対応できません。回避策としては以下の方法があります。
- 対話的な操作が必要な設定のみ特権モードからコマンドを実行する cisco.ios.ios_command モジュールを使用してタスクを分ける
- 対話的な操作が必要な設定のみ対象項目の設定用モジュールを使用してタスクを分ける
cisco.ios.ios_command モジュールを使用する方法
cisco.ios.ios_command モジュールでは対話的な操作に対応するためのオプションが用意されているため、これを利用します。
以下はユーザ削除を行う場合のタスク例です。
- name: Delete user
cisco.ios.ios_command:
commands:
- command: "conf t"
- command: "no username test"
prompt: "[confirm]"
answer: "y"commands:- リスト形式で実行コマンドリストを定義します
- command: "conf t"- cisco.ios.ios_command モジュールは特権モードからコマンドを実行するため、まずグローバルコンフィギュレーションモードに移行します
- command: "no username test"- ユーザ削除用のコマンドです
prompt: "[confirm]"- 待ち受ける文字列を指定します。メッセージ全体である必要は無く、部分的な文字列でも大丈夫です
- この文字列を受信した際に
answer:で指定した文字列を送信します
answer: "y"prompt:で指定した文字列を受信した際に送信する文字列を指定しています
対象項目の設定用モジュールを使用する方法
cisco.ios コレクションでは、いくつかの設定項目についてはその項目を設定するためのモジュールが用意されています。これらの設定用モジュールを使用して設定変更する場合は対話的な操作を考慮する必要はなく、より安全に設定変更をすることができます。
例えばユーザを設定するためのモジュールとしてcisco.ios.ios_userというモジュールが存在します。
以下はユーザ削除を行う場合のタスク例です。
- name: Delete user
cisco.ios.ios_user:
name: test
state: absentname: test- 対象のユーザ名として「test」を指定しています
state: absent- 対象ユーザの状態を指定しています。「absent」を指定するとユーザが存在しない状態となり、対象ユーザが存在する場合はそのユーザが削除されます
参考資料
Ansible 関連記事一覧

コメント