【Terraform】AWS IAM構築用のmoduleを作成してみた

# はじめに
友人と飲みに行った際のトイレで IAM ロールの module の良い構築方法を思いついたので IAM ロールの module を作ることに。
何気ない時にぱっと思いついた案が採用されることってありますよね。。

バージョンは以下の通り。
このバージョン以外での動作は確認してません。

```terraform
terraform {
  required_version = "1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.3.0"
    }
  }
}
```
GitHubにソースコードを上げておりますので、もしよろしければ参考に。
⇒ [こちらをクリック](https://github.com/onishi-app/terraform)

参考にしていただくためにGitHubに残しております。
いかなる損失が発生しても一切責任は負いませんのでよろしくお願いしますね。

また、今回は別の s3 の module も使用しております。
それに関する記事は、[こちらをクリック](https://qiita.com/onishi_820/items/e9f792af67fa42f46a71)

# module
IAM ロールと IAM ポリシー作成用の module について説明していきます。
そんなに複雑なことはやっていないです。

記事が長くなるので一部抜粋して説明していきます。
コードは GitHub からご確認を。

## variables.tf
まずは変数ファイルから。

基本的にはシンプルですが、唯一ややこしいのはこれ。

```terraform:module/iam/variables.tf
variable "policy_statement" {
  type = list(object({
    effect    = string       # Allow もしくは Deny
    actions   = list(string) # 許可 もしくは 拒否するアクションのリスト
    resources = list(string) # 対称のリソースのリスト。バケットARN以降の文字列を指定。
    condition = list(object({
      test     = string       # 評価する IAM 条件演算子の名前
      variable = string       # 条件を評価する値
      values   = list(string) # 条件を適用するコンテキスト変数の名前
    }))
  }))
  default     = []
  description = "IAMポリシーステートメントのリスト"
}
```

どのような値を渡せばいいかは、module 呼び出し元の説明にて記載します。
`effect`, `actions`, `resources`は後述するリソース作成時に必須のパラメータになるので、適切な値を渡す必要があります。
逆に`condition`に関しては設定しなくてもよいので、`condition = []`とすれば設定されません。

## main.tf
outputs.tf に関しては説明不要ですので、次は main.tf について。
main.tfでは IAM ロールと IAM ポリシーの両方を作成しているので分けて説明します。

まずは IAM ポリシーから。

```terraform:module/iam/main.tf
data "aws_iam_policy_document" "this" {    ※１
  dynamic "statement" {
    for_each = var.policy_statement
    content {
      effect    = statement.value.effect
      actions   = statement.value.actions
      resources = statement.value.resources

      dynamic "condition" {
        for_each = statement.value.condition    ※２
        content {
          test     = condition.value.test
          variable = condition.value.variable
          values   = condition.value.values
        }
      }
    }
  }
}
resource "aws_iam_policy" "this" { 
  name        = local.policy_name
  description = var.policy_description
  policy      = data.aws_iam_policy_document.this.json     ※３

  tags = {
    Name = local.policy_name
  }
}
```

### ※１
`data "aws_iam_policy_document" "this" { }`

data ソースを使用して IAM ポリシーのドキュメントを作成しております。
公式ページ ⇒ [Data Sources](https://developer.hashicorp.com/terraform/language/data-sources)

Terraform 実行対象の AWS アカウントやリージョン、または既存リソースのパラメータ取得など、
data ソースを使用することで様々なことが出来ますので是非身につけましょう。

### ※２
`for_each = statement.value.condition`

先ほども説明しましたが、IAM ポリシーを作成するにあたり、`condition` は必須ではないです。
そのため、for_each を使用し値が渡されている時のみ設定するようにしております。

### ※３
`policy = data.aws_iam_policy_document.this.json`

※１で作成した IAM ポリシードキュメントを json 形式で参照しております。
この他にも、ファイルにポリシーを json 形式で記載しておき、下記の形式で記述することも可能です。

`policy = templatefile("ファイルパス", {})`


次に IAM ロール関連の記述の説明です。

```terraform:module/iam/main.tf
data "aws_iam_policy_document" "assume_role" {     ※１
  statement {
    principals {
      type        = var.assume_type
      identifiers = var.assume_identifiers
    }
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
  }
}
resource "aws_iam_role" "this" {
  name                 = local.role_name
  description          = var.role_description
  assume_role_policy   = data.aws_iam_policy_document.assume_role.json     ※２
  managed_policy_arns  = concat(var.managed_policy_arns, [aws_iam_policy.this.arn])     ※３
  max_session_duration = var.max_session_duration

  tags = {
    Name = local.role_name
  }
}
```

### ※１
`data "aws_iam_policy_document" "assume_role" {}`

IAM ポリシーの時にも登場した data ソースです。
今回は IAM ロールに設定する**信頼されたエンティティ**の定義をしております。

どんな感じで設定されるか最後の方で画像を載せてます。

### ※２
`assume_role_policy = data.aws_iam_policy_document.assume_role.json`

※１で定義した data ソースを json 形式で参照しております。


### ※３
`managed_policy_arns = concat(var.managed_policy_arns, [aws_iam_policy.this.arn])`

カスタマー管理の IAM ポリシーと、AWS管理の IAM ポリシーを設定する個所です。

`list(string)`型で渡す必要があるので、変数 managed_policy_arns と
先ほど作成した IAM ポリシー（`aws_iam_policy.this`）の ARN をリストに入れ、
`concat`で二つの list を結合しております。

concat について ⇒ [concat Function](https://developer.hashicorp.com/terraform/language/functions/concat)

# module 呼び出し元
今回は module 呼び出しのサンプルを１つ作成。

```terraform:iam.tf
module "sample_iam_1" {
  source = "./modules/iam"

  prefix  = local.prefix
  subname = "sample-1"

  # IAM Role
  role_description = "sample iam role 1"
  managed_policy_arns = [
    "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
  ]
  assume_type        = "Service"     ※１
  assume_identifiers = ["ec2.amazonaws.com"]

  # IAM Policy
  policy_description = "sample iam policy 1"
  policy_statement = [
    {
      effect    = "Allow"
      actions   = ["s3:*"]     ※２
      resources = [module.s3_logs.bucket_arn]     ※３
      condition = [     ※４
        {
          test     = "NumericLessThanEquals"
          variable = "aws:MultiFactorAuthAge"
          values   = ["3600"]
        }
      ]
      # condition を設定しない場合は condition = []
    }
  ]
}
```


### ※１
`assume_type = "Service"`

信頼されたエンティティに設定するプリンシパルのタイプを定義します。
渡すことのできる値は下記のいずれか。
* AWS
* Service
* Federated
* CanonicalUser
* \*

type が AWS の場合は assume_identifiers に アカウントIDのリストを渡したりします。

### ※２
`actions = ["s3:*"]`

IAM ポリシーで許可するアクションのリスト
サンプルの様に`*`を用いることが可能です。

ただ、マネコンと異なりガバガバすぎるポリシーでも警告されないので注意。

### ※３
`resources = [module.s3_logs.bucket_arn]`

S3バケットを作成する module の返り値である ARN を指定している。
S3に関する記事は、[こちらをクリック](https://qiita.com/onishi_820/items/e9f792af67fa42f46a71)


### ※４
`condition = [・・・]`

condition については先ほども記述しましたが、
`condition = []` とすることで設定しないことも可能です。

# AWS リソース
まずは IAM ポリシー
ポリシーが作成されており内容も問題ないので OK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/a5338867-2dbb-cb98-02a0-f3388a0c0504.png)


次に IAM ロール
今回作成した IAM ポリシーだけでなく、EC2フルアクセスの AWS 管理ポリシーもついているので OK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/4a7488ac-1cd3-009a-f8b8-71716a13ecf9.png)

信頼関係の設定も問題なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/7f75bf31-f357-0e91-1a73-61129534e081.png)

# さいごに

最後までご覧いただきありがとうございました。
IAM ポリシーの内容が適当なので、ちゃんとしたサンプルを今度追加する予定です。
（EC2 の module を作成したタイミングでサンプル追加しようかなとか思っていたり。）

今後もGitHubは更新していく予定なので役に立ったら記事にコメントでもしてくれると嬉しくて泣きます。
module の変更案などもコメントしてください。

ではでは。