IAMベストプラクティスにほぼほぼ準拠したIAMユーザ・グループ作成と、MFA デバイスの自己管理を許可するポリシーの取り扱い注意点


# 1. はじめに
* <font color="Red">AWS マネジメントコンソール作業で使用するIAMグループ や IAMユーザー</font>の設計や作成フローを、[IAM ベストプラクティス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html) を参考にして見直してみる。
* けど、IAMグループ や IAMユーザー の作成に関する [IAM ベストプラクティス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html) をすべて採用するのは窮屈なので、採用するものを決めて My IAM プラクティスを作成してみる。
* そして、My IAM プラクティスに準拠したIAM グループ、IAMユーザーの作成フローを構築してみる。
* 例として、新規に参画したAWS マネジメントコンソール作業者で、開発チームのAさんの IAM ユーザーを作成してみる。
* あと、IAMポリシーとIAMグループを Terraform で、IAMユーザーを CloudFormation でコード化する。<br />
※ IAMユーザーの Terraform 化は、[aws_iam_user_login_profile](https://www.terraform.io/docs/providers/aws/r/iam_user_login_profile.html) の pgp_key のところがなんか面倒いので、やむを得ず CloudFormation を使ってる。。

# 2. My IAM プラクティス
#### AWSの IAM ベストプラクティスで採用するもの
* [個々の IAM ユーザーの作成](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#create-iam-users)
 * 参考：[AWSを安全に利用するために - ルートユーザー利用禁止、IAMを使う](https://qiita.com/tonishy/items/9eb772b4a5a338ac6ee6)
<p />
* [IAM ユーザーへのアクセス許可を割り当てるためにグループを使用する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#use-groups-for-permissions)
 * IAMユーザーにポリシーをアタッチするのはNG。「IAMグループ作成 ⇒ IAMグループにポリシーをアタッチ ⇒ IAMユーザーをIAMグループに割り当てる」という流れでIAMユーザーに権限を与える。
 * IAM グループに少なくとも 1 つの IAM ユーザーが存在するかどうかを AWS Config マネージドルール の [iam-group-has-users-check](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/iam-group-has-users-check.html) でチェックすることができる。
 * IAM ユーザーが少なくとも 1 つの IAM グループのメンバーであるかどうかを AWS Config マネージドルール の [iam-user-group-membership-check](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/iam-user-group-membership-check.html) でチェックすることができる。
 * IAMユーザーにポリシーがアタッチされているかどうかを AWS Config マネージドルール の [iam-user-no-policies-check](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/iam-user-no-policies-check.html) でチェックすることができる。何かしらのポリシーがアタッチされていたら、非準拠となる。
<p />
* [AWS 管理ポリシーを使用したアクセス許可の使用開始](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#bp-use-aws-defined-policies)
 * 例えば、開発者用ユーザーには 「AWSにより管理」されている [PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシー の権限を与える。
<p />
* [インラインポリシーではなくカスタマー管理ポリシーを使用する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#best-practice-managed-vs-inline)
 * 「AWSにより管理」されているポリシーリストに適切なポリシーがない場合は、利用者側でポリシーを作成する。
 * このとき、インラインポリシーとしてIAMグループにアタッチするのではなく、「カスタマー管理（ユーザーによる管理）」ポリシーとして作成して、IAMグループにアタッチする。
<p />
* [ユーザーの強力なパスワードポリシーを設定](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#configure-strong-password-policy)
 * IAM ユーザーのアカウントパスワードポリシーが、指定した要件を満たしているかどうかを AWS Config マネージドルール の  [iam-password-policy](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/iam-password-policy.html) でチェックすることができる。<p />
* [MFA の有効化](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#enable-mfa-for-privileged-users)
 * コンソールパスワードを使用するすべての IAM ユーザーについて MFA が有効になっているかどうかを AWS Config マネージドルール の [mfa-enabled-for-iam-console-access](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/mfa-enabled-for-iam-console-access.html) でチェックすることができる。
<p />


#### AWSの IAM ベストプラクティスで採用しないもの
* [最小権限を付与する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)
 * 例えば、開発者には [PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシーをがっつり割り当てる。

#### 次も My IAM プラクティスに追加する
* AWS マネジメントコンソール</font>作業で使用するIAMユーザーには、プログラムによるアクセスを許可しない（アクセスキー ID と シークレットアクセスキー を作成しない）。
* IAMユーザー 自身が AWS マネジメントコンソール のパスワードを変更できるようにする。
* IAMユーザー 自身が MFA を設定/変更できるようにする。


# 3. My IAM プラクティスに準拠したIAMユーザーを作成するフロー
1. 新規に参画したAWS マネジメントコンソール作業者に与える権限(ポリシー)を決定する。2. へ。
<p />
2. 与えるべき権限(ポリシー)を持ったIAMグループがすでにあれば、4.へ。なければ 3. へ。
<p />
3. 与えるべき権限(ポリシー)を持ったIAMグループがなければ、IAMグループを作成して適切な AWS管理ポリシー をアタッチする。<br />もし、適切な AWS管理ポリシーがなければ、ポリシー（インラインポリシーではなく、カスタマー管理ポリシー）を作成してIAMグループにアタッチする。4. へ。
<p />
4. IAMユーザーを作成する。<br />・共有ではなく個々に作る。<br />・ポリシーはアタッチしない。<br />・所属するIAMグループを設定する。



# 4. My IAM プラクティスで開発者AさんのIAMユーザーを作成する例

#### 1. ユーザーの強力なパスワードポリシーを設定する（IAMユーザー全体に影響する設定）

|![20200229-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/60a72130-1f17-b275-9346-b02e4e55da2a.png)|
|:-:|
↓

|![20200229-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/14cf201e-4743-7f26-baa3-84c82244b7d9.png)|
|:-:|
↓

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/59d4213b-abfa-df7a-7640-5207da356663.png)|
|:-:|
↓
こんな感じに設定

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/8a2d45c0-8f6d-6b3e-ab62-2a97a48ec765.png)|
|:-:|


#### 2. ユーザーにパスワード変更を許可する設定（IAMユーザー全体に影響する設定）
パスワードポリシー設定で、「ユーザーにパスワードの変更を許可する」にチェックする。
これにチェックしておけば、AWSによる管理の「IAMUserChangePassword」ポリシーをアタッチしなくても、自身がパスワードを変更できるようになる。

|![20200229-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/0bc8a8ef-448a-fdc0-c4ba-25925233c4e9.png)|
|:-:|

#### 3. 開発者Aさんに PowerUserAccess 権限を与えることにする
* <font color="Red">注意点その１</font>：AdministratorAccess とか、PowerUserAccess ポリシー
 * AWSによる管理の [AdministratorAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator) ポリシーも、 [PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシーも、IAM 操作権限がない。
 * なので、AWS管理者がIAM周りの作業を行うためには、[AdministratorAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator) ポリシーに加えて、IAMFullAccess ポリシーをアタッチする。
 * 開発者Aが自身のMFAを設定/変更できるようにするためには、[PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシーに加えて、[MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html) ポリシーをアタッチする。（マネジメントコンソールのパスワード変更は、パスワードポリシーのところで許可してあれば、IAMUserChangePassword ポリシーはアタッチ不要）

* <font color="Red">注意点その２</font>：[MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html) ポリシー
 * このポリシーについては、数日前にクラスメソッドに[素敵な記事](https://dev.classmethod.jp/cloud/aws/forced_mfa/)が上がっていたので、説明は省く。
 * 気をつけないといけないのは、次の３点。
     1. MFA認証でサインインしないと、アタッチされているポリシーがすべて無効になる。例えば、[PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシーをアタッチしていたとしても、マネジメントコンソールのすべてのAWSサービスのページで権限がないと表示されてしまう。
<p />
     2. IAMグループに [MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html) ポリシーをアタッチして、そのIAMグループに新規IAMユーザーを割り当てて作成するとき、IAMユーザー作成設定で「パスワードのリセットが必要」にチェックを入れてしまうと、初回サインイン時のパスワード変更が権限不足で変更できなくなる。
<p />
     3. このポリシーをアタッチしたIAMユーザーが AWS CLI を使うと、Access Denied される<br />例）<br />＄ aws s3 ls<br />
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied<br />↓<br />次の方法でMFAをパスする必要がある<br />[MFA トークンを使用して、AWS CLI 経由で AWS リソースへのアクセスを認証する方法を教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/authenticate-mfa-cli/)

* 手順例
 * セッショントークンを発行する
<pre>
＄　aws sts get-session-token --serial-number "MFAのarn" --token-code "MFAデバイスから発行される6桁の数字"
</pre>
必要に応じて --profile オプションを付ける場合<pre>
＄　aws --profile hogehoge sts get-session-token --serial-number "MFAのarn" --token-code "MFAデバイスから発行される6桁の数字"
</pre>
MFAのARNは、AWSマネジメントコンソールのIAMから確認できる
![20201105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/971544d3-4782-fe72-a84e-f04da927c598.png)
 * パスが通ると次のような情報が返ってくる。このSessionTokenにはExpiration（有効期限）がある。

```json
{
    "Credentials": {
        "AccessKeyId": "aaaaaaaaaaaaaaaaaaaa",
        "SecretAccessKey": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
        "SessionToken": "cccccccc....",
        "Expiration": "2020-11-05T17:08:48+00:00"
    }
}
```

 * シェルに環境変数を設定する
<pre>
＄ export AWS_ACCESS_KEY_ID=aaaaaaaaaaaaaaaaaaaa
$ export AWS_SECRET_ACCESS_KEY=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
$ export AWS_SESSION_TOKEN=cccccccc....
</pre>
 * これで、トークンが有効期間中 AWS CLIが使えるようになった
<pre>
$ aws --profile hogehoge s3 ls
2020-10-16 19:29:24 AAAA
2020-08-03 22:01:02 BBBB
2020-08-11 17:03:37 CCCC
</pre>

* [MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html) ポリシーをアタッチしている IAMユーザー の初回サインイン時のパスワード変更エラーの例

 * IAMユーザー作成設定の「パスワードのリセットが必要」にチェック<br />
![20200229-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/c32749cb-7911-dc74-2da9-1e0ba2bb2f1a.png)

 * 初回サインイン<br />
![20200229-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/1479394e-2db9-0078-c017-05541a5a32d1.png)

 * パスワード変更画面<br />
アカウントパスワードポリシーに準拠した新しいパスワードを入力<br />
![20200229-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/35607002-96c9-6e4f-c2c2-7981b7902d2b.png)

 * パスワード変更できない！<br />
「ユーザーに iam:ChangePassword を実行する権限がないか、入力されたパスワードが管理者によって設定されたアカウントパスワードポリシーに準拠していません」<br />
![20200229-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/1b48f9a9-365d-82de-fc8f-0c5bd82b2cc4.png)

#### 4. IAMグループを作成する
* 開発者用のIAMグループ「developers」を作成する。
* IAMグループ「developers」に次のポリシーをアタッチする。
 * [PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシー
 * [MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html)  ポリシー

#### 5. IAMユーザーを作成する
* 開発者A用のIAM ユーザー「userA」を作成する。
* 初回サインイン時にパスワード変更しない設定する。
 * [MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html)  ポリシーのため、パスワードを変更できない。
 * アカウントパスワードポリシーのルールで90日後には強制的にパスワードを変更させる。
* IAMグループ「developers」に割り当てる。

#### 6. IAMユーザー作成後の初回のサインイン時にやること
* 初回サインイン<br />
![20200229-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/1479394e-2db9-0078-c017-05541a5a32d1.png)

* サインイン後にEC2のページにいくと、認証エラー
![20200229-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/11b9fa95-479f-94f9-5a79-57b0d77f2229.png)

* なので、何よりも先に MFA 設定を行う
![20200229-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/4130a1fb-7b02-7e78-12e5-6268a5becae6.png)
↓
![20200229-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/52c84f47-63a2-298f-7cd3-66104c7d7573.png)
※ MFA設定方法は割愛

* MFAの設定が終わったら、サインアウトする
![20200229-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/7d28c048-9d4b-9229-b2d7-5da2321205f8.png)

* 再度、サインインする

 * パスワード認証<br />
![20200229-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/1479394e-2db9-0078-c017-05541a5a32d1.png)

 * MFA認証<br />
![20200229-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/75474d45-a03c-d2de-c249-bbe8418671fb.png)

 * 正常サインイン後にEC2のページに行ってみると見えてる<br />
![20200229-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/a5e6d44c-957b-62dc-4389-7df11592a9b2.png)

* マネジメントコンソールのパスワードを変更する
![20200229-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/e9beddd3-3bd7-03fe-d3f5-d4415739be70.png)
↓
![20200229-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/50b0320a-bac3-78b3-9fa5-1a71993b63e8.png)
※ パスワード設定変更方法は割愛

# 5. AWS Config ルールでチェック、チェック！
[AWS Configの初期設定とマネージドルールを Terraform で設定して、非準拠項目を確認して対処してみる](https://qiita.com/tonishy/items/5f1f03057c1b2e56f13a)


# 6. コード化
* [MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html) ポリシー を Terraform で作成する。<br />
※ AWS Documentの方は、AWSアカウントIDのところが「*」になっている。<br />※ 下記の Terraform では、実際のアカウントIDが入るようにしている。

```policy_enable-mfa-device.tf
data "aws_caller_identity" "current" {
}
data "aws_iam_policy_document" "policy-document_enable-mfa-device" {
  version = "2012-10-17"
  statement {
    effect = "Allow"
    actions = [
      "iam:ListUsers",
      "iam:ListVirtualMFADevices"
    ]
    resources = [
      "*"
    ]
  }
  statement {
    effect = "Allow"
    actions = [
      "iam:ListMFADevices"
    ]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/*",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}"
    ]
  }
  statement {
    effect = "Allow"
    actions = [
      "iam:CreateVirtualMFADevice",
      "iam:DeleteVirtualMFADevice",
      "iam:EnableMFADevice",
      "iam:ResyncMFADevice"
    ]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/$${aws:username}",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}"
    ]
  }
  statement {
    effect = "Allow"
    actions = [
      "iam:DeactivateMFADevice"
    ]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:mfa/$${aws:username}",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/$${aws:username}"
    ]
    condition {
      test     = "Bool"
      variable = "aws:MultiFactorAuthPresent"
      values   = ["true"]
    }
  }
  statement {
    effect = "Deny"
    not_actions = [
      "iam:CreateVirtualMFADevice",
      "iam:EnableMFADevice",
      "iam:ListMFADevices",
      "iam:ListUsers",
      "iam:ListVirtualMFADevices",
      "iam:ResyncMFADevice"
    ]
    resources = [
      "*"
    ]
    condition {
      test     = "BoolIfExists"
      variable = "aws:MultiFactorAuthPresent"
      values   = ["false"]
    }
  }
}
resource "aws_iam_policy" "policy_enable-mfa-device" {
  name   = "enable-mfa-device"
  policy = data.aws_iam_policy_document.policy-document_enable-mfa-device.json
}
```

* IAMグループ「developers」を Terraform で作成する。
 * [PowerUserAccess](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_job-functions.html#jf_developer-power-user) ポリシーと、[MFA デバイスの自己管理を許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_iam_mfa-selfmanage.html)  ポリシーの「enable-mfa-device」をアタッチする。<br />
 * 「************」のところは、実際のAWSアカウントIDに置き換えて

```group_developers.tf
resource "aws_iam_group" "developers" {
  name = "developers"
  path = "/"
}
resource "aws_iam_group_policy_attachment" "developers-PowerUserAccess" {
  group      = aws_iam_group.developers.name
  policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
resource "aws_iam_group_policy_attachment" "developers-enable-mfa-device" {
  group      = aws_iam_group.developers.name
  policy_arn = "arn:aws:iam::************:policy/enable-mfa-device"
}
```

* IAMユーザー「userA」を CloudFormation で作成する。
 * Groups は、「developers」を設定する
 * UserNaameは、「userA」を設定する
 * Password は、パスワードポリシーに準拠したもの
 * PasswordResetRequired（パスワードのリセットが必要）は無効にする。

```IAMUser-userA.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: IAM User - userA
Resources:
  IAMUser:
    Type: 'AWS::IAM::User'
    Properties:
      Groups:
        - developers
      Path: /
      UserName: userA
      LoginProfile:
        Password: ************
        PasswordResetRequired: false
Outputs:
  IAMUser:
    Description: IAM User Name
    Value: !Ref IAMUser
```