てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] file モジュールの基本的な使い方(ファイルやディレクトリの操作)

■ はじめに

Ansible には、ファイル属性の設定やディレクトリの作成などができる file モジュール があります。

この記事では、 file モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ [例1] 所有者やパーミッションを設定する

Playbook

既存のファイル /etc/foo.conf の所有者、所有グループ、パーミッションを設定する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - file:
        path: /etc/foo.conf
        owner: kingyo
        group: kingyo
        # when specifying mode using octal numbers, add a leading 0
        mode: 0644
  • path オプション
    • 対象ファイルのパスを指定します。(必須)
    • 本タスクの場合、state オプションは指定していないため(デフォルトの state: file 扱い)、ここで指定したファイルは あらかじめ存在している必要があります
    • もし空のファイルを作成する場合は、state: touch を併用します。その場合も、パスの途中に含まれるディレクトリは存在している必要があります。
  • onwer オプション
    • 対象ファイルの所有者を指定します。
  • group オプション
    • 対象ファイルの所有グループを指定します。
  • mode オプション
    • 対象ファイルの mode を指定します。
    • 8進数で指定する場合は、0644 のように 0 から始めるか、'644' のようにクォーテーションで囲う必要があります。
    • u=rw,g=r,o=r のような指定もできます。

本タスクで指定していないその他のオプション例

  • recurse オプション
    • path オプションでディレクトリを指定した場合、操作は再帰的に行うかどうかを指定します。(デフォルト no

実行ログ

ここでは、/etc/foo.conf が 所有者 root、所有者グループ rootパーミッション 664 の状態で Playbook を実行します。

  • リモート側の事前確認
$ sudo ls -al /etc/foo.conf
-rw-rw-r--. 1 root root 6 Mar  3 06:57 /etc/foo.conf
  • Ansible 側
$ ansible-playbook -i inventory file_attr.yml 

PLAY [linux] ******************************************************************************************

TASK [file] *******************************************************************************************
changed: [linux1]

PLAY RECAP ********************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
  • リモート上の事後確認
$ sudo ls -al /etc/foo.conf
-rw-r--r--. 1 kingyo kingyo 6 Mar  3 06:57 /etc/foo.conf

無事に /etc/foo.conf が指定通りの所有者、パーミッションになりました。


■ [例2] ファイルやディレクトリを削除する

Playbook

ファイル /etc/foo.conf をファイルを削除する Playbook です。 公式ドキュメントの資料には載っていませんが、使用する頻度はそこそこあるのではないかと思います。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - file:
        path: /etc/foo.conf
        state: absent
  • path オプション
    • 対象(ここではファイル名)のパスを指定します。(必須)
  • state オプション
    • 対象の種類を指定します。ここでは削除するため state: absent を指定しています。

実行ログ

ここでは、/etc/foo.conf がある状態で Playbook を実行します。

  • Ansible 側
$ ansible-playbook -i inventory file_absent.yml 

PLAY [linux] ***************************************************************************

TASK [file] ****************************************************************************
changed: [linux1]

PLAY RECAP *****************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
  • リモート上の事後確認
$ sudo ls /etc/foo.conf
ls: cannot access /etc/foo.conf: No such file or directory

無事に /etc/foo.conf が削除されました。


■ [例3] ディレクトリを作成する

Playbook

ディレクト/etc/some_directory を作成する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - file:
        path: /etc/some_directory
        state: directory
        mode: 0755
  • path オプション
    • 対象(ここではディレクトリ名)のパスを指定します。(必須)
  • state オプション
    • 対象の種類を指定します。ここでは対象がディレクトリのため state: directory を指定してます。
    • パスの途中に存在していないディレクトリが含まれている場合でも再帰的にディレクトリを作成することもできます。(mkdir コマンドの -p オプションのように)
  • mode オプション
    • 対象ファイルの mode を指定します。

実行ログ

ここでは、/etc/some_directory がない状態で Playbook を実行します。

  • Ansible 側
$ ansible-playbook -i inventory file_dir.yml 

PLAY [linux] ******************************************************************************************

TASK [file] *******************************************************************************************
changed: [linux1]

PLAY RECAP ********************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
 
  • リモート上の事後確認
$ ls -al /etc/some_directory/
total 8
drwxr-xr-x.  2 root root 4096 Mar  3 07:14 .
drwxr-xr-x. 88 root root 4096 Mar  3 07:14 .

無事に /etc/some_directory ディレクトリが作成されました。すでにこのディレクトリがあって、パーミッション755 だった場合は特に何もしません。


■ まとめ

公式ドキュメントの使用例をベースにして、file モジュール の使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、file モジュールは Files modules に分類されています。Files modules には、他にも、指定した正規表現にマッチするすべての文字列を置換する replace や、行単位で編集する lineinfile などのモジュールがあります。詳細は Files modules の一覧からご確認ください。

[Ansible] template モジュールの基本的な使い方(Jinja2テンプレートからファイル生成)

■ はじめに

Ansible には、Jinja2 というテンプレートエンジンによるテンプレートファイルを利用してファイルを生成してリモートに送れる template モジュール があります。httpd.conf などの設定ファイルを、変数とテンプレートから生成するといった用途に利用できます。

この記事では、 template モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ [例1] 単純にテンプレートからファイルを生成して送る

Playbook

Jinja2 テンプレートファイル foo.j2 を元にファイルを生成して、リモートに /etc/file.conf として送る Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    # Example from Ansible Playbooks
    - template:
        src: ./foo.j2
        dest: /etc/file.conf
        owner: bin
        group: wheel
        mode: 0644

  vars:
    test_name: Ansible
  • src オプション
    • もととなる Jinja2 テンプレートファイル名を指定します。(必須)
    • 今回指定している ./foo.j2 の内容は後述します。
  • dest オプション
    • 送り先のリモートのファイル名を指定します。(必須)
  • onwer オプション
    • リモートファイルの所有者を指定します。
  • group オプション
    • リモートファイルの所有グループを指定します。
  • mode オプション
    • リモートファイルの mode を指定します。
    • 8進数で指定する場合は、0644 のように 0 から始めるか、'644' のようにクォーテーションで囲う必要があります。
    • u=rw,g=r,o=r のような指定もできます。

使用するテンプレートファイル (./foo.j2)は以下のとおりです。

Hello, {{ test_name }}!

{{ test_name }} には、今回の場合 Playbook 内の vars ディレクティブ で指定した変数 test_name の値が展開されます。

Jinja2 の書式の詳細は、Jinja2 のドキュメントを確認してください。

本タスクで指定していないその他のオプション例

  • backup オプション
    • dest オプションで指定したリモートのファイルがすでに存在する場合に、バックアップをとるかどうか指定するオプションです。(デフォルト no
    • バックアップファイルの名前はタイムスタンプを含むものになります。

実行ログ

ここでは、リモートに /etc/file.conf が存在しない状態で Playbook を実行します。

  • Ansible 側
$ ansible-playbook -i inventory template_simple.yml 

PLAY [linux] ******************************************************************************

TASK [template] ***************************************************************************
changed: [linux1]

PLAY RECAP ********************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
  • リモート上の確認
$ cat /etc/file.conf
Hello, Ansible!
$ ls -al /etc/file.conf
-rw-r--r--. 1 bin wheel 16 Mar  3 05:44 /etc/file.conf

無事に /etc/file.conf が生成され、テンプレートで指定した変数 {{ test_name }} も展開されました。 所有者やパーミッションも指定したとおりになりました。

なお、この後もう一度同じ Playbook を実行した場合は、すでに同じ内容、権限 になっていることを検出して、とくに何もしません。そのため、対象ファイルの更新日時も変わりません。


■ [例2] 正しい内容に生成されるかを事前に検証する

Playbook

Jinja2 テンプレートファイル sudoers.j2 を元にファイルを生成して、リモートに /etc/file.conf として送って適用する前に /usr/sbin/visudo -cf %s コマンドでファイルのフォーマットを確認する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    # Copy a new "sudoers" file into place, after passing validation with visudo
    - template:
        src: ./sudoers.j2
        dest: /etc/sudoers
        validate: '/usr/sbin/visudo -cf %s'

  vars:
    wheel_group_name: wheel
  • src オプション
    • もととなる Jinja2 テンプレートファイル名を指定します。(必須)
    • 今回指定している ./sudoers.j2 の内容は後述します。
  • dest オプション
    • 送り先のリモートのファイル名を指定します。(必須)
  • validate オプション
    • 生成したファイルの内容が正しいか検証するためのコマンドを指定します。対象を %s で指定します。
    • ファイルの内容のフォーマットエラーが、システムへ大きく影響するためあらじめ検証たい場合に利用できます。

使用するテンプレートファイル (./sudoers.j2)は以下のとおりです。

(...略...)
%{{ wheel_group_name }}  ALL=(ALL)       ALL
(...略...)

{{ wheel_group_name }} には、今回の場合 Playbook 内の vars ディレクティブ で指定した変数 wheel_group_name の値が展開されます。

実行ログ

  • Ansible 側
$ ansible-playbook -i inventory template_validate.yml            

PLAY [linux] ********************************************************************************************************

TASK [template] *****************************************************************************************************
changed: [linux1]

PLAY RECAP **********************************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
  • リモート上の確認
$ sudo cat /etc/sudoers
(...略...)
%wheel  ALL=(ALL)       ALL
(...略...)

無事に /etc/sudoers が生成され、テンプレートで指定した変数 {{ wheel_group_name }} も展開されました。


■ まとめ

公式ドキュメントの使用例をベースにして、template モジュール の使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、template モジュールは Files modules に分類されています。Files modules には、他にも、指定した正規表現にマッチするすべての文字列を置換する replace や、行単位で編集する lineinfile などのモジュールがあります。詳細は Files modules の一覧からご確認ください。

[Ansible] user モジュールの基本的な使い方(ユーザーの作成・削除など)

■ はじめに

Ansible には、ユーザーを管理(作成、削除など)する user モジュール があります。

この記事では、 user モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ [例1] 単純にユーザーを作成する

Playbook

ユーザー kingyo を作成する Playbook です。公式ドキュメントの使用例にはありませんが、一番シンプルな使い方です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Add the user 'kingyo'
      user:
        name: kingyo
  • name オプション
    • 対象のユーザー名を指定します。(必須)

本タスクで指定していないその他のオプション例

  • state オプション
    • 対象のユーザーが存在する状態(present)にするか、存在しない状態(absent)にするかを指定します。(デフォルト present
    • ここでは state: absent を指定しているため、存在しない状態(存在する場合は削除)という指定になります。

実行ログ

ここでは、kingyo ユーザー未作成の状態で Playbook を実行します。

  • リモート上の事前確認
$ id kingyo 
id: kingyo: no such user  # kingyo ユーザーがまだ存在しない
  • Ansible 側
$ ansible-playbook -i inventory user_add_simple.yml 

PLAY [linux] **************************************************************************************************

TASK [Add the user 'kingyo'] **********************************************************************************
changed: [linux1]

PLAY RECAP ****************************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
  • リモート上の事後確認
$ id kingyo 
uid=1041(kingyo) gid=1041(kingyo) groups=1041(kingyo)  # kingyo ユーザーが作成された
$ cat /etc/passwd | grep kingyo
kingyo:x:1041:1041::/home/kingyo:/bin/bash

無事に指定した通りに、ユーザー kingyo が作成されました。 groupgroups オプションを指定していなかったため、暗黙的に新たに作成された kingyo グループにプライマリグループとして所属しています。


■ [例2] プライマリグループとユーザーIDを指定してユーザーを作成する

Playbook

作成済みの admin グループにプライマリグループとして所属し、ユーザーID 1040 のユーザー jonhnd を作成する Playbook です

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
      user:
        name: johnd
        comment: John Doe
        uid: 1040
        group: admin
  • name オプション
    • 対象のユーザー名を指定します。(必須)
  • comment オプション
    • /etc/passwd のコメント欄に記入するコメントを指定します。
  • uid オプション
    • 対象のユーザーIDを指定します。
  • group オプション
    • 対象のユーザーが所属するプライマリグループ名を指定します。
    • 似たようなオプションに groups(サブグループの指定)があるので注意してください。

実行ログ

ここでは、johnd ユーザーは未作成、admin グループは作成済みの状態で Playbook を実行します。

$ ansible-playbook -i inventory user_add.yml 

PLAY [linux] **********************************************************************************

TASK [Add the user 'johnd' with a specific uid and a primary group of 'admin'] ****************
changed: [linux1]

PLAY RECAP ************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ 
$ id johnd
uid=1040(johnd) gid=1002(admin) groups=1002(admin)
$ cat /etc/passwd | grep johnd
johnd:x:1040:1002:John Doe:/home/johnd:/bin/bash    # johnd ユーザーが作成された

無事に指定した通りに、ユーザー johnd が作成されました。


■ [例3] サブグループと使用シェルを指定してユーザーを作成する

Playbook

作成済みの adminsdevelopers グループにサブグループとして所属し、`/bin/bash を使用するユーザー james を作成する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Add the user 'james' with a bash shell, appending the group 'admins' and 'developers' to the user's groups
      user:
        name: james
        shell: /bin/bash
        groups: admins, developers
        append: yes
  • name オプション
    • 対象のユーザー名を指定します。(必須)
  • shell オプション
    • 使用するシェルを指定します。
  • groups オプション
    • 対象のユーザーが所属するサブグループ名を指定します。
    • 複のグループ名を指定できます。Ansible 2.3 からは、カンマ区切りの他にリストも指定できます。
    • 似たようなオプションに group (プライマリグループの指定)があるので注意してください。
  • append オプション
    • groups オプションで指定したグループ名を追加所属させるかどうかを指定します。(デフォルト no
    • append: no を指定(デフォルト)の場合、groups オプションで指定したグループ以外の所属は外れます。

実行ログ

ここでは、james ユーザーは未作成、adminsdevelopers グループは作成済みの状態で Playbook を実行します。

$ ansible-playbook -i inventory user_add_goups.yml 

PLAY [linux] **********************************************************************************

TASK [Add the user 'james' with a bash shell, appending the group 'admins' and 'developers' to the user's groups] ***
changed: [linux1]

PLAY RECAP ************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ id james
uid=1002(james) gid=1005(james) groups=1005(james),1003(admins),1004(developers)  # james ユーザーが作成された
$ cat /etc/passwd | grep james   
james:x:1002:1005::/home/james:/bin/bash

無事に指定した通りに、ユーザー james が作成されました。


■ [例4] ユーザーを削除する

Playbook

作成済みのユーザー johnd を削除する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Remove the user 'johnd'
      user:
        name: johnd
        state: absent
        remove: yes
  • name オプション
    • 対象のユーザー名を指定します。(必須)
  • state オプション
    • 対象のユーザーが存在する状態(present)にするか、存在しない状態(absent)にするかを指定します。(デフォルト present
  • remove オプション
    • state: absent の指定場合った場合、対象のユーザーに関連づいたディレクトリを削除します。(デフォルト no

実行ログ

$ id johnd                  
uid=1040(johnd) gid=1002(admin) groups=1002(admin) # johnd ユーザーがまだ存在する
$ cat /etc/passwd | grep johnd
johnd:x:1040:1002:John Doe:/home/johnd:/bin/bash
$ sudo ls -al /home/johnd/  
total 20         # johnd のホームディレクトリがまだ存在する
drwx------. 2 1040 admin 4096 Mar  3 01:53 .
drwxr-xr-x. 6 root root  4096 Mar  3 02:08 ..
-rw-r--r--. 1 1040 admin   18 Oct 30 17:07 .bash_logout
-rw-r--r--. 1 1040 admin  193 Oct 30 17:07 .bash_profile
-rw-r--r--. 1 1040 admin  231 Oct 30 17:07 .bashrc

$ ansible-playbook -i inventory user_remove.yml 

PLAY [linux] ********************************************************************************************************************

TASK [Remove the user 'johnd'] **************************************************************************************************
changed: [linux1]

PLAY RECAP **********************************************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ id johnd   
id: johnd: no such user      # johnd ユーザーが削除された
$ sudo ls -al /home/johnd/   
ls: cannot access /home/johnd/: No such file or directory  # johnd のホームディレクトリも削除された

無事に指定した通りに、ユーザー johnd とホームディレクトリも削除されました。


■ まとめ

公式ドキュメントの使用例をベースにして、user モジュール の使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、user モジュールは System modules に分類されています。System modules には、他にも、グループを管理する group や、cron エントリを管理する cron 、などのモジュールがあります。詳細は System modules の一覧からご確認ください。

[Ansible] lineinfile モジュールの基本的な使い方(テキストファイルの行単位の編集)

■ はじめに

Ansible には、テキストファイルを行単位で編集する lineinfile モジュール があります。httpd.conf などの設定ファイルの設定項目を書き換えるといった用途に利用できます。

この記事では、 lineinfile モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ 単純に行置換する

Playbook

/etc/selinux/config 内の '^SELINUX=' にマッチする行を SELINUX=enforcing に置換する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - lineinfile:
        path: /etc/selinux/config
        regexp: '^SELINUX='
        line: 'SELINUX=enforcing'
  • path オプション
    • 対象のファイル名のパス名を指定します。(必須)
  • regexp オプション
    • 編集対象とする行を示す正規表現を指定します。
    • マッチする行が複数ある場合、最後にマッチした行が対象になります。
    • マッチしなかった場合は EOF が対象(デフォルトの backrefs: no の場合 )
  • line オプション
    • 挿入/置換する行を指定します。
    • state オプションが present (デフォルト) の場合のみ有効です。

本タスクで指定していないその他のオプション例

  • ownergroup オプション
    • 所有者情報を指定できます。
  • mode オプション
  • create オプション
    • path で指定したファイルが存在しなかった場合にファイル作成するかどうか(デフォルト no
  • backup オプション
    • 編集前のファイルをバックアップとして別途保存するかどうか(デフォルト no

実行ログ

ここでは、SELINUX=disabled の状態で Playbook を実行します。

$ cat /etc/selinux/config | grep "^SELINUX=" 
SELINUX=disabled
$ ansible-playbook -i inventory lineinfile_simple.yml 

PLAY [linux] ***************************************************************

TASK [lineinfile] **********************************************************
changed: [linux1]

PLAY RECAP *****************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ cat /etc/selinux/config | grep "^SELINUX="  
SELINUX=enforcing

無事に SELINUX=enforcing に置換されました。

なお、この後もう一度同じ Playbook を実行した場合は、すでに SELINUX=enforcing になっていることを検出して、とくに何もしません。そのため、対象ファイルの更新日時も変わりません。


■ 行を削除する

Playbook

/etc/sudoers 内の '^%wheel' にマッチする行を削除する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - lineinfile:
        path: /etc/sudoers
        state: absent
        regexp: '^%wheel'
  • state オプション
    • マッチした行を削除したい場合は state: absent を指定します。

実行ログ

ここでは、'^%wheel' にマッチする行がある状態で Playbook を実行します。

$ sudo cat /etc/sudoers | grep "^%wheel"
%wheel  ALL=(ALL)       ALL
$ ansible-playbook -i inventory lineinfile_absent.yml 

PLAY [linux] ***************************************************************

TASK [lineinfile] **********************************************************
changed: [linux1]

PLAY RECAP *****************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ sudo cat /etc/sudoers | grep "^%wheel"
$ # 表示なし

無事に '^%wheel' にマッチする行が削除されました。


■ マッチしなかった場合に挿入したい箇所を指定して挿入する

Playbook

./test.txt 内に 、'^Listen ' にマッチする行があれば line の内容に置換し、マッチする行がなければ insertafter で指定する '^#Listen ' にマッチする行の次の行に Listen 8080 を挿入する Playbook です。

設定ファイル内にコメントアウトされた設定があるときに、その次の行に有効な設定値を挿入したい場合などに利用できます。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - lineinfile:
        path: ./test.txt
        regexp: '^Listen '
        insertafter: '^#Listen '
        line: 'Listen 8080'
  • insertafter オプション
    • regexp オプションで指定した正規表現にマッチする行がなかった場合、このオプションで指定した正規表現にマッチする行の「次の行」に line オプションの行を挿入します。
    • insertafter: EOF を指定した場合は、ファイルの最後の行に挿入します。
    • 似たオプションとして infertbefore オプションがあり、マッチする行の「前の行」に挿入します。

本タスクで指定していないその他のオプション例

  • firstmatch オプション(Ansible 2.5から利用可能)
    • insertafterinsertbefore オプションの正規表現で複数の行がマッチした場合、最初にマッチした行を基準とするかどうかを指定します。(デフォルト no

実行ログ

ここでは、#Listen 80 がある状態で Playbook を実行します。

$ cat ./test.txt 
abc
abc
#Listen 80
abc
abc
$ ansible-playbook -i inventory lineinfile_after.yml 

PLAY [linux] *******************************************************************

TASK [lineinfile] **************************************************************
changed: [linux1]

PLAY RECAP *********************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ cat ./test.txt
abc
abc
#Listen 80
Listen 8080
abc
abc

無事に '^#Listen ' にマッチする次の行に Listen 8080 が挿入されました。

なお、もし ./test.txt

abc
abc
Listen 80
abc
abc

のように regexp オプションで指定した '^Listen ' にマッチする行がある場合は、単純に置換されて以下のようになります。

abc
abc
Listen 8080
abc
abc


■ 正しく編集が行われるかを事前に検証する

Playbook

/etc/sudoers の内容を編集して保存する前に、/usr/sbin/visudo -cf コマンドでファイルのフォーマットを確認する Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - lineinfile:
        path: /etc/sudoers
        state: present
        regexp: '^%ADMIN ALL='
        line: '%ADMIN ALL=(ALL) NOPASSWD: ALL'
        validate: '/usr/sbin/visudo -cf %s'
  • validate オプション
    • 編集後のファイルの内容が正しいか検証するためのコマンドを指定します。対象を %s で指定します。

実行ログ

ここでは、SELINUX=disabled の状態で Playbook を実行します。

$ sudo cat /etc/sudoers | grep '^%ADMIN ALL='
$ 
$ ansible-playbook -i inventory lineinfile_validate.yml     
PLAY [linux] ****************************************************************

TASK [lineinfile] ***********************************************************
changed: [linux1]

PLAY RECAP ******************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
$ sudo cat /etc/sudoers | grep '^%ADMIN ALL='
%ADMIN ALL=(ALL) NOPASSWD: ALL

無事に /etc/sudoers の内容が編集されました。

なお、もし line: '%ADMIN ALL=(ALL) XXXXXXXx: ALL' のように無効なフォーマットを指定して Playbook を実行した場合は、以下のようなエラーメッセージが表示されて異常終了します。

TASK [lineinfile] *************************************************************************************
fatal: [linux1]: FAILED! => {"changed": false, "msg": "failed to validate: rc:1 error:>>> /tmp/tmpWQcMFk: syntax error near line 121 <<<\n"}

■ 応用編: チェックモードであらかじめ編集前後の diff を確認する

ansible-playbook コマンドの --check--diff オプションを一緒に利用すると、実際はファイルの内容を編集せずに、あらかじめ編集前後の diff を確認できます。

以下の Playbook で実行例を確認します。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - lineinfile:
        path: ./test.txt
        regexp: '^Listen '
        insertafter: '^#Listen '
        line: 'Listen 8080'
$ cat test.txt      # 対象ファイルの事前確認
abc
abc
#Listen 80
abc
abc

$ ansible-playbook -i inventory lineinfile_after.yml --diff --check 

PLAY [linux] **********************************************************

TASK [lineinfile] ******************************************************
--- before: ./test.txt (content)
+++ after: ./test.txt (content)
@@ -1,5 +1,6 @@
 abc
 abc
 #Listen 80
+Listen 8080
 abc
 abc

changed: [linux1]

PLAY RECAP *********************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ cat test.txt      # ファイルの内容は変更されていない
abc
abc
#Listen 80
abc
abc


■ まとめ

公式ドキュメントの使用例をベースにして、lineinfile モジュール の使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、lineinfile モジュールは Files modules に分類されています。Files modules には、他にも、指定した正規表現にマッチするすべての文字列を置換する replace や、行ではなくブロック単位で編集する blockinfile などのモジュールがあります。詳細は Files modules の一覧からご確認ください。

[Ansible] service モジュールの基本的な使い方(サービスの起動・停止・自動起動の有効化など)

■ はじめに

Ansible には、sytemctlservice コマンドなどによるサービスの管理(起動、停止、再起動削除など)をする service モジュール があります。

この記事では、 service モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ サービスが起動された状態にする

Playbook

apache httpd が起動された状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Start service httpd, if not started
      service:
        name: httpd
        state: started
  • name オプション
    • 対象のサービス名を指定します。(必須)
  • state オプション
    • name で指定したサービスどのような状態としたいかを指定します。
    • state: started を指定するとサービスが起動された状態になります。
      • 起動していない状態だった場合は、起動します。
        • systemctl start httpd のイメージ
      • 起動している状態だった場合は、何もしません。
      • start (起動そのものを指示)ではなく、started (起動された状態なっていることを指示)であることが、他のモジュール、オプションにも通じる特徴です。
      • 他に指定できる値(stopped など)については後述の使用例で説明します。

実行ログ

ここでは、サービスが起動していない状態で Playbook を実行します。

$ ansible-playbook -i inventory service_started.yml 

PLAY [linux] ***********************************************************************************

TASK [Start service httpd, if not started] *****************************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2019-02-24 08:04:47 UTC; 2s ago
   (...略...)

Feb 24 08:04:47 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 08:04:47 centos7 httpd[14442]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:04:47 centos7 systemd[1]: Started The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.

無事にサービスが起動された状態になりました。


■ サービスが停止された状態にする

Playbook

apache httpd が停止された状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Stop service httpd, if started
      service:
        name: httpd
        state: stopped
  • name オプション
    • 対象のサービス名を指定します。(必須)
  • state オプション
    • name で指定したサービスどのような状態としたいかを指定します。
    • state: stopped を指定するとサービスが起動された状態になります。
      • 起動している状態だった場合は、起動します。
        • systemctl stop httpd のイメージ
      • 起動していない状態だった場合は、何もしません。
  • enabled オプション(後述)
    • もし、自動起動を有効にしたい場合は、 enabled: yes も併用して指定します。本オプションの詳細は後述します。

実行ログ

ここでは、サービスが起動している状態で Playbook を実行します。

$ ansible-playbook -i inventory service_stopped.yml 

PLAY [linux] ***********************************************************************************

TASK [Stop service httpd, if started] **********************************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   
(ansible2780) [vagrant@centos7 service]$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:httpd(8)
           man:apachectl(8)

Feb 24 07:55:54 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 07:55:54 centos7 httpd[14291]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 07:55:54 centos7 systemd[1]: Started The Apache HTTP Server.
Feb 24 08:04:16 centos7 systemd[1]: Stopping The Apache HTTP Server...
Feb 24 08:04:17 centos7 systemd[1]: Stopped The Apache HTTP Server.
Feb 24 08:04:47 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 08:04:47 centos7 httpd[14442]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:04:47 centos7 systemd[1]: Started The Apache HTTP Server.
Feb 24 08:06:34 centos7 systemd[1]: Stopping The Apache HTTP Server...
Feb 24 08:06:35 centos7 systemd[1]: Stopped The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.

無事にサービスが停止された状態になりました。


■ サービスが再起動された状態にする

Playbook

apache httpd が再起動された状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Restart service httpd, in all cases
      service:
        name: httpd
        state: restarted
  • name オプション
    • 対象のサービス名を指定します。
  • state オプション
    • state: restarted を指定すると、再起動された状態になります。
      • 起動している状態だった場合は、再起動(停止、起動)します。
        • systemctl restart httpd のイメージ
      • 起動していない状態だった場合は、起動します。
        • systemctl restart httpd のイメージ

実行ログ

ここでは、サービスが起動している状態で Playbook を実行します。

$ ansible-playbook -i inventory service_restarted.yml 

PLAY [linux] ***********************************************************************************

TASK [Restart service httpd, in all cases] *****************************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

(ansible2780) [vagrant@centos7 service]$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2019-02-24 08:13:09 UTC; 4s ago
   (...略...)

Feb 24 08:13:09 centos7 systemd[1]: Stopped The Apache HTTP Server.
Feb 24 08:13:09 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 08:13:09 centos7 httpd[14645]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:13:09 centos7 systemd[1]: Started The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.

無事にサービスが再起動された状態になりました。(ログに StoppedStarting がある)

応用編: 再起動が必要な場合のみ再起動する

設定ファイルを変更した場合にのみ、サービスを再起動する、ということも指定できます。

以下の Playbook では、httpd.conf を管理対象ホスト側にコピーします。もし、そのコピーによって、httpd.conf が変更された場合(changed)だけ、handers 配下に定義された httpd_restarted というハンドラー(タスク)が呼び出され、サービスの再起動が実行されます。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: deploy httpd.conf
      copy:
        src: httpd.conf
        dest: /etc/httpd/conf/httpd.conf 
      notify: httpd_restarted   # handlers の httpd_restarted に対応

  handlers:
    - name: httpd_restarted  # notify の httpd_restarted に対応
      service:
        name: httpd
        state: restarted

以下に、2回分の実行ログを載せます。1回目は httpd.conf が変更されたため、ハンドラー経由でサービスが再起動しています。 続く2回目は、httpd.conf が前回実行分から変更されていないため、特に何もしていません。

$ ansible-playbook -i inventory service_restarted2.yml   # 1回目 (httpd.conf 変更あり)

PLAY [linux] ***********************************************************************************

TASK [deploy httpd.conf] ***********************************************************************
changed: [linux1]

RUNNING HANDLER [httpd_restarted] **************************************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=2    changed=2    unreachable=0    failed=0   

$ ansible-playbook -i inventory service_restarted2.yml  # 2回目 (httpd.conf 変更なし)

PLAY [linux] ***********************************************************************************

TASK [deploy httpd.conf] ***********************************************************************
ok: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=0    unreachable=0    failed=0


■ サービスのコンフィグが再読み込みされた状態にする

Playbook

apache httpd のコンフィグが再読み込みされた状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Reload service httpd, in all cases
      service:
        name: httpd
        state: reloaded
  • name オプション
    • 対象のサービス名を指定します。
  • state オプション
    • state: reloaded を指定すると、コンフィグが再読み込みされた状態になります。
      • 起動している状態だった場合は、realod します。
        • systemctl reload httpd のイメージ
      • 起動していない状態だった場合は、起動します。
        • systemctl start httpd のイメージ

実行ログ

ここでは、サービスが起動している状態で Playbook を実行します。

$ ansible-playbook -i inventory service_reloaded.yml 

PLAY [linux] ***********************************************************************************

TASK [Reload service httpd, in all cases] ******************************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2019-02-24 08:24:17 UTC; 7s ago
   (...略...)

Feb 24 08:24:17 centos7 systemd[1]: Stopped The Apache HTTP Server.
Feb 24 08:24:17 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 08:24:17 centos7 httpd[14978]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:24:17 centos7 systemd[1]: Started The Apache HTTP Server.
Feb 24 08:24:22 centos7 systemd[1]: Reloading The Apache HTTP Server.
Feb 24 08:24:22 centos7 httpd[15029]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:24:22 centos7 systemd[1]: Reloaded The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.

無事にサービスが reload された状態になりました。(ログに Reloaded がある)


■ サービスの自動起動が有効化された状態にする

Playbook

apache httpd自動起動が有効化された状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: Enable service httpd, and not touch the state
      service:
        name: httpd
        enabled: yes
  • name オプション
    • 対象のサービス名を指定します。
  • enabled オプション
    • state: restarted を指定すると、自動起動が有効化された状態になります。
      • 無効化されている状態だった場合は、有効化します。
        • systemctl enable httpd のイメージ
      • 有効化されている状態だった場合は、何もしません。
    • もし、enabled: no と指定した場合は、無効化された状態にします。(逆の動作)

実行ログ

ここでは、サービスの自動起動が無効化されている状態で Playbook を実行します。

$ ansible-playbook -i inventory service_enabled.yml  

PLAY [linux] ***********************************************************************************

TASK [Enable service httpd, and not touch the state] *******************************************
changed: [linux1]

PLAY RECAP *************************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

(ansible2780) [vagrant@centos7 service]$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2019-02-24 08:26:41 UTC; 8min ago
   (...略...)

Feb 24 08:26:41 centos7 systemd[1]: Starting The Apache HTTP Server...
Feb 24 08:26:41 centos7 httpd[15161]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:26:41 centos7 systemd[1]: Started The Apache HTTP Server.
Feb 24 08:26:45 centos7 systemd[1]: Reloading The Apache HTTP Server.
Feb 24 08:26:45 centos7 httpd[15175]: AH00558: httpd: Could not reliably determine the ser...age
Feb 24 08:26:45 centos7 systemd[1]: Reloaded The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.

無事にサービスの自動起動が有効化された状態になりました。(ログに httpd.service; enabled がある)


■ まとめ

公式ドキュメントの使用例をベースにして、service モジュール の使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、service モジュールは System modulesに分類されています。System modules には、他にもusertimezonehostnamefirewalld などのモジュールがあります。詳細は System modules の一覧からご確認ください。

[Ansible] yum モジュールの基本的な使い方(パッケージのインストールなど)

■ はじめに

Ansible には、yum によるパッケージの管理(インストール、更新、削除など)をする yum モジュール があります。

この記事では、 yum モジュールの公式ドキュメントに記載されている使用例をベースにして、使い方を説明します。

なお、公式ドキュメントの使用例は、Playbook 単位ではなくtask 単位で記載されています。この記事では Playbook 単位で例示します。

動作確認環境

  • Ansible 2.3.0, 2.7.8
  • CentOS 7.6 (Ansible 側、管理対象ホスト側とも)

目次


■ 最新バージョンのパッケージをインストールされた状態にする

Playbook

apache httpd の最新版をインストールされた状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: install the latest version of Apache
      yum:
        name: httpd
        state: latest
  • name オプション
    • 対象のパッケージ名を指定します。
  • state オプション
    • name で指定したパッケージをどのような状態としたいかを指定します。
    • state: latest を指定すると最新バージョンがインストールされた状態になります。
      • インストールされていない状態だった場合は、最新バージョンをインストールします。
        • yum install httpd のイメージ
      • 古いバージョンがインストールされている場合は、最新にアップデートします。
        • yum update httpd のイメージ
    • もし、state: present を指定した場合は、任意のバージョンがインストールされた状態という指定になります。

実行ログ

ここでは、インストールされていない状態で Playbook を実行します。

$ ansible-playbook -i inventory yum_latest.yml 

PLAY [linux] ***********************************************************************

TASK [install the latest version of Apache] ****************************************
changed: [linux1]

PLAY RECAP *************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ yum list installed | grep httpd
httpd.x86_64                         2.4.6-88.el7.centos        @base           
httpd-tools.x86_64                   2.4.6-88.el7.centos        @base 

無事に最新版がインストールされました。


■ 複数のパッケージをインストールされた状態にする

Playbook

gitwget をインストールされた状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: ensure a list of packages installed
      yum:
        name: "{{ packages }}"
      vars:
        packages:
          - git
          - wget
  • name オプション
    • 対象のパッケージ名を指定します。
    • 別途定義する変数 packages を指定しています。
    • 本タスクのようにリストを指定した場合は、1回の yum コマンドに対するオプションの指定になります。
      • 例えば yum install gityum install wget をそれぞれ実行するのではなく、yum install git wget を 1回実行。
    • 本タスクでは state オプションを省略しているので、実質 state: present 扱いになり、任意のバージョンがインストールされた状態という指定になります。
  • packages 変数
    • インストールされた状態としたいパッケージ名をリストで指定しています。
    • 変数名は任意で、name オプションの指定と整合性があれば問題ありません。

実行ログ

ここでは、いずれもインストールされていない状態で Playbook を実行します。

$ ansible-playbook -i inventory yum_multi.yml 

PLAY [linux] ***********************************************************************

TASK [install the latest version of Apache] ****************************************
changed: [linux1]

PLAY RECAP *************************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ yum list installed | grep git 
git.x86_64                           1.8.3.1-20.el7             @updates   
$ yum list installed | grep wget
wget.x86_64                          1.14-18.el7                @base           

無事に gitwget がインストールされました。


■ パッケージがインストールされていない状態にする

Playbook

apache httpd がインストールされていない状態にする Playbook です。

- hosts: linux
  gather_facts: no
  become: yes

  tasks:
    - name: remove the Apache package
      yum:
        name: httpd
        state: absent
  • name オプション
    • 対象のパッケージ名を指定します。
  • state オプション
    • state: absent を指定すると、インストールされていない状態になります。
      • インストールされている状態だった場合は、アンインストールします。
        • yum remove httpd のイメージ
      • インンストールされていない状態だった場合は何もしません

実行ログ

ここでは、httpd がインストールされている状態で Playbook を実行します。

$ ansible-playbook -i inventory yum_absent.yml 

PLAY [linux] *******************************************************************

TASK [remove the Apache package] ***********************************************
changed: [linux1]

PLAY RECAP *********************************************************************
linux1                     : ok=1    changed=1    unreachable=0    failed=0   

$ yum list installed | grep httpd
httpd-tools.x86_64                   2.4.6-88.el7.centos        @base  
$ 

無事に http がアンインストールされました。(httpd をインストールしたときに一緒にインストールした httpd-tools は残ります)


■ まとめ

公式ドキュメントの使用例をベースにして、yum モジュール モジュールの使い方を説明しました。

他に「こんなことできるかな?」と気になる事がありましたら、公式ドキュメントで詳細をご確認ください。

docs.ansible.com

また、yum モジュールは Packaging modulesに分類されています。Packaging modules には、他にも yum_repositorydnfapt などのモジュールがあります。詳細は Packaging modules の一覧からご確認ください。

[Ansible] 認証情報の変数名は ansible_user、ansible_password に統一したほうがよさそう

■ はじめに

Ansible で管理対象ホストのユーザー名やパスワードを定義する変数名はいくつかあります。

  • ユーザ名
    • ansible_ssh_user
    • ansible_user
  • パスワード
    • ansible_ssh_pass
    • ansible_pasword

みなさんはどのよいうに使い分けていますでしょうか。

先日たまたま、現在開発中の Ansible 2.8 の Porting Guide を眺めていたら、

  • ユーザー名とパスワードは ansible_useransible_pasword という変数名に統一しましょう
  • ansible_ssh_user のようなコネクションタイプ名が含まれる変数名は将来的に非推奨になるだろう

という旨の記述を見かけました。

Connection plugins have been standardized to allow use of ansible<conn-type>user and ansible<conn-type>password variables. Variables such as ansible<conn-type>pass and ansible<conn-type>username are treated with lower priority than the standardized names and may be deprecated in the future. In general, the ansible_user and ansible_password vars should be used unless there is a reason to use the connection-specific variables.

この記事では本件の経緯などをまとめます。

(以下ツイートの続きです)


■ 経緯

下記の issue で、 _pass_password などの複数の変数名が許容されているものは、ドキュメント上は標準化していったほうが良いのではという提案がありました。

github.com

対するプルリクは以下のものです。

github.com

ドキュメントやログメッセージに含まれる変数名が以下のように変更されていることが、変更の差分で確認できます。

  • ansible_ssh_user から ansible_user
  • ansible_ssh_pass から ansible_password
  • ansible_become_pass から ansible_become_password


■ そもそもどのような変数名が使えるの?

もし既存の Playbook や 変数名の中から認証情報に関する変数名を洗い出して統一するために修正する場合、そもそもどのような変数名が使えるのかを知る必要があります。その場合は、各コネクションプラグイン最新版Doc開発中Doc)の説明ページで確認できます。

例えばネットワークモジュール向けのひとつ、 network_cli コネクションプラグイン (最新版Doc開発中Doc) であれば、パスワードとして ansible_ssh_passansible_password を利用できることが確認できます。


■ まとめ

Ansible で管理対象ホストのユーザー名やパスワードを定義する変数名は、標準化、将来性のため ansible_useransible_password に統一したほうが良さそうです。