Puppet使いがAnsibleの使い方まとめてみた

日本においてインフラ自動化界隈は完全にAnsible勝利で確定の様相で、長らくPuppet使ってきた者としては寂しい限り:sob:ではありますが、それはそれ、これはこれというコトで、Puppet使いの観点からAnsibleの公式ドキュメントをベースにAnsibleの使い方をまとめてみました。

※サンプルは自作していますが、後半のよく使うモジュールのところでは公式ドキュメントのサンプルをコピーし、リファレンス先のリンクを提示しています。

# Ansbileのインストール
インストールは以下のコマンドラインになる。
```
$ sudo yum install ansible
```
RHEL 7 および RHEL 8 の RPM は、Ansible Engine リポジトリー から入手できる。

RHEL 8 用の Ansible Engine リポジトリーを有効にするには、以下のコマンドを実行。
```
$ sudo subscription-manager repos --enable ansible-2.9-for-rhel-8-x86_64-rpms
```
RHEL 7 用の Ansible Engine リポジトリーを有効にするには、以下のコマンドを実行。
```
$ sudo subscription-manager repos --enable rhel-7-server-ansible-2.9-rpms
```
# インベントリを作る
/etc/ansible/hostsを定義する。ここで定義されないホストはアクセスできない。

```
all:
  hosts:
    ansible:
    tgt:

  children:
    masters:
      hosts:
        ansible:
```
対象のホストはssh鍵による認証ができるようになっているのが推奨される。もしパスワードを都度確認させるのであれば「--ask-pass」オプションを使う。

# コマンドラインで使う

「-m」オプションでモジュール名、「-a」オプションで属性値を指定して直接実行が可能。
```
ansible [host] -m copy -a "src=/etc/hosts dest=/tmp/hosts"
```
同様のことが「ansible-console」で対話的にできる。

# playbookを使う

playbookはYAML形式の実行タスクを記述したファイル。Puppetでいうマニフェスト。playbookの実行は以下のコマンドライン。
```
ansible-playbook [playbook]
```
playbookは以下のように記述する。

```
- hosts: all
  remote_user: root

  tasks:
    - shell: cat /etc/hosts
      register: result
    - debug:
        msg: '{{ result.stdout }}'
```
複数行にまたがる定義も可能。例えば複数行定義されたシェル実行もできる。

```
- hosts: all
  remote_user: root

  tasks:
    - shell: |
        ls -l /
        ls -l /etc
        ls -l /home
      register: result
    - debug:
        msg: '{{ result.stdout }}'
```
playbookの文法チェックは以下のコマンドライン。

```
# ansible-playbook --syntax-check test.yaml
```
以下のコマンドラインでタスク一覧を確認できる。

```
# ansible-playbook --list-tasks test7.yaml
```
以下のコマンドラインでドライランできる。puppetの「--noop」と同じ。

```
# ansible-playbook --check test.yaml
```
実行していないためコマンド実行の返り値を使っている場合など不具合が出るので注意。

# 変数

playbookで変数が利用可能。Puppet Languageみたく再代入禁止ではない。

## 変数の定義
「vars」で定義可能。
```
- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22
```
ハッシュの場合は「[]」で指定する他、「.」で階層を区切ることもできれる。
以下は同値：

```
{{ ansible_facts['devices']['xvda']['model'] }}
```
```
ansible_facts.devices.xvda.model
```
## 外部ファイルからの変数定義の読み込み
「vars_files」で変数を定義したファイルを指定が可能。hieraみたいなものですね。

```
- hosts:
    - ansible
  gather_facts: no

  vars:
    num1: "abcd"
  vars_files:
    - "common.yaml"
```

## 対話的な変数値の入力

実行時に変数の値をコマンドラインから入力させることができる。

```
- hosts: all
  vars_prompt:
    - name: username
      prompt: "What is your username?"
      private: no

    - name: password
      prompt: "What is your password?"

  tasks:
    - debug:
        msg: 'Logging in as {{ username }}'
```

## 外部ソースからの変数値の読み込み

外部のファイルやデータベースから読み込むことができる。

```
- hosts: ansible
  gather_facts: no

  vars:
    motd_value: "{{ lookup('file', '/etc/hostname') }}"

  tasks:
    - debug: msg="hostname value is {{ motd_value }}"
```

lookupでアクセスできるソースは以下のコマンドで確認できる。

```
# ansible-doc -t lookup -l
```

## 変数定義の暗号化

パスワードなどプレインテキストではセキュリティ上の問題があるケースにおいては、ansible-vaultを使い定義内容を暗号化できる。

「vault-id」でパスワード読み込みのソースを指定できる。ここではコマンドラインからの入力「@prompt」を指定している。

key「encname」、value「secret_string」を暗号化するコマンドラインは以下の通り。

```
# ansible-vault encrypt_string --vault-id @prompt "secret_string"  --name "encname"

New vault password (default):
Confirm new vault password (default):
encname: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66626261376661333233333732306334356135646637616463656666303432336565343633336631
          3066646135636331396637323266656135643638333265350a326232396531633962653934376164
          66643663656432376264653534313665363664613436636335663038306164353031626338626533
          3335353634306165370a616134353034346634643264366135366532323030336665663832396165
          3332
Encryption successful
```

ここで生成された暗号化文字列を以下のようにして変数定義に組み込む。「!valut」が必要になる。

```
- hosts: all

  vars:
    encname: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66626261376661333233333732306334356135646637616463656666303432336565343633336631
          3066646135636331396637323266656135643638333265350a326232396531633962653934376164
          66643663656432376264653534313665363664613436636335663038306164353031626338626533
          3335353634306165370a616134353034346634643264366135366532323030336665663832396165
          3332

  tasks:
    - debug: var=encname
```

実行時は「--vault-id」オプションを設定する。ここでは「--vault-id @prompt」指定により、パスワードをコマンドラインから入力させている。

```
# ansible-playbook --vault-id @prompt test.yaml
```

# システム情報の取得

Ansible factsを使ってシステム情報を取得できる。これもpuppetと同じ。取得可能なfactsは以下のコマンドラインで参照できる。

```
# ansible <hostname> -m setup
```

```
{{ ansible_facts['devices']['xvda']['model'] }}
```
ここで取得できる情報はplaybook内の変数として予め定義されているため、そのまま利用できる。ただし以下のように「gather_facts: no」を定義していると情報は入っていない。

```
- hosts: all
  gather_facts: no
```

# ループ構造（loop）

「loop」でループ構造を作れる。

```
- hosts: all

  tasks:
    - debug: msg="loop variable is {{ item }}"
      loop:
        "{{ [0,2,4,6,8] }}"

    - debug: msg="loop variable is {{ item.name }} , {{ item.group }}"
      loop:
        - { name: 'user1', group: 'ug1' }
        - { name: 'user2', group: 'ug2' }
```

この場合、変数itemにループの値が入っている。

# 別のホストでタスクを実行（delegate）

「delegate_to」で指定したホストで当該のタスクを実行する。
負荷分散装置への登録などで応用できる。

```
- hosts: ansible
  gather_facts: no
  tasks:
    - name: test shell
      shell: |
        echo "test"
        cat /etc/hostname
      register: results
      delegate_to: tgt
    - debug: msg="{{ results.stdout }}"
```

# 条件分岐/ブロック

「when」を使ってif的な記述が可能。
「block」を使うと複数のタスクを一つのブロックとして扱える。
「rescue」でブロックの中でエラーが発生した場合の例外処理が定義できる。
「always」でブロックの処理を抜けた際に必ず実行するタスクを定義できる。

```
- hosts: all

  vars:
    num1: "abcd"
  vars_files:
    - "common.yaml"

  tasks:
    - block:
      - debug: msg="{{ num1 }}"
      - debug: msg="NG"
      - shell: "/bin/true"
      when: num2 is undefined
      rescue:
        - debug: msg="failed."
      always:
        - debug: msg="always"

    - name: set fact
      ansible.builtin.set_fact:
        num2: 123

    - debug: msg="{{ num2 }}"
```

# ロール

## ロールの構造

ロールを定義するには以下のようなディレクトリ構造を作る。PuppetのRole-Profileモデルと同様の考えでOK。

```
site.yml
webservers.yml
fooservers.yml
roles/
    common/
        tasks/
        handlers/
        library/
        files/
        templates/
        vars/
        defaults/
        meta/
    webservers/
        tasks/
        defaults/
        meta/
```

各ディレクトリの役割は以下の通り。

```
tasks/main.yml       - the main list of tasks that the role executes.
handlers/main.yml    - handlers, which may be used within or outside this role.
library/my_module.py - modules, which may be used within this role.
defaults/main.yml    - default variables for the role.
vars/main.yml        - other variables for the role.
files/main.yml       - files that the role deploys.
templates/main.yml   - templates that the role deploys.
meta/main.yml        - metadata for the role, including role dependencies.
```

「tasks」「vars」「files」「templates」はよく使う。「handlers」は通知タスクを別だしにしておくのであれば、「defaults」は外部から値が指定されない場合のデフォルト値をまとめるのであれば定義する。「defulats」で定義された変数値は「vars」で同じ変数名の定義があると「vars」の内容で上書きされる。

## ロール定義のサンプル

サンプルのロール定義を以下に示す。

```
# tree --charset=C
.
|-- roles
|   |-- apache
|   |   |-- defaults
|   |   |   `-- main.yaml
|   |   |-- tasks
|   |   |   |-- apache.yaml
|   |   |   `-- main.yaml
|   |   `-- vars
|   |       `-- main.yaml
|   |-- common
|   |   `-- tasks
|   |       |-- main.yaml
|   |       `-- rhel.yaml
|   `-- tomcat
|       |-- files
|       |   `-- testfile.txt
|       |-- tasks
|       |   |-- main.yaml
|       |   `-- tomcat.yaml
|       |-- templates
|       |   `-- testtmpl.j2
|       `-- vars
|           `-- main.yaml
`-- test.yaml

12 directories, 12 files
```

トップレベルのyaml定義の例：

```
- hosts: ansible
  roles:
    - common
    - apache
    - tomcat
```

変数定義の例：

```
# cat vars/main.yaml
tomcat_user: "denka"
```

デフォルト値定義の例：

```
# cat apache/defaults/main.yaml

http_user: "denka"
http_pass: "hogehoge"
http_port: 8080
```

タスク定義の例：

```
# cat tomcat/tasks/main.yaml

- name: tomcat role test
  import_tasks: tomcat.yaml
```

インポートタスクの定義例：

```
# cat tomcat/tasks/tomcat.yaml

- name: tomcat tasks
  debug: msg="called Tomcat tasks"

- name: file copy task
  copy:
    src: testfile.txt
    dest: "/tmp/testfile.txt"

- name: template task
  template:
    src: testtmpl.j2
    dest: "/tmp/template.txt"
```

## ロール構造のベストプラクティス

インストール（install.yaml）、設定（config.yaml）、サービス定義（service.yaml）に分ける。
main.yamlにはこれらのインポートだけを記述する。

```
|-- roles
|   |-- apache
|   |   |-- tasks
|   |   |   |-- config.yaml
|   |   |   |-- install.yaml
|   |   |   |-- main.yaml
|   |   |   `-- service.yaml
```

main.yaml：

```
- name: install apache
  import_tasks: install.yaml

- name: configure apache
  import_tasks: config.yaml

- name: service configure
  import_tasks: service.yaml
```

install.yaml：

```
- name: install httpd package
  package:
    name: httpd
    state: present
```

config.yaml：

```
- name: configure httpd
  debug: msg="nothing to do at this moment"
```

service.yaml：

```
- name: configure httpd service
  service:
    name: httpd
    state: started
    enabled: yes
```

# テンプレートの利用

テンプレートはtemplateモジュールを利用して実現する。タスクは以下のように定義する。

```
- name: template task
  template:
    src: testtmpl.j2
    dest: "/tmp/template.txt"
```

Jinja2テンプレートは以下のように変数を埋め込める。

```
Hello, {{ tomcat_user }}!!
```

# includeとimport

include_\*はタスク実行時に動的読み込み、import_\*はタスク実行開始時に静的読み込みする。タグやループ構造などで挙動が異なるので注意。

# タグ

タスクやブロックに１つ以上のタグをつけて、playbook実行時に特定のタグを持つタスクやブロックのみ実行することができる。

```
- hosts: all

  tasks:
    - name: test1
      debug: msg='test1'
      tags: always
    - name: test2
      debug: msg='test2'
      tags: test3
    - name: test3
      include_tasks:
        file: test13_imp.yaml
      tags: test3
```

「always」タグを振ると常に実行対象となる。
「never」タグを振ると常に実行されない。デバッグコードなどで使うと良い。

実行時はコマンドラインでタグを指定する。

```
# ansible-playbook --tag=test3 test.yaml
```

任意のタグを持つタスクを指定して実行できるが、エラーが発生して中断してしまったなど、途中から実行したい場合は「--start-at」オプションを使う

```
ansible-playbook -i <hosts> <playbook.yml> --start-at ’<task_name>’
```

動的読み込みのinclude_taskの場合、タグの情報は継承されないため、読み込み対象のタスクには適切なタグが必要。ただし以下のようにapplyでincludeされるタスクに継承させることもできる。

```
- name: Apply the db tag to the include and to all tasks in db.yaml
  include_tasks:
    file: db.yml
    # adds 'db' tag to tasks within db.yml
    apply:
      tags: db
  # adds 'db' tag to this 'include_tasks' itself
  tags: db
```

なお静的読み込みのimport_taskの場合は何もしなくてもタグは継承される。

# よく使うモジュール

## fileモジュール

ファイルの作成、削除など。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/file_module.html)

```
- name: Change file ownership, group and permissions
  file:
    path: /etc/foo.conf
    owner: foo
    group: foo
    mode: '0644'

- name: Create an insecure file
  file:
    path: /work
    owner: root
    group: root
    mode: '1777'

- name: Create a symbolic link
  file:
    src: /file/to/link/to
    dest: /path/to/symlink
    owner: foo
    group: foo
    state: link

- name: Create two hard links
  file:
    src: '/tmp/{{ item.src }}'
    dest: '{{ item.dest }}'
    state: link
  with_items:
    - { src: x, dest: y }
    - { src: z, dest: k }

- name: Touch a file, using symbolic modes to set the permissions (equivalent to 0644)
  file:
    path: /etc/foo.conf
    state: touch
    mode: u=rw,g=r,o=r

- name: Touch the same file, but add/remove some permissions
  file:
    path: /etc/foo.conf
    state: touch
    mode: u+rw,g-wx,o-rwx

- name: Touch again the same file, but dont change times this makes the task idempotent
  file:
    path: /etc/foo.conf
    state: touch
    mode: u+rw,g-wx,o-rwx
    modification_time: preserve
    access_time: preserve

- name: Create a directory if it does not exist
  file:
    path: /etc/some_directory
    state: directory
    mode: '0755'

- name: Update modification and access time of given file
  file:
    path: /etc/some_file
    state: file
    modification_time: now
    access_time: now

- name: Set access time based on seconds from epoch value
  file:
    path: /etc/another_file
    state: file
    access_time: '{{ "%Y%m%d%H%M.%S" | strftime(stat_var.stat.atime) }}'

- name: Recursively change ownership of a directory
  file:
    path: /etc/foo
    state: directory
    recurse: yes
    owner: foo
    group: foo
```

## copy

ファイルのコピー。role定義で用意しているファイルの配置も含む。roleの場合、srcのルートはrole定義のfilesディレクトリとなる。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/copy_module.html)


```
- name: Copy file with owner and permissions
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: '0644'

- name: Copy file with owner and permission, using symbolic representation
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: u=rw,g=r,o=r

- name: Another symbolic mode example, adding some permissions and removing others
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: u+rw,g-wx,o-rwx

- name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
  copy:
    src: /mine/ntp.conf
    dest: /etc/ntp.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes

- name: Copy a new "sudoers" file into place, after passing validation with visudo
  copy:
    src: /mine/sudoers
    dest: /etc/sudoers
    validate: /usr/sbin/visudo -cf %s

- name: Copy a "sudoers" file on the remote machine for editing
  copy:
    src: /etc/sudoers
    dest: /etc/sudoers.edit
    remote_src: yes
    validate: /usr/sbin/visudo -cf %s

- name: Copy using inline content
  copy:
    content: '# This file was moved to /etc/other.conf'
    dest: /etc/mine.conf

- name: If follow=yes, /path/to/file will be overwritten by contents of foo.conf
  copy:
    src: /etc/foo.conf
    dest: /path/to/link  # link to /path/to/file
    follow: yes

- name: If follow=no, /path/to/link will become a file and be overwritten by contents of foo.conf
  copy:
    src: /etc/foo.conf
    dest: /path/to/link  # link to /path/to/file
    follow: no
```

## template

テンプレートを使ってファイルを作成する。
roleの場合、srcのルートはrole定義のtemplatesディレクトリとなる。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/template_module.html)

```
- name: Template a file to /etc/files.conf
  template:
    src: /mytemplates/foo.j2
    dest: /etc/file.conf
    owner: bin
    group: wheel
    mode: '0644'

- name: Template a file, using symbolic modes (equivalent to 0644)
  template:
    src: /mytemplates/foo.j2
    dest: /etc/file.conf
    owner: bin
    group: wheel
    mode: u=rw,g=r,o=r

- name: Copy a version of named.conf that is dependent on the OS. setype obtained by doing ls -Z /etc/named.conf on original file
  template:
    src: named.conf_{{ ansible_os_family}}.j2
    dest: /etc/named.conf
    group: named
    setype: named_conf_t
    mode: 0640

- name: Create a DOS-style text file from a template
  template:
    src: config.ini.j2
    dest: /share/windows/config.ini
    newline_sequence: '\r\n'

- name: Copy a new sudoers file into place, after passing validation with visudo
  template:
    src: /mine/sudoers
    dest: /etc/sudoers
    validate: /usr/sbin/visudo -cf %s

- name: Update sshd configuration safely, avoid locking yourself out
  template:
    src: etc/ssh/sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: '0600'
    validate: /usr/sbin/sshd -t -f %s
    backup: yes
```

## unarchive

アーカイブファイルの展開。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/unarchive_module.html)

```
- name: Extract foo.tgz into /var/lib/foo
  unarchive:
    src: foo.tgz
    dest: /var/lib/foo

- name: Unarchive a file that is already on the remote machine
  unarchive:
    src: /tmp/foo.zip
    dest: /usr/local/bin
    remote_src: yes

- name: Unarchive a file that needs to be downloaded (added in 2.0)
  unarchive:
    src: https://example.com/example.zip
    dest: /usr/local/bin
    remote_src: yes

- name: Unarchive a file with extra options
  unarchive:
    src: /tmp/foo.zip
    dest: /usr/local/bin
    extra_opts:
    - --transform
    - s/^xxx/yyy/
```


## mount

ファイルシステムのマウント/アンマウント。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/mount_module.html)

```
# Before 2.3, option 'name' was used instead of 'path'
- name: Mount DVD read-only
  mount:
    path: /mnt/dvd
    src: /dev/sr0
    fstype: iso9660
    opts: ro,noauto
    state: present

- name: Mount up device by label
  mount:
    path: /srv/disk
    src: LABEL=SOME_LABEL
    fstype: ext4
    state: present

- name: Mount up device by UUID
  mount:
    path: /home
    src: UUID=b3e48f45-f933-4c8e-a700-22a159ec9077
    fstype: xfs
    opts: noatime
    state: present

- name: Unmount a mounted volume
  mount:
    path: /tmp/mnt-pnt
    state: unmounted

- name: Mount and bind a volume
  mount:
    path: /system/new_volume/boot
    src: /boot
    opts: bind
    state: mounted
    fstype: none
```

## package

OSのパッケージマネージャを利用したパッケージのインストール/アンインストール。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/package_module.html)

```
- name: install ntpdate
  package:
    name: ntpdate
    state: present

# This uses a variable as this changes per distribution.
- name: remove the apache package
  package:
    name: "{{ apache }}"
    state: absent
```

## service

OSのサービス制御。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/service_module.html)

```
- name: Start service httpd, if not started
  service:
    name: httpd
    state: started

- name: Stop service httpd, if started
  service:
    name: httpd
    state: stopped

- name: Restart service httpd, in all cases
  service:
    name: httpd
    state: restarted

- name: Reload service httpd, in all cases
  service:
    name: httpd
    state: reloaded

- name: Enable service httpd, and not touch the state
  service:
    name: httpd
    enabled: yes

- name: Start service foo, based on running process /usr/bin/foo
  service:
    name: foo
    pattern: /usr/bin/foo
    state: started

- name: Restart network service for interface eth0
  service:
    name: network
    state: restarted
    args: eth0
```

## debug

メッセージを出力。デバッグ時に使う。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/debug_module.html)

```
# Example that prints the loopback address and gateway for each host
- debug:
    msg: System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}

- debug:
    msg: System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}
  when: ansible_default_ipv4.gateway is defined

# Example that prints return information from the previous task
- shell: /usr/bin/uptime
  register: result

- debug:
    var: result
    verbosity: 2

- name: Display all variables/facts known for a host
  debug:
    var: hostvars[inventory_hostname]
    verbosity: 4

# Example that prints two lines of messages, but only if there is an environment value set
- debug:
    msg:
    - "Provisioning based on YOUR_KEY which is: {{ lookup('env', 'YOUR_KEY') }}"
    - "These servers were built using the password of '{{ password_used }}'. Please retain this for later use."
```

## command

任意のコマンドを実行する。シェルを介して実行されないため、パイプ、リダイレクトは使えない。
これらを使いたい場合はshellを使う。commandはユーザ環境に依存せずにセキュアにコマンドを実行できる。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/command_module.html)

```
- name: return motd to registered var
  command: cat /etc/motd
  register: mymotd

- name: Run command if /path/to/database does not exist (without 'args').
  command: /usr/bin/make_database.sh db_user db_name creates=/path/to/database

# 'args' is a task keyword, passed at the same level as the module
- name: Run command if /path/to/database does not exist (with 'args').
  command: /usr/bin/make_database.sh db_user db_name
  args:
    creates: /path/to/database

- name: Change the working directory to somedir/ and run the command as db_owner if /path/to/database does not exist.
  command: /usr/bin/make_database.sh db_user db_name
  become: yes
  become_user: db_owner
  args:
    chdir: somedir/
    creates: /path/to/database

# 'argv' is a parameter, indented one level from the module
- name: Use 'argv' to send a command as a list - leave 'command' empty
  command:
    argv:
      - /usr/bin/make_database.sh
      - Username with whitespace
      - dbname with whitespace

- name: safely use templated variable to run command. Always use the quote filter to avoid injection issues.
  command: cat {{ myfile|quote }}
  register: myoutput
Return Values
```

コマンド実行時に環境変数を渡したい場合は以下のように定義できる。

```
- hosts: all
  gather_facts: no

  environment:
    TEST_ENV: "hello world"

  tasks:
    - name: test env
      command: "/bin/env"
      register: result
    - debug: var=result
```

## shell

任意のコマンドをシェルを介して実行する。パイプ、リダイレクトが使える。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/shell_module.html)

```
- name: Execute the command in remote shell; stdout goes to the specified file on the remote.
  shell: somescript.sh >> somelog.txt

- name: Change the working directory to somedir/ before executing the command.
  shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/

# You can also use the 'args' form to provide the options.
- name: This command will change the working directory to somedir/ and will only run when somedir/somelog.txt doesn't exist.
  shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/
    creates: somelog.txt

- name: Run a command that uses non-posix shell-isms (in this example /bin/sh doesn't handle redirection and wildcards together but bash does)
  shell: cat < /tmp/*txt
  args:
    executable: /bin/bash

- name: Run a command using a templated variable (always use quote filter to avoid injection)
  shell: cat {{ myfile|quote }}

# You can use shell to run other executables to perform actions inline
- name: Run expect to wait for a successful PXE boot via out-of-band CIMC
  shell: |
    set timeout 300
    spawn ssh admin@{{ cimc_host }}

    expect "password:"
    send "{{ cimc_password }}\n"

    expect "\n{{ cimc_name }}"
    send "connect host\n"

    expect "pxeboot.n12"
    send "\n"

    exit 0
  args:
    executable: /usr/bin/expect
  delegate_to: localhost

# Disabling warnings
- name: Using curl to connect to a host via SOCKS proxy (unsupported in uri). Ordinarily this would throw a warning.
  shell: curl --socks5 localhost:9000 http://www.ansible.com
  args:
    warn: no
```

コマンド実行時に環境変数を渡したい場合は以下のように定義できる。

```
- hosts: all
  gather_facts: no

  environment:
    TEST_ENV: "hello world"

  tasks:
    - name: test env
      shell: "/bin/env > /tmp/aaa.txt"
```

## cron

cronの設定管理。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/cron_module.html)

```
- name: Ensure a job that runs at 2 and 5 exists. Creates an entry like "0 5,2 * * ls -alh > /dev/null"
  cron:
    name: "check dirs"
    minute: "0"
    hour: "5,2"
    job: "ls -alh > /dev/null"

- name: 'Ensure an old job is no longer present. Removes any job that is prefixed by "#Ansible: an old job" from the crontab'
  cron:
    name: "an old job"
    state: absent

- name: Creates an entry like "@reboot /some/job.sh"
  cron:
    name: "a job for reboot"
    special_time: reboot
    job: "/some/job.sh"

- name: Creates an entry like "PATH=/opt/bin" on top of crontab
  cron:
    name: PATH
    env: yes
    job: /opt/bin

- name: Creates an entry like "APP_HOME=/srv/app" and insert it after PATH declaration
  cron:
    name: APP_HOME
    env: yes
    job: /srv/app
    insertafter: PATH

- name: Creates a cron file under /etc/cron.d
  cron:
    name: yum autoupdate
    weekday: 2
    minute: 0
    hour: 12
    user: root
    job: "YUMINTERACTIVE=0 /usr/sbin/yum-autoupdate"
    cron_file: ansible_yum-autoupdate

- name: Removes a cron file from under /etc/cron.d
  cron:
    name: "yum autoupdate"
    cron_file: ansible_yum-autoupdate
    state: absent

- name: Removes "APP_HOME" environment variable from crontab
  cron:
    name: APP_HOME
    env: yes
    state: absent
```

## user

ユーザ管理。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/user_module.html)

```
- 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: 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: Remove the user 'johnd'
  user:
    name: johnd
    state: absent
    remove: yes

- name: Create a 2048-bit SSH key for user jsmith in ~jsmith/.ssh/id_rsa
  user:
    name: jsmith
    generate_ssh_key: yes
    ssh_key_bits: 2048
    ssh_key_file: .ssh/id_rsa

- name: Added a consultant whose account you want to expire
  user:
    name: james18
    shell: /bin/zsh
    groups: developers
    expires: 1422403387

- name: Starting at Ansible 2.6, modify user, remove expiry time
  user:
    name: james18
    expires: -1
```

## group

グループ管理。
[リファレンス](https://docs.ansible.com/ansible/2.8/modules/group_module.html)

```
- name: Ensure group "somegroup" exists
  group:
    name: somegroup
    state: present
```