Terraformingでiam roleをimportするとdescriptionがなくてdiffが出るのをどうにかする. ついでに中身を見て理解を深める & iam_role_policy_attachmentリソース追加してPR出した

# 背景

- Terraforming (http://terraforming.dtan4.net) を使うと、既存のResourceをTerraformにImportできるのでとても便利!
- `terraform import` は、 tf ファイルのimportには対応してないので、既存のを載せるには、やはり Terraformingがよい

    > The current implementation of Terraform import can only import resources into the state. It does not generate configuration. A future version of Terraform will also generate configuration.

# 問題

Terraforming で iam role をImportするとDescriptionがimport出来ずdiffが出てしまう

# 解決方法

https://github.com/dtan4/terraforming/pull/474 すでにPRが出ている。が、 2019/06/25に出たPRがマージされてない…

# おまけ

せっかくなのでhttps://github.com/dtan4/terraforming をちゃんと理解してみた。

## Terraformingの歴史

Commit logで振り返った。ざっくり見ると以下の感じ

1. Initial commitは2015年3月25日! ←自分がTerraformのTの字も知らない頃…
2. 最初のリソースはRDS、そしてS3
3. もともとのコマンドの使い方は、以下のような形

    ```
    aws rds describe-db-instances | ./terraforming-rds
    aws s3 ls | ./terraforming-s3
    ```

4. ELB, VPC, Security Groupが追加
5. `lib/terraforming/resource/` にクラスが移される
6. `erb` テンプレートが導入される
7. 別れていたコマンドが`terraforming` へ統合される
8. いろいろツール導入とテスト修正
9. それまでは aws cli + terraformingをパイプで渡していたのから、`aws-sdk`を利用するように変更になる
10. Thorを使ったcliになる
12. 残りは細かい修正とResourceの追加が繰り返される


## 構成

```
.
├── bin
│   └── terraforming # cliとして呼ばれる
...
├── lib
│   ├── terraforming
│   │   ├── cli.rb # cliの定義
│   │   ├── resource # AWSのResourceのClassを定義したファイルがここに入ってる (ここにaws-sdkからresource情報をとりtf/tfstate生成する)
│   │   ├── template # tf生成するときに使われるerbのテンプレート
│   │   ├── util.rb # 共通のメソッド
│   │   └── version.rb # バージョン
│   └── terraforming.rb # terraforming/以下で定義されたものをrequireしている
├── script
│   ├── console
│   ├── generate # 新しいResourceを追加するとき用のscript
│   └── setup
├── spec # rspecファルダ
│   ├── fixtures
│   │   └── terraform.tfstate
│   ├── lib
│   │   ├── terraforming
│   │   └── terraforming_spec.rb
│   └── spec_helper.rb
├── templates # テンプレート
│   ├── resource.erb.erb
│   ├── resource.rb.erb
│   └── resource_spec.rb.erb
├── terraforming.gemspec
```

## 自分でもgem作ってみよう! 目標:「iam_roleのdescriptionを追加したのを動かす」

Gemとかほとんどつくったことないから適当にGoogleって my_rubygemとか作ってみた (割愛)

https://github.com/nakamasato/practice_terraforming 真似て作ったやつ。

### Gemを作る

- gem作成

    ```
    bundle gem practice_terraforming
    ```
- gemspecの編集

### iam_roleだけ動くもの作る

以下のものをコピってくる

- `lib/practice_terraforming/resource/iam_role.rb` `aws-sdk`を使ってresource情報を取ってきてtfstateを作るロジックが書いてある. tfファイルは、templateに読み込んだ変数からerbで生成するようになっている
- `lib/practice_terraforming/template/tf/iam_role.erb` tfファイル作るようのテンプレート
- `spec/lib/practice_terraforming/resource/iam_role_spec.rb`
- `bin/practice_terraforming` cliでexecuteする部分
- `lib/practice_terraforming.rb` dependenciesの読み込み
- `lib/practice_terraforming/cli.rb` Thorを使ったcliの実装
- `lib/practice_terraforming/util.rb` いろいろ使える道具が入ってる

いろいろ動かして https://github.com/nakamasato/practice_terraforming/commit/93dbdc8f01485c022ffee51e9641d4d6e903f953 (コミット名がクソすぎるが) このコミットでとりあえず iam_roleが動くようになった。 (s3はbackendに使ってるせいかなんか必要そうだったから入れといた、iam_policy_attachmentも入れてしまった)

動かすには、

```
gem build practice_terraforming.gemspec
gem install  practice_terraforming-0.1.0.gem
```

とすると、`practice_terrafroming` が使えるようになる

```
practice_terraforming
Commands:
  practice_terraforming help [COMMAND]  # Describe available commands or one specific command
  practice_terraforming iampa           # Iam Policy Attachment
  practice_terraforming iamr            # Iam Role
  practice_terraforming s3              # S3

Options:
  [--merge=MERGE]                                # tfstate file to merge
  [--overwrite], [--no-overwrite]                # Overwrite existing tfstate
  [--tfstate], [--no-tfstate]                    # Generate tfstate
  [--profile=PROFILE]                            # AWS credentials profile
  [--region=REGION]                              # AWS region
  [--assume=ASSUME]                              # Role ARN to assume
  [--use-bundled-cert], [--no-use-bundled-cert]  # Use the bundled CA certificate from AWS SDK
```



### iam_roleにdescriptionを足す (変更の仕方を勉強)

上記に貼ったPRと同じような変更だが、自分でやってみる https://github.com/nakamasato/practice_terraforming/commit/d5e97ebc56f2811a33ec61b01e8d376d605f5cae 

以下の2つを対応させるロジックを `lib/practice_terraforming/resource/iam_role.rb` に書くだけである。

- `aws-sdk`で帰ってくる値を https://docs.aws.amazon.com/sdkforruby/api/Aws/ClientStubs.html で確認
- TerraformのResourceをhttps://www.terraform.io/docs/providers/aws/r/iam_role.html で確認

今回の場合は、description追加のみ

## ついでだから gemもリリース

```
bundle exec rake build
practice_terraforming 0.1.3 built to pkg/practice_terraforming-0.1.3.gem.
```

```
bundle exec rake release
practice_terraforming 0.1.3 built to pkg/practice_terraforming-0.1.3.gem.
Tagged v0.1.3.
Pushed git commits and tags.
Pushing gem to https://rubygems.org...
Successfully registered gem: practice_terraforming (0.1.3)
Pushed practice_terraforming 0.1.3 to rubygems.org
```

https://rubygems.org/gems/practice_terraforming ができた。

# おまけ2

## 問題2

実はもう一つ問題があった。 Terraformingにiam_policy_attachmentは実装されてるが、これは、公式ドキュメントで以下のように書いてある。

> WARNING: The aws_iam_policy_attachment resource creates exclusive attachments of IAM policies. Across the entire AWS account, all of the users/roles/groups to which a single policy is attached must be declared by a single aws_iam_policy_attachment resource. This means that even any users/roles/groups that have the attached policy via any other mechanism (including other Terraform resources) will have that attached policy revoked by this resource. Consider aws_iam_role_policy_attachment, aws_iam_user_policy_attachment, or aws_iam_group_policy_attachment instead. These resources do not enforce exclusive attachment of an IAM policy.

要するに、iam_policy_attachmentで管理してて変更すると、それ以外で管理してたやつが完全に外されるから気をつけてねって話。 `aws_iam_role_policy_attachment`とか`aws_iam_user_policy_attachment`とか`aws_iam_group_policy_attachment`を使えばそういうことは起きないということ。


関連のGithubとかqiitaもある

- [#133 provider/aws : IAM policy attachment/detach bug ?](https://github.com/terraform-providers/terraform-provider-aws/issues/133)n
- [terraform の aws_iam_policy_attachment は使わないほうが無難](https://qiita.com/billthelizard/items/8b54c40351e2ff39afa0)


## `iam_role_policy_attachment` のリソースも追加してPRもだした

`iam_role_policy_attachment`と `aws_iam_user_policy_attachment`と`aws_iam_group_policy_attachment`それぞれでimportできればいいわけだからそれを作ることにした。今回はとりあえず`iam_role_policy_attachment`だけ。

あんまりアクティブじゃなさそうだから、マージされるかわからないけど、 https://github.com/dtan4/terraforming/pull/489 を出した。 `practice_terraforming` で練習してからこのPR出したから、 `practice_terraforming` の方では実装済み。
あとは、 `iam_user_policy_attachment` と `iam_group_policy_attachment` も同じように書いておこう。

# まとめ

- terraformingは便利
- terraformingのリソース変更は、`lib/terraforming/resource/<resource>.rb`と`lib/terraforming/template/tf/<resource>.erb` を `aws-sdk`と Terraformのresourceドキュメントを見て変更すれば良い (テストも)
- リソースの追加は、 terraformingのdocsにあるように `script/generate <resource>` を使ってgenerateしたあとに、aws-sdkとterraformのresourceの間を埋めるロジックを追加する (テストも)