Ansible Galaxyを使ってMacを構築してみた -2022-

2022.04.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。たかやまです。

今まで生粋のWindowsユーザでしたが、この度Macが支給(※Windows/Mac選べます)されたのでこれからのMac利用を見据えてAnsibleでMacを構築してみたいと思います。

今回はAnsible GalaxyでMac構築に良さげなRoleがあったので記事にしたいと思います。

Ansible Galaxyとは

Ansibleで利用できるRoleやCollections (module, plugin, roleをまとめた単位)を配布するコミュニティサイトです。

今回はこのAnsible Galaxyで配布されているgeerlingguy.macを使用してMacを構築してみます。

構成

  • macOS 12.3.1 M1/16GB
  • Homebrew 3.4.4
  • Ansible-core 2.12.4/Ansible 5

Mac構築手順

Homebrewインストール

Ansibleはpipでインストールするのが公式推奨手順ですが、個別にpipをインストールしたりpath通したりするのが手間なため今回はHomebrewでインストールします。
そのため、まずはHomebrewをインストールします。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

インストール後、homebrewのpathを通す。 ※zshの場合

echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/your_username/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

インストールできました。

% brew --version
Homebrew 3.4.5
Homebrew/homebrew-core (git revision 492433b71e4; last commit 2022-04-07)

Ansibleインストール

homebrewでansibleをインストールします。

brew install ansible

Ansibleもサクッとインストールできます。

% ansible --version
ansible [core 2.12.4]
  config file = None
  configured module search path = ['/Users/your_username/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/homebrew/Cellar/ansible/5.5.0/libexec/lib/python3.10/site-packages/ansible
  ansible collection location = /Users/your_username/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/homebrew/bin/ansible
  python version = 3.10.2 (main, Feb  2 2022, 05:51:25) [Clang 13.0.0 (clang-1300.0.29.3)]
  jinja version = 3.1.1
  libyaml = True

Ansible Galaxyインストール

Ansible-Galaxyからgeerlingguy.macをインストールします。

ansible-galaxy collection install geerlingguy.mac

Ansible-Galaxyのパッケージはカレントディレクトリ配下の.ansibleにダウンロードされます。あとは後述のPlaybookの中でインストールしたRole指定することで利用可能となります。

% ansible-galaxy collection list
.
.
# /Users/your_username/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 4.7.0
geerlingguy.mac   1.4.0

Playbookの作成

先ほどインストールしたgeerlingguy.macは3つのRoleで構成されています。
作成するPlaybookには利用するRoleで参照される変数を定義していく形になります。

Role 機能
geerlingguy.mac.homebrew homebrewを使ったソフトウェア管理をする
geerlingguy.mac.mas Mac App Storeのソフトウェアを管理する
geerlingguy.mac.dock dockのレイアウト管理をする

Playbook(site.yml)の記述例は以下の通りです。
今回インストールしたRoleだけでセットアップする場合は、こちらのPlaybookだけ用意すれば大丈夫です。

site.yml

- hosts: localhost
  connection: local
  become_user: your_computename
  gather_facts: false

  vars:
    ansible_machine: arm64 # arm64/x86_64を指定する。今回はM1 Macなのでarm64を指定
    ansible_user_id: your_username # 実行ユーザID
    ansible_user_gid: your_groupid # 実行ユーザのグループID

    homebrew_cask_apps: # インストールしたいcaskアプリをリスト形式で指定
      - 1password

    homebrew_cask_uninstalled_apps: # アンインストールしたいcaskアプリをリスト形式で指定
      - alfred

    homebrew_installed_packages: # インストールしたいGUIアプリをリスト形式で指定
      - awscli

    homebrew_uninstalled_packages: # アンインストールしたいGUIアプリをリスト形式で指定
      - autoconf

    mas_installed_apps: # インストールしたいMac Apple Storeアプリをリスト形式で指定
      - { id: 409183694, name: "Keynote" }

    mas_uninstalled_apps: # アンインストールしたいMac Apple Storeアプリをリスト形式で指定
      - { id: 682658836, name: "GarageBand" }

    dockitems_persist: # Dockの配列を指定
      - name: Launchpad
        path: "/Applications/Launchpad.app/"
        pos: 1

  roles: # インストールしたRoleを指定
    - role: geerlingguy.mac.homebrew
      tags: homebrew
    - role: geerlingguy.mac.mas
      tags: mas
    - role: geerlingguy.mac.dock
      tags: dock

ここに定義した変数以外にBrewfileを使ったインストール方法など他にも変数が用意されているので、気になった方はドキュメントをご確認ください。

Playbookの実行

以下のコマンドを実行することで、先ほど定義したPlaybook(site.yml)を元にRoleを読み込んでMacのセットアップをしてくれます。

ansible-playbook -i localhost, -c local site.yml
実行例(クリックで展開)
% ansible-playbook -i localhost, -c local site.yml

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

TASK [geerlingguy.mac.homebrew : Determine Homebrew ownership variables] ***************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure Homebrew parent directory has correct permissions (M1).] ***************************************************************
[WARNING]: Platform darwin on host localhost is using the discovered Python interpreter at /opt/homebrew/bin/python3.9, but future installation of another
Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.12/reference_appendices/interpreter_discovery.html for
more information.
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure Homebrew parent directory has correct permissions (MacOS >= 10.13).] ***************************************************
skipping: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure Homebrew parent directory has correct permissions (MacOS < 10.13).] ****************************************************
skipping: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure Homebrew directory exists.] ********************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure Homebrew is installed.] ************************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure proper permissions and ownership on homebrew_brew_bin_path dirs.] ******************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure proper ownership on homebrew_install_path subdirs.] ********************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Check if homebrew binary is already in place.] ********************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Symlink brew to homebrew_brew_bin_path.] **************************************************************************************
skipping: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure proper homebrew folders are in place.] *********************************************************************************
ok: [localhost] => (item=Cellar)
ok: [localhost] => (item=Homebrew)
ok: [localhost] => (item=Frameworks)
ok: [localhost] => (item=Caskroom)
ok: [localhost] => (item=bin)
ok: [localhost] => (item=etc)
ok: [localhost] => (item=include)
ok: [localhost] => (item=lib)
ok: [localhost] => (item=opt)
ok: [localhost] => (item=sbin)
ok: [localhost] => (item=share)
ok: [localhost] => (item=share/zsh)
ok: [localhost] => (item=share/zsh/site-functions)
ok: [localhost] => (item=var)

TASK [geerlingguy.mac.homebrew : Force update brew after installation.] ****************************************************************************************
skipping: [localhost]

TASK [geerlingguy.mac.homebrew : Where is the cache?] **********************************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Ensure configured taps are tapped.] *******************************************************************************************
ok: [localhost] => (item=homebrew/core)

TASK [geerlingguy.mac.homebrew : Ensure blacklisted cask applications are not installed.] **********************************************************************
ok: [localhost] => (item=alfred)

TASK [geerlingguy.mac.homebrew : Install configured cask applications.] ****************************************************************************************
ok: [localhost] => (item=1password)

TASK [geerlingguy.mac.homebrew : Ensure blacklisted homebrew packages are not installed.] **********************************************************************
ok: [localhost] => (item=autoconf)

TASK [geerlingguy.mac.homebrew : Ensure configured homebrew packages are installed.] ***************************************************************************
ok: [localhost] => (item=awscli)

TASK [geerlingguy.mac.homebrew : Upgrade all homebrew packages (if configured).] *******************************************************************************
skipping: [localhost]

TASK [geerlingguy.mac.homebrew : Check for Brewfile.] **********************************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.homebrew : Install from Brewfile.] *******************************************************************************************************
skipping: [localhost]

TASK [geerlingguy.mac.dock : Install dockutil.] ****************************************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.dock : Remove configured Dock items.] ****************************************************************************************************

TASK [geerlingguy.mac.dock : Ensure required dock items exist.] ************************************************************************************************
included: /Users/your_username/.ansible/collections/ansible_collections/geerlingguy/mac/roles/dock/tasks/dock-add.yml for localhost => (item={'name': 'Launchpad', 'path': '/Applications/Launchpad.app/', 'pos': 1})

TASK [geerlingguy.mac.dock : See if Dock item Launchpad exists.] ***********************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.dock : Ensure Dock item Launchpad exists.] ***********************************************************************************************
skipping: [localhost]

TASK [geerlingguy.mac.dock : Ensure dock items are in correct position.] ***************************************************************************************
included: /Users/your_username/.ansible/collections/ansible_collections/geerlingguy/mac/roles/dock/tasks/dock-position.yml for localhost => (item={'name': 'Launchpad', 'path': '/Applications/Launchpad.app/', 'pos': 1})

TASK [geerlingguy.mac.dock : Check the current Dock position of Launchpad.] ************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.dock : Get current dock item position from output.] **************************************************************************************
ok: [localhost]

TASK [geerlingguy.mac.dock : Move dock item to the correct position.] ******************************************************************************************
skipping: [localhost]

PLAY RECAP *****************************************************************************************************************************************************
localhost                  : ok=21   changed=0    unreachable=0    failed=0    skipped=9    rescued=0    ignored=0

Tips

私が利用した時点で、以下のRoleの実行元コマンドでエラーがあったので共有です。

Role 不具合
geerlingguy.mac.mas MasでのUninstallが動作しない
mas-cli/mas#313
geerlingguy.mac.dock homebrewでインストールしたdockutilが動作しないため、
別途最新のdockutilをインストールする
kcrawford/dockutil#127

まとめ

今回geerlingguy.macを使用したMacのセットアップ方法をご紹介しました。こちらのRoleを利用することでAnsibleに詳しくなくても簡単にMac構築を自動化をできると思います。

また、こちらのRoleを使うだけでもだいぶ構築を自動化ができますが、Ansibleが提供している他のCollectionsを利用することでシステム設定ファイルなどの細かい管理もできますの興味の湧いた方はぜひAnsibleのドキュメントをのぞいてみてください。

参考までに、今回私が構築に利用したPlaybookをGitHubにあげておきます。

以上、たかやまでした!