認証戦略を用いてIAMユーザを作成する

随分前に[TerraformでIAMグループ・ユーザを作成する](https://qiita.com/ldr/items/427f6cf7ed14f4187cd2)をリファクタリングしたのでUpdateします。

認証戦略としては、属性ベースのアクセスコントロール (ABAC) 、ロールベースのアクセスコントロール (RBAC) がありますが、ABACもRBACもすべてのアクションに対して設定可能なわけではないので適宜Mixさせます。
共通しているのは **「権限のガードレールによりオペミスや悪意のあるアクセスを防ぎ、重大インシデントを未然に防止する」** です。

- 基本設計
![Untitled (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58788/07a01052-c164-881a-cd03-0f749f136ea3.png)

- 下記ではIAMユーザーがメンバーになれるグループ数は上限緩和ができない10に設定されているので、注意が必要。

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html#reference_iam-quotas-entities

![Untitled (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58788/9679e988-032f-6d49-371f-5f7984242ea1.png)

---
- Dir構成
```zsh
iam
├── group-path
│   ├── dev-user
│   │   ├── iam_user.tf
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variable.tf
├── policy
│   ├── ip-restriction
│   │   ├── iam_policy.tf
│   │   └── main.tf
│   ├── password-mfa
│   │   ├── iam_policy.tf
│   │   └── main.tf
│   └── switch-role
│       ├── dev
│       │   ├── iam_policy.tf
│       │   └── main.tf
├── role
│   ├── dev_role
│   │   ├── iam_role.tf
│   │   └── main.tf
```

- dev-userというPATHの括りでuser作成する

```zsh
iam/group-path/dev-user
├── iam_user.tf
├── main.tf
├── output.tf
└── variables.tf
```

<details><summary>iam_user.tf</summary>

```hcl
data "aws_caller_identity" "current" {}

resource "aws_iam_user" "dev_user" {
  for_each = var.dev_users

  name          = each.value.name
  path          = each.value.path
  force_destroy = each.value.force_destroy
}

resource "aws_iam_user_login_profile" "dev_user_login_profile" {
  for_each = { for k, v in var.dev_users : k => v if v.create_login_profile }

  user                    = each.value.name
  pgp_key                 = "keybase:development"
  password_reset_required = true
  password_length         = "10"

  lifecycle {
    ignore_changes = [
      password_length,
      password_reset_required,
      pgp_key,
    ]
  }
}

resource "aws_iam_access_key" "dev_user_access_key" {
  for_each = var.dev_users

  user    = each.value.name
  pgp_key = "keybase:development"
}

resource "aws_iam_user_policy_attachment" "dev_user_policy_attach_1" {
  for_each = var.dev_users

  user       = each.value.name
  policy_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/ip_restriction_policy"
}

resource "aws_iam_user_policy_attachment" "dev_user_policy_attach_2" {
  for_each = var.dev_users

  user       = each.value.name
  policy_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/password_mfa_policy"
}
```
</details>

<details><summary>output.tf</summary>

```hcl
output "user1_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user1"].encrypted_secret
}

output "user1_id" {
  value = aws_iam_access_key.dev_user_access_key["user1"].id
}

output "user1_user" {
  value = aws_iam_access_key.dev_user_access_key["user1"].user
}

output "user1_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user1"].encrypted_password
}

output "user2_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user2"].encrypted_secret
}

output "user2_id" {
  value = aws_iam_access_key.dev_user_access_key["user2"].id
}

output "user2_user" {
  value = aws_iam_access_key.dev_user_access_key["user2"].user
}

output "user2_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user2"].encrypted_password
}

output "user3_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user3"].encrypted_secret
}

output "user3_id" {
  value = aws_iam_access_key.dev_user_access_key["user3"].id
}

output "user3_user" {
  value = aws_iam_access_key.dev_user_access_key["user3"].user
}

output "user3_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user3"].encrypted_password
}
```
</details>

<details><summary>variables.tf</summary>

```hcl
variable "dev_users" {
  type = map(any)

  default = {
    user1 = {
      name                 = "user1"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user2 = {
      name                 = "user2"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user3 = {
      name                 = "user3"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user4 = {
      name                 = "user4"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user5 = {
      name                 = "user5"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
  }
}
```
</details>

- SecretAccessKeyとPasswordの復号化
```zsh
% echo “xxxxxxxxxxx” | base64 -d | gpg -r name
```

---

- dev_userがスイッチ可能なRole
```zsh
iam/role
├── dev_role
│   ├── iam_role.tf
│   └── main.tf
```

<details><summary>iam_role.tf</summary>

```hcl
resource "aws_iam_role" "dev_role" {
  name                 = "dev_role"
  max_session_duration = "43200" // 12hour

  assume_role_policy = jsonencode({
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user1",
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user2",
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user3"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
 })
}

resource "aws_iam_policy" "dev_ssm_policy" {
  name        = "dev_ssm_policy"
  description = "dev_ssm_policy"

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession",
                "ssm:SendCommand"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/*"
            ],
            "Condition": {
                "StringLike": {
                    "ssm:resourceTag/Service": [
                        "*"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetConnectionStatus",
                "ssm:DescribeInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/$${aws:username}-*"
            ]
        }
    ]
}
EOF
}
```
</details>

- 接続元ip制限

```zsh
iam/policy/ip-restriction
├── iam_policy.tf
└── main.tf
```
<details><summary>iam_policy.tf</summary>

```hcl
resource "aws_iam_policy" "ip_restriction_policy" {
  name        = "ip_restriction_policy"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "***.***.***.***/32",
                        "***.***.***.***/32"
                    ]
                }
            },
            "Resource": "*"
        },
        {
            "Effect": "Deny",
            "NotAction": [
                "iam:*"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
  })
}

```
</details>

```zsh
iam/policy/switch-role/dev
├── iam_policy.tf
└── main.tf
```
<details><summary>iam_policy.tf</summary>

```hcl
resource "aws_iam_policy" "dev_user_switch_role_policy" {
  name = "dev_user_switch_role_policy"
  path = "/dev/"

  policy = jsonencode({
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/dev_role"
  }
 })
}
```
</details>

- パスワード変更とMFA設定許可

```zsh
iam/policy/password-mfa
├── iam_policy.tf
└── main.tf
```

<details><summary>iam_policy.tf</summary>

```hcl
resource "aws_iam_policy" "password_mfa_policy" {
  name        = "password_mfa_policy"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
    	{
    		"Resource": [
                "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/$${aws:username}"
    		],
    		"Action": [
          "iam:Get*",
          "iam:List*",
          "iam:ChangePassword",
    			"iam:CreateAccessKey",
    			"iam:CreateVirtualMFADevice",
    			"iam:DeactivateMFADevice",
    			"iam:DeleteAccessKey",
    			"iam:DeleteVirtualMFADevice",
    			"iam:EnableMFADevice",
    			"iam:GetAccountPasswordPolicy",
    			"iam:UpdateAccessKey",
    			"iam:UpdateSigningCertificate",
    			"iam:UploadSigningCertificate",
    			"iam:UpdateLoginProfile",
    			"iam:ResyncMFADevice"
    		],
    		"Effect": "Allow"
    	},
     {
     "Resource": "*",
       "Action": [
         "iam:List*"
       ],
       "Effect": "Allow"
     }
  ]
 })
}
```
</details>