Terraformのcidrsubnets関数について

## はじめに
TerraformはIaC(Infrastructure as Code)ツールの一つです。
私自身AWSを勉強する傍ら、きちんと理解を深めるために、Terraformについても学んでいます。
cidrsubnets関数については情報が出てくるものの、
cidrsubnet"s"関数についてはあまり情報がなかったので、
メモとして、まとめさせていただきました。

## cidrsubnetsのポイント
cidrsubnetsでおさえてほしいポイントは2つだけです。
- cidrsubnetsはcidrsubnetの集合を返す
- 第2引数以降でサブネットマスクが無条件に決まる

## cidrsubnetsとcidrsubnetの違い
cidrsubnetの説明については、ほかの方々の説明に譲りますが、一言で言ってしまえば、
「定義されたアドレスの範囲を同じサイズで分割してできたブロックをインデックスで指定して返す」
と認識しています。
イメージ図を書くとこんなところです。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3580608/fe972d94-c8b6-380e-1bd5-e69529996a45.png">

対してcidrsubnetsについては「
定義されたアドレスの範囲を、任意のサイズで分割したブロックの集合を返す」
と認識しています。
イメージ図を書くとこんな感じです。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3580608/8cfb7b05-cffd-c5ee-7807-7f994791e9d2.png">

可変サイズで分割できるのが魅力的ですね。
## 公式サイトの記述と例
### 公式サイト内の記載
[リンク(公式)](https://developer.hashicorp.com/terraform/language/functions/cidrsubnets)にはこのように記載されています。
> cidrsubnets calculates a sequence of consecutive IP address ranges within a particular CIDR prefix.
> ```
> cidrsubnets(prefix, newbits...)
> ```
> The remaining arguments, indicated as newbits above, 
> each specify the number of additional network prefix bits for one returned address range. 
> The return value is therefore a list with one element per newbits argument, 
> each a string containing an address range in CIDR notation.

### 例
同サイト内で、例として出されているのがこちら
> ```
> cidrsubnets("10.1.0.0/16", 4, 4, 8, 4)
> [
>   "10.1.0.0/20",
>   "10.1.16.0/20",
>  "10.1.32.0/24",
>   "10.1.48.0/20",
> ]
> ```
「ネットワーク部のアドレス(16, 32, 48という数字)ってどっから出てきた？」
というのが疑問の発端となります。

## 個人的な考察
そこで、理解のポイントとなるのが、「第2引数以降でサブネットマスクが無条件に決まる」という点です。
先ほどの例を考えてみます。
> ```
> cidrsubnets("10.1.0.0/16", 4, 4, 8, 4)
> ```
これは次のように解読できます。
- "10.1.0.0/16"という範囲がある。
- network prefixを4bit追加した範囲、すなわちホスト部の大きさが12(=16-4)となる範囲を切り取る。
- 同様に、ホスト部の大きさが12(=16-4)となる範囲を切り取る。
- 次に、ホスト部の大きさが8(=16-8)となる範囲を切り取る。
- 最後に、ホスト部の大きさが12(=16-4)となる範囲を切り取る。

そのままアドレスブロックを表現すると、次のようになります。
| index  | 追加bit | アドレス範囲 |　CIDR表記
|:-:|:-:|:-:|:-:|
| 1  |4 |10.1.0.0 ~ 10.1.15.255 | 10.1.0.0/12|
| 2  |4 |10.1.16.0 ~ 10.1.31.255 | 10.1.16.0/12|
| 3  |8 |10.1.32.0 ~ 10.1.32.255 | 10.1.32.0/8|
| 4  |4 |10.1.33.0 ~ 10.1.48.255 |?????????| 

お気づきでしょうか？そう、4番目のブロックについてはCIDR表記ができないのです。
CIDR表記が使えないとなると、実務上、不便になることは想像に難くありません。

そこで、「第2引数以降でサブネットマスクが無条件に決まる」という視点で考えてみましょう。
先ほどの例から整理すると、次のようになります。
| index  | 追加bit| アドレス範囲 |　CIDR表記 | 表現したいアドレス|
|:-:|:-:|:-:|:-:|:-:|
| 1  |4 |10.1.0.0 ~ 10.1.15.255 | 10.1.0.0/12|X.X.X.X/<font color="red">**12**</font>|
| 2  |4 |10.1.16.0 ~ 10.1.31.255 | 10.1.16.0/12|X.X.X.X/<font color="red">**12**</font>|
| 3  |8 |10.1.32.0 ~ 10.1.32.255 | 10.1.32.0/8| X.X.X.X/<font color="red">**8**</font>|
| 4  |4 |10.1.33.0 ~ 10.1.48.255 |???????|X.X.X.X/<font color="red">**12**</font>|

相変わらず、4番目のブロックについては表現しきれません。
そのためにどうすればよいでしょうか？
気づいてる方もいらっしゃるとは思いますが、
CIDRブロックで表現できるように、アドレス範囲の開始位置をずらしてあげればよいのです。
中途半端な開始位置を次のようにずらしてあげると、
| index  |追加bit| アドレス範囲 | アドレス範囲(補正) | CIDR表記 | 
|:-:|:-:|:-:|:-:|:-:|
| 1  | 4|10.1.0.0 ~ 10.1.15.255 | 10.1.0.0 ~ 10.1.15.255|10.1.0.0/12|
| 2  | 4|10.1.16.0 ~ 10.1.31.255 | 10.1.16.0 ~ 10.1.31.255 |10.1.16.0/12|
| 3  | 8|10.1.32.0 ~ 10.1.32.255 | 10.1.32.0 ~ 10.1.32.255| 10.1.32.0/8|
| 4  | 4|10.1.33.0 ~ 10.1.48.255 |10.1.<font color="red">**48**</font>.0 ~ 10.1.63.255|10.1.48.0/12|

無事CIDR表記ができる形で分割することができました。
これが、前述の例のからくりです。

cidrsubnetsを使って分割すると、
分割したブロックは自動的にCIDRで表現できるように補正してくれていることがわかります。
ただ、使われないで棄却されてしまう範囲(10.1.33.0 ~ 10.1.47.255)も
出てきてしまうことが見て取れますね。

## テスト
※terraformの初期設定は済ませているものとします。
適当にサブディレクトリを作って、適当なtfファイルを放り込んでください(名前も適宜設定してください)。
```
└─work
     └─ test.tf
```
.tfの内容はこんな感じにしてみましょう
```terraform
output "cidrsubnets_test_4_4_8_4" {
  value = cidrsubnets("10.1.0.0/16", 4, 4, 8, 4)
}
output "cidrsubnets_test_4_4_8_8" {
  value = cidrsubnets("10.1.0.0/16", 4, 4, 8, 8)
}
output "cidrsubnets_test_4_3_5_7" {
  value = cidrsubnets("10.1.0.0/16", 4, 3, 5, 7)
}
```
この状態で、terraform planを実行すると、結果が出力されます。
```yaml
Changes to Outputs:
  + cidrsubnets_test_4_3_5_7 = [
      + "10.1.0.0/20",
      + "10.1.32.0/19",
      + "10.1.64.0/21",
      + "10.1.72.0/23",
    ]
  + cidrsubnets_test_4_4_8_4 = [
      + "10.1.0.0/20",
      + "10.1.16.0/20",
      + "10.1.32.0/24",
      + "10.1.48.0/20",
    ]
  + cidrsubnets_test_4_4_8_8 = [
      + "10.1.0.0/20",
      + "10.1.16.0/20",
      + "10.1.32.0/24",
      + "10.1.33.0/24",
    ]
```
きちんと出力されていることがわかります。
## まとめ
- cidrsubnetsを使えば可変のブロックに分割できる
- 分割した範囲がCIDR表記できるようにするため補正がかかる。
## 最後に
コメントや間違っている部分等がありましたら、気軽に連絡をいただけると助かります。