テックブログ

技術ネタ

Ansibleでオプションのデフォルト値を利用して応用の幅を持たせる

こんにちは!當間です。今年の夏は暑いですね……

そんな気温に負けないようなアツい Ansible Tips を今回も紹介していきます!

前回までのリンク

  1. Ansible 2.0 導入
  2. Ansibleでユーザ追加
  3. Ansibleでユーザパスワード設定
  4. Ansibleで楽にパスワード設定
  5. Ansibleで複数ユーザ追加と鍵作成

今回はオプションとデフォルト値についてです。
復習も兼ねて、最小設定から見ていきましょう。
第2回の記述のように、userモジュールはnameオプションのみが必須となっています。


tasks:
  - name: Add User
    user:
      name: "hoge"

複数ユーザ作成ができるようにリスト形式を利用して書くと次の通り

tasks:
  - name: Add Users
    user
      name: "{{ item }}"
    with_items:
      - hoge
      - fuga

実際にユーザを利用する際はpasswordオプションの指定もするので、 dict形式でリスト化します。

tasks:
  - name: Add Users
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
    with_items:
      - { name: hoge, pw: "hoge-pw" }
      - { name: fuga, pw: "fuga-pw" }

昨今のSSH利用状況ではパスワード認証のみだと弱いため、generate_ssh_key: yesを指定してssh-key設定も行いたい所です。

tasks:
  - name: Add Users
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
    with_items:
      - { name: hoge, pw: "hoge-pw" }
      - { name: fuga, pw: "fuga-pw" }

更にssh-keyファイルにもssh_key_passphraseオプションでパスフレーズを掛けておくことで、keyファイル流出時にも悪用されないよう対策する事にしましょう。これで完璧ですね!

tasks:
  - name: Add Users
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
      ssh_key_passphrase: "{{ item.key_pass }}"
    with_items:
      - { name: hoge, pw: "hoge-pw", key_pass: "hoge-ssh-key-pass" }
      - { name: fuga, pw: "fuga-pw", key_pass: "fuga-ssh-key-pass" }

ところが、一部の自動化利用ユーザからすると、接続時にパスフレーズ入力待ちで止まってしまう状況は好ましいものではありません。

回避策としてssh-agentを利用してパスフレーズを一時的に記憶させておく事も可能ですが、シェル毎に起動する為、重複処理の調整等に手間取る事が多く、要望としてパスフレーズ無しの鍵ファイルを利用したいという声があがりました。

安易な解決策としてはパスフレーズ有り・無しのタスクを用意してused_ssh_key_pass: True/False という変数次第でどちらかを実行させるという方法です。

vars:
  ssh_users:
    - { name: hoge, pw: "hoge-pw", key_pass: "hoge-ssh-key-pass", used_ssh_key_pass: True }
    - { name: fuga, pw: "fuga-pw", key_pass: "fuga-ssh-key-pass", used_ssh_key_pass: False }
tasks:
  - name: パスフレーズ有りのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
      ssh_key_passphrase: "{{ item.key_pass }}"
    with_items: "{{ ssh_users }}"
    when: (used_ssh_key_pass)

  - name: パスフレーズ無しのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
    with_items: "{{ ssh_users }}"
    when: (not used_ssh_key_pass)

今回の場合、ユーザ名hogeはパスフレーズ有り、ユーザ名fugaはパスフレーズ無しのユーザ作成タスクが実行されます。
もうちょっと考えて、used_ssh_key_passという変数を増やさなくてもkey_passが定義されているかどうかで分岐する方法を考えました。

vars:
  ssh_users:
    - { name: hoge, pw: "hoge-pw", key_pass: "hoge-ssh-key-pass" }
    - { name: fuga, pw: "fuga-pw" }
tasks:
  - name: パスフレーズ有りのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
      ssh_key_passphrase: "{{ item.key_pass }}"
    with_items: "{{ ssh_users }}"
    when: (item.key_pass is defined)

  - name: パスフレーズ無しのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: yes
    with_items: "{{ ssh_users }}"
    when: (item.key_pass is not defined)

似たようなタスクが2件あって不格好ですがこれで要望は満たせるようになりました。

しかし新たに、「ユーザのホームディレクトリを指定したい」という要望があがりました。
確かにhomeオプションで指定する事は可能ですが、パスフレーズ有無と同じように分岐させると4パターンのタスクを書く必要があります。

  • パスフレーズ有り、HOMEデフォルトのユーザ作成
  • パスフレーズ無し、HOMEデフォルトのユーザ作成
  • パスフレーズ有り、HOME指定のユーザ作成
  • パスフレーズ無し、HOME指定のユーザ作成

他にも、ログインシェルを指定したい、所属グループを指定したい等の要望が今後発生しないとも限りません。
オプション指定で設定は可能ですが要素の階上分パターンが増えるので、16パターンもの似たようなタスクを書いてメンテしていくのはとても大変です。
そこで各要素を変数にして、ユーザ毎に定義する事でタスクの数を減らしてみました。

tasks:
  - name: 色んなパターンのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: "{{ item.create_key }}"
      ssh_key_passphrase: "{{ item.key_pass }}"
      home: "{{ item.home }}"
      shell: "{{ item.shell }}"
    with_items: "{{ ssh_users }}"
      - { name: hoge, pw: "hoge-pw", create_key: "yes", key_pass: "hoge-ssh-key-pass", home: "/home/hoge",         shell: "/bin/bash" }
      - { name: fuga, pw: "fuga-pw", create_key: "yes", key_pass: "",                  home: "/var/www/html/fuga", shell: "/bin/zsh"  }
      - { name: piyo, pw: "piyo-pw", create_key: "no",  key_pass: "",                  home: "/home/piyo",         shell: "/bin/bash" }

上記実行すると次のユーザが作成されます
– hogeユーザ: パスフレーズ有り、HOMEデフォルト
– fugaユーザ: パスフレーズ無し、HOME指定、ログインシェルもzshに変更
– piyoユーザ: ssh-key自体を作成せず、他はデフォルト

タスクの数が減った代わりに、ユーザ毎の変数指定が増えてしまいました。
しかしこれもdefault()フィルターを利用する事で更に記述を少なくする事が可能です。

tasks:
  - name: 色んなパターンのユーザ作成
    user:
      name: "{{ item.name }}"
      password: "{{ item.pw | password_hash('sha512') }}"
      generate_ssh_key: "{{ item.create_key | default('yes') }}"
      ssh_key_passphrase: "{{ item.key_pass | default(omit) }}"
      home: "{{ item.home | default(omit) }}"
      shell: "{{ item.shell | default(omit) }}"
    with_items: "{{ ssh_users }}"
      - { name: hoge, pw: "hoge-pw", key_pass: "hoge-ssh-key-pass" }
      - { name: fuga, pw: "fuga-pw", home: "/var/www/html/fuga", shell: "/bin/zsh"  }
      - { name: piyo, pw: "piyo-pw", create_key: "no" }
      - { name: bar, pw: "bar-pw" }

default()は対象値が未定義のデフォルト動作を指定するフィルターです。
generate_ssh_keyのデフォルト値は元々’no’ですが、上記ではデフォルト値を’yes’にしています。
これにより、ユーザ変数でcreate_keyが指定されてない piyo以外のユーザは、yesが入るようになります。

default(omit)は値が指定されてない場合、そのオプション自体を省略(記述なし)とします。
homeオプションに指定すると、指定がない場合システムのデフォルトに沿った形で/home/(ユーザ名)となります。
これを利用してuserモジュールの各オプションから変更したい部分のみユーザ毎に定義する事ができます。

このタスクでは最小の定義がbarユーザのようにnameとpwの2点だけになります。
少しずつ動きの違うタスクをwhenで条件判断させて沢山書くよりは大分スマートになったのではないでしょうか。

Ansibleには他にも色々なJinja2フィルターが利用できます。

文字列を大文字にしたり、JSON形式へ変換したり、IP形式になっているかチェックしたり、PATHからファイル名だけ抜き出したり……

その変数に指定値が含まれているかチェックするregex_searchや置換するregex_replace等のフィルターが使えると非常に便利ですが、正規表現を利用するので気をつけないと見通しの悪い黒魔術的Playbookになってしまいます。

今回のdefault(omit)は、書くのも読み解くのも簡単で応用効果は絶大な点が気に入っており、よく利用するフィルターの1つです。

userモジュール以外でも各オプションとそのデフォルト値を利用すればPlaybookをよりスマートに記述できるので、どんどん活用して活用の幅を持たせていきましょう!

実績数30,000件!
サーバーやネットワークなど
ITインフラのことならネットアシストへ、
お気軽にご相談ください