zaki work log

作業ログやら生活ログやらなんやら

[Ansible] 環境変数の利用についておさらい

本記事は「Ansible Advent Calendar 2022」の19日目のエントリとなります。
Ansibleで環境変数を参照したりタスク実行時にセットしたりする方法についておさらい。

おさらいして整理してると気付けたけど、「ローカルか、リモートか」でなく、「Ansible本体の実行環境か、マネージドノードのタスクの実行環境か」という区切りで考えるのがポイント。(環境変数に限らず、だけど)

環境変数の参照

コントロールノード(ローカル)の環境変数を参照

大前提として「ルックアップ(lookup)プラグインはコントロールノードで処理される」という仕様があります。もう少し細かくいうと、(対象がローカルの場合を含むマネージドノード上の)タスクを実行するタイミングでなく、(CLIであれば)ansible-playbookを実行するノードで処理されます。
このルックアッププラグインの1つで環境変数を参照できるenvルックアッププラグインを使えばタスク実行時などにローカルの環境変数を参照できます。

環境変数zzzをprintするサンプルは以下。

---
- hosts: server

  tasks:
  - debug:
      msg: "{{ lookup('env', 'zzz') }}"

環境変数zzzhelloをセットした実行例が以下。
(実行ノードのawx.jp-z.jpはリモートです)

$ export zzz=hello
$ ansible-playbook -i inventory.ini print-env-sample.yml 

PLAY [server] ***************************************************************

TASK [debug] ****************************************************************
ok: [awx.jp-z.jp] => {
    "msg": "hello"
}
ok: [localhost] => {
    "msg": "hello"
}

ルックアッププラグイン自体の説明はこちら

docs.ansible.com

envルックアッププラグイン

docs.ansible.com

マネージドノード(リモート)の環境変数を参照

ノードのfacts変数のenvを参照します。具体的にはansible_facts.envに取得できた環境変数が全てセットされています。(あるいは以前からの書式でansible_env)
facts変数で環境変数を収集するには、gather_factsを使用します。(デフォルト有効)
gather_subsetで収集する範囲を限定する場合でも、環境変数min指定でもOKの模様。(明示的にenvも指定可能)

環境変数USERをprintするサンプルは以下。

---
- hosts: server
  gather_facts: true
  gather_subset: min

  tasks:
  - debug:
      msg: "{{ ansible_facts.env.USER }}"

ansible_connectionでコネクションプラグインlocalを使ってローカル実行のUSERzaki、リモートはubuntuという環境での実行例。
以下はデフォルトの状態で実行。ローカル(ansible-playbookを実行しているノード)はユーザーzakiで、リモートのawx.jp-z.jpの実行ユーザーはubuntuとなっている。

$ ansible-playbook -i inventory.ini print-env-sample.yml 

PLAY [server] ***************************************************************

TASK [Gathering Facts] ******************************************************
ok: [localhost]
ok: [awx.jp-z.jp]

TASK [debug] ****************************************************************
ok: [awx.jp-z.jp] => {
    "msg": "ubuntu"
}
ok: [localhost] => {
    "msg": "zaki"
}

ここで、実行ノードでexportを使って環境変数USERを侑ちゃんに設定して実行してみると以下の通り。
ローカル実行のタスクのみ内容が更新されており、ansible_factsで収集したリモートの環境変数には影響してないことを確認できます。

$ export USER=yu-chan
$ ansible-playbook -i inventory.ini print-env-sample.yml 

PLAY [server] ***************************************************************

TASK [Gathering Facts] ******************************************************
ok: [localhost]
ok: [awx.jp-z.jp]

TASK [debug] ****************************************************************
ok: [awx.jp-z.jp] => {
    "msg": "ubuntu"
}
ok: [localhost] => {
    "msg": "yu-chan"
}

ただしexportを使ったローカルのこの動作は「コネクションプラグインlocalを指定した場合」の話。sshを使ってローカルホストに接続した場合は「exportを使って使用中のシェルに一時的に設定した環境変数」は維持されないため、ログイン時にセットされるデフォルトの環境変数となります。

上記例ではプレイでfacts変数を収集するgather_factsを指定していますが、1つのタスクとしてfacts変数を収集することも可能。

zaki-hmkc.hatenablog.com

環境変数の指定

タスクの実行時にモジュールで使用する値として環境変数を設定するにはenvironmentディレクティブを使用します。
指定方法は以前作成した以下も参照。

zaki-hmkc.hatenablog.com

全タスクで使用するにはプレイに、タスク単体で使用するには(環境変数を使用するのが一つのタスクのみ、等)タスクと、要件にあわせて指定箇所を変更してスコープを調整できます。
記述例として、環境変数zzzの値としてhelloをプレイにセットする場合は以下の通り。

- hosts: server
  environment:
    zzz: hello

実行例

---
- hosts: server
  gather_facts: true
  gather_subset:
    - min
  environment:
    zzz: hello

  tasks:
  - debug:
      msg: "{{ lookup('env', 'zzz') }}"

  - debug:
      msg: "{{ ansible_facts.env.zzz }}"

実行結果は以下の通り。

TASK [debug] ****************************************************************
ok: [awx.jp-z.jp] => {
    "msg": ""
}
ok: [localhost] => {
    "msg": ""
}

TASK [debug] ****************************************************************
ok: [awx.jp-z.jp] => {
    "msg": "hello"
}
ok: [localhost] => {
    "msg": "hello"
}

environmentディレクティブは「タスク実行の際に環境変数を付与」する動きになるため、マネージドノード上でのタスクの実行元となるコントロールノードのAnsibleプロセスの実行には影響しない。そのため、ルックアッププラグインでは参照できないのが確認できます。(説明が難しい…伝われ…)


使いどころとしては、クラウドサービスのアクセスキーとシークレットキーや、AAPのようなミドルウェアの接続情報など、複数タスクで共通して使用する値。モジュールのパラメタだけでなく環境変数を使用することで、プレイブックの記述量を減らして可読性を上げることができます。

- hosts: aap
  remote_user: ec2-user
  gather_facts: false
  environment:
    CONTROLLER_USERNAME: admin
    CONTROLLER_PASSWORD: "{{ password }}"
    CONTROLLER_VERIFY_SSL: false

使用するモジュールで環境変数が使用可能どうかはまとまった情報があるわけでなく、各モジュールのパラメタ等に「環境変数でも指定可能」のように書かれているのを確認する。(しかない、と思う)
例えばAAPやAWXのホスト情報を編集するawx_hostモジュール(というかAWX関連の各モジュール)は、パラメタ毎に以下のような記載がある。

If value not set, will try environment variable CONTROLLER_HOST and then config files

controller_host

https://docs.ansible.com/ansible/latest/collections/awx/awx/host_module.html

AWSであればガイドのAuthenticationの項に以下の記載あり。

Authentication with the AWS-related modules is handled by either specifying your access and secret key as ENV variables or module arguments.

For environment variables:

export AWS_ACCESS_KEY_ID='AK123'
export AWS_SECRET_ACCESS_KEY='abc123'

参考

Ansibleドキュメント

docs.ansible.com

docs.ansible.com

docs.ansible.com

過去ブログ

zaki-hmkc.hatenablog.com

まとめ

  • コントロールノードの環境変数を参照するにはenvルックアッププラグイン
  • マネージドノードの環境変数を参照するにはfacts変数を収集
  • タスクに環境変数をセットするにはenvironmentディレクティブ
  • ルックアッププラグインが作用するのはAnsibleを実行するコントロールノード
  • environmentディレクティブが作用するのはタスクを実行するマネージドノード

昔よくやってた(今でも多分やってるかも)けど、コントロールノードの実行ユーザーとマネージドノードのユーザーが同一だと、「処理対象ノードのホームディレクトリ以下の〇〇で□□するタスク」とかで、ついenvルックアッププラグイン使ってホームディレクトリを取得してちゃんと動いたように見えてたけど、あれ本当は誤りなんだよね。。