PuppetのHieraで同じリソースを複数定義する（create_resources）

Puppet Advent Calendar 2015の、5日目です。
Puppetをもりあげていこうということで、参加させてもらいます。

# Hieraで同じリソースを複数定義する

Puppetで同じリソースを複数定義したい場合があったりします。
例えば、Apacheのvirtual hostの設定を/etc/httpd/conf.d/の配下に個別に置く定義を何度も使いたいとか、ユーザのアクセス設定をする定義をユーザごとに使いたいとか(ftpの/etc/vsftpd/user_conf/をユーザごとに定義するとか)。
Hieraを使った場合でも、**create_resources**を使うと、同じ定義を何度も使うことができたりします。

# create_resources

create_resourceは、ハッシュ情報を指定したリソースに変換する関数です。パラメータに、リソースタイプとハッシュ情報（リソースへのパラメータをハッシュ形式で書いたもの）を指定すると、指定したリソースを複数定義できます。

# 既存リソースでの例

まずは既存リソースで使う例です。例えば、groupリソースとuserリソースをHieraで直接複数定義したい場合、まず次のようなクラス用意します。

```puppet:/etc/puppet/modules/base/manifests/account.pp
class base::account (
) {

  $groups = hiera('base::account::groups', {})
  create_resources('group', $groups)

  $users = hiera('base::account::users', {}) # (1)
  create_resources('user', $users)           # (2)

}

```

(1)はHieraの定義から、base::acount::usersの情報をハッシュとして\$usersに格納する行で、(2)はハッシュ情報(\$users)をcreate_resourcesでuserリソースに変換する行です。base::acount::usersや\$usersは変数名を適当に付けているだけなので、好みな変数名でかまいません。

Hiera側は下記のように書きます。ユーザやグループのパラメータをハッシュ形式で列挙するだけです。AnsibleをYAML形式で書いた場合にちょっと似てます。base::account::groupsとbase::account::usersがさきほどのbase::accountクラスにわたって、create_resourcesでリソース定義に変換されます。

```yaml:/etc/puppet/hieradata/sample.yaml
---
classes:
  - base::account

base::account::groups:
  hoge:
    ensure: 'present'
    gid: '1002'
  fuga: 
    ensure: 'present'
    gid: '1003'
base::account::users:
  hoge:
    ensure: 'present'
    gid: '1002'
    home: '/home/hoge'
    shell: '/bin/bash'
    uid: '1002'
  fuga:
    ensure: 'present'
    gid: '1003'
    home: '/home/fuga'
    shell: '/bin/bash'
    uid: '1003'
```

ちなみに、上の定義は次のマニフェスト定義と等価です。

```puppet:
group { 'hoge':
  ensure => 'present',
  gid    => '1002',
}

group { 'fuga':
  ensure => 'present',
  gid    => '1003',
}

user { 'hoge':
  ensure           => 'present',
  gid              => '1002',
  home             => '/home/hoge',
  shell            => '/bin/bash',
  uid              => '1002',
}

user { 'fuga':
  ensure           => 'present',
  gid              => '1003',
  home             => '/home/fuga',
  shell            => '/bin/bash',
  uid              => '1003',
}
```

# defineしたリソースでも
ちなみに、既存のリソースだけでなく、defineで独自に作ったリソースでも同様です。例えば、/user/vsftpd/user_conf/ユーザ名を、次のようなHiera定義で指定するには、

```yaml:/etc/puppet/hieradata/sample.yaml
---
classes:
  - vsftpd::config_user_conf

vsftpd::config_user_conf::hash:
  hoge:
    user: 'hoge'
    content: 'local_root=/home/ftp/hoge'
  fuga:
    user: 'fuga'
    content: 'local_root=/home/ftp/fuga'
```

このような"/etc/vsftpd/user_conf/ユーザ名"を配布するリソースをdefineして、

```puppet:/etc/puppet/modules/vsftpd/manifests/def_user_conf.pp
define vsftpd::def_user_conf(
  $user,
  $content,
){

  file { "/etc/vsftpd/user_conf/${user}":
    content => $content,
    owner   => 'root',
    group   => 'root',
    mode    => '644',
  }

}

```

create_resourcesで展開する、ラッパークラスみたいなものを用意すればよいです。

```puppet:/etc/puppet/modules/vsftpd/manifests/config_user_conf.pp
class vsftpd::config_user_conf (
) {
  
  $user_conf_hash = hiera('vsftpd::config_user_conf::hash', {})
  create_resources('vsftpd::def_user_conf', $user_conf_hash)
  
}

```

実際にPuppet適用後の結果はこんな感じです。

```bash
[root@localhost ~]# grep local /etc/vsftpd/user_conf/*
/etc/vsftpd/user_conf/fuga:local_root=/home/ftp/fuga
/etc/vsftpd/user_conf/hoge:local_root=/home/ftp/hoge
```

# もしかすると

Puppet4から強化されたループの文法を使う方が主流になるかもしれませんが、こんなやり方もあったりします。

# 参考

- https://docs.puppetlabs.com/references/latest/function.html#createresources
- https://ask.puppetlabs.com/question/1380/problems-creating-hiera-hashes-for-create_resources/