Ansibleでmacのローカル環境をサクッと作ってみた

## はじめに
この記事は[Hamee Advent Calendar 2017](https://qiita.com/advent-calendar/2017/hamee)の22日目の記事です。
Ansibleを使ってローカル環境の構築を自動化させたのでそのお話です。

## 背景
最近個人用のMacを新調したのですが、必要なものとか前から使ってたアプリとかを手作業で移行させるのって結構面倒だなと。
そこでワンラインでサクッとローカル環境の構築してくれる素晴らしツール、Ansibleを見つけたので紹介します。

 -  **Mac買ったはいいけど、初期セットアップ面倒だなぁ**
 -  **dotfilesは管理はしてるけどその他のツールはやってないや**
 -  **なんか難しそうだし後にしておこう...（´・ω・`）.;:…（´・ω...:.;::..（´・;::: .:.;: ｻﾗｻﾗ..**

という僕と同じ境遇にいた方の手助けできれば幸いです。

## そもそもAnsibleって？
この記事がものすごくわかりやすくまとまっているのでリンクを貼っておきます。
[Ansibleをはじめる人に。](https://qiita.com/t_nakayama0714/items/fe55ee56d6446f67113c#ansible%E3%81%A8%E3%81%AF) 
Ansibleは

 - Inventory
	 - Ansibleで操作する対象マシンの定義。**「どこに」**Ansibleを実行するか
 - Module
	 - Ansibleにおけるコマンド。**「何を」**Ansibleから実行するか
 - Playbook
	 - Ansibleにおけるスクリプト(コード)。**「どのように」**Ansibleを実行するか

をyaml形式で記述し、人手を介さずソフトウェアによって自動実行化する、Infrstructure as Codeの概念を実現させるようなツールの一つです。
本来はインフラの設定を自動化する用途で用いられることが多いのですが、ローカルのマシンの設定なんかも出来たりします。

## 事前準備
事前にXcode、homebrew、ansible本体、pythonがインストールしておく必要があります。
実行環境は以下の環境で実行しました。

 - マシン: MacbookPro  
 - OS: macOS High Sierra  
 - バージョン: 10.13.1

## ディレクトリ構成とPlaybookの中身
[ディレクトリ構成のベストプラクティス](http://docs.ansible.com/ansible/latest/playbooks_best_practices.html#directory-layout)も挙げられているので参考にどうぞ。
自分が作ったディレクトリ構成はこんな感じです。

```
├── hosts -- Inventory。今回はローカルでの実行なのでlocalhostを記載
├── exec.yml  -- Playbook。読み込むModuleはRoleで切り分ける
└── roles/ -- 実行単位毎に分解
    ├── anyenv/
    │   ├── tasks/
    │   └── vars/
    ├── dotfiles/
    │   ├── tasks/
    │   └── vars/
    ├── gem/
    │   ├── tasks/
    │   └── vars/
    ├── go/
    │   ├── tasks/
    │   └── vars/
    ├── homebrew/
    │   ├── tasks/
    │   └── vars/
    └── php/
        ├── tasks/
        └── vars/
```

でPlaybook(exec.yml)に

```
- hosts: localhost
  connection: local
  roles:
    - { role: homebrew, tags: [homebrew] }
    - { role: anyenv, tags: [anyenv] }
    - { role: gem, tags: [gem] }
    - { role: dotfiles, tags: [dotfiles] }
    - { role: go, tags: [go] }
    - { role: php, tags: [php] }

```


を書き込みます。
rolesディレクトリ内で作業を自動化させたいツールごとにディレクトリを分けて、それぞれのディレクトリには

 - tasks
	 - **機能ごとに実行する手順の設定をまとめたファイル**を配置したディレクトリ
 - vars
	 - tasksで実行する手順内で**使用するリストをまとめたファイル**を配置したディレクトリ

で設定ファイルを設置しています。
こうすることで、後々「あー次は○○のツールも自動化させようかなー」って乗り気になった時に追加がしやすいかなと思いこの構成にしました。

## 実装

全てを紹介しているとかなり長くなってしまいそうだったので、homebrew周りの実装以外を割愛させてもらいます。
↓他のもツールの実装も興味あるよーって方は以下のソースを参照ください↓
https://github.com/gtongy/osx-provisioning
下の実装はhomebrewに関連するroleのソースです。

```yaml:tasks/main.yml

- name: Add homebrew tap repository
  homebrew_tap: tap={{ item }} state=installed
  with_items:
    - "{{ homebrew_taps }}"

- name: Update homebrew
  homebrew: update_homebrew=yes

- name: Install brew packages
  homebrew:
    name={{ item.name }}
    state={{ item.state | default('installed') }}
    install_options={{ item.install_options | default('latest') }}
  with_items:
    "{{ homebrew_packages }}"

- name: Install cask packages
  homebrew_cask:
    name={{ item.name }}
    state={{ item.state | default('installed') }}
  with_items:
    - "{{ homebrew_cask_packages }}"

```

```yaml:var/main.yml
homebrew_taps:
  - caskroom/cask

homebrew_packages:
  - { name: rmtrash }
  - { name: openssl }
  - { name: libxml2 }
  - { name: mcrypt }
  - { name: autoconf }
  - { name: automake }
  - { name: bison }
  - { name: chromedriver }
  - { name: docker-machine }
  - { name: jq }
  - { name: tree }
  - { name: curl }
  - { name: nkf }
  - { name: peco }
  - { name: zplug }
  - { name: parallel }
  - { name: wget }
  - { name: awscli }
  - { name: node }

homebrew_cask_packages:
  - { name: docker }
  - { name: visual-studio-code }
  - { name: phpstorm }
  - { name: atom }
  - { name: spectacle }
  - { name: alfred }
  - { name: iterm2 }
  - { name: slack }
  - { name: table-tool }
  - { name: google-chrome }
  - { name: macdown }

```
varsディレクトリ内に定義されたリストはそれぞれwith_itemsモジュールに渡しています。
タスク内で渡したリストをループさせhomebrew tapの作成やhomebrew、homebrew caskのパッケージをインストールしてます。


## 実行

以下のコマンドを実行します。

```
$ cd /path/to/work/osx-provisioning
$ HOMEBREW_CASK_OPTS="--appdir=~/Applications" ansible-playbook -i hosts exec.yml
```
`HOMEBREW_CASK_OPTS`にはcaskで取ってきたアプリ群を落とすディレクトリを指定するようにしてください。
マシンによっては`--appdir=/Applications`だったりすることもあるのでご注意を。

## 実行結果
homebrew周りの2度目の実行の出力結果です。

```
PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [homebrew : Add homebrew tap repository] **********************************
ok: [localhost] => (item=caskroom/cask)

TASK [homebrew : Update homebrew] **********************************************
ok: [localhost]

TASK [homebrew : Install brew packages] ****************************************
ok: [localhost] => (item={u'name': u'rmtrash'})
ok: [localhost] => (item={u'name': u'openssl'})
ok: [localhost] => (item={u'name': u'libxml2'})
ok: [localhost] => (item={u'name': u'mcrypt'})
ok: [localhost] => (item={u'name': u'autoconf'})
ok: [localhost] => (item={u'name': u'automake'})
ok: [localhost] => (item={u'name': u'bison'})
ok: [localhost] => (item={u'name': u'chromedriver'})
ok: [localhost] => (item={u'name': u'docker-machine'})
ok: [localhost] => (item={u'name': u'jq'})
ok: [localhost] => (item={u'name': u'tree'})
ok: [localhost] => (item={u'name': u'curl'})
ok: [localhost] => (item={u'name': u'nkf'})
ok: [localhost] => (item={u'name': u'peco'})
ok: [localhost] => (item={u'name': u'zplug'})
ok: [localhost] => (item={u'name': u'parallel'})
ok: [localhost] => (item={u'name': u'wget'})
ok: [localhost] => (item={u'name': u'awscli'})
ok: [localhost] => (item={u'name': u'node'})

TASK [homebrew : Install cask packages] ****************************************
ok: [localhost] => (item={u'name': u'docker'})
ok: [localhost] => (item={u'name': u'visual-studio-code'})
ok: [localhost] => (item={u'name': u'phpstorm'})
ok: [localhost] => (item={u'name': u'atom'})
ok: [localhost] => (item={u'name': u'spectacle'})
ok: [localhost] => (item={u'name': u'alfred'})
ok: [localhost] => (item={u'name': u'iterm2'})
ok: [localhost] => (item={u'name': u'slack'})
ok: [localhost] => (item={u'name': u'table-tool'})
ok: [localhost] => (item={u'name': u'google-chrome'})
ok: [localhost] => (item={u'name': u'macdown'})

PLAY RECAP *********************************************************************
localhost                  : ok=5    changed=0    unreachable=0    failed=0
```

うまく実行されていますね！  

## おわりに

いかがでしょうか？
個人的には、Ansibleのワンライナーで勝手にサクッと環境を作ってくれちゃうクールさと設定ファイルを書くみたいに書ける手軽さがとってもいい感じです。
いろんなサイトから掻い摘んで少しずつやってきましたが、とりあえずは形になった（？）のでよかったです。
何よりも一から自分の設定とか作るのって改めて楽しいなーー
改めて自分の触るマシンに、愛着を持って育てて行きたいですね！
環境構築だるいよって人や、こうやって自分のマシンを一から育てるのが大好きな方は是非触ってみてください。

## 参考にしたリンク
https://github.com/geerlingguy/ansible-role-dotfiles
http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html
https://qiita.com/t_nakayama0714/items/fe55ee56d6446f67113c
https://qiita.com/supistar/items/be0fcdeb05370a7b670d
https://github.com/ansible-macos/macos-playbook
http://lazy-dog.hatenablog.com/entry/2014/09/21/114738
https://github.com/Leko/setup-osx

## 参考にした書籍
[Ansible徹底入門](https://www.amazon.co.jp/Ansible%E5%BE%B9%E5%BA%95%E5%85%A5%E9%96%80-%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E6%99%82%E4%BB%A3%E3%81%AE%E6%96%B0%E3%81%97%E3%81%84%E6%A7%8B%E6%88%90%E7%AE%A1%E7%90%86%E3%81%AE%E5%AE%9F%E7%8F%BE-%E5%BB%A3%E5%B7%9D-%E8%8B%B1%E5%AF%BF/dp/4798149942/ref=sr_1_1?ie=UTF8&qid=1513697690&sr=8-1&keywords=ansible%E5%BE%B9%E5%BA%95%E5%85%A5%E9%96%80)