まだPCにIAMアクセスキー保存してるの？ ベスプラに沿って、AWS CLIやSDKから安全に開発しよう！

AWSで開発をする際に必須のわりには、CLIやSDKからAWSアカウントへのアクセス設定ってかなり複雑ですよね。

私も未だによく混乱しては調べ直しているので、改めてベスプラを整理してまとめておきます。


# AWS Organizations & IAM Identity Centerが使える場合

理由がなければこちらを採用しましょう。
個人検証用のAWSアカウントでも、用途ごとにアカウントをつど発行して使い捨てにできるので、安全ですし便利です。


## 理想的なアクセス方式

個人用IAM IICユーザー（Assume Role権限のみ） -> Assume Roleを実施（MFA認証必須） -> 作業対象AWSアカウント（作業に必要なIAMロールを利用）

![スクリーンショット 2025-03-16 17.24.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/019fa12a-1482-457d-a14b-de6935c8314e.png)


### 補足： IAM IICとは？

IAMユーザーとは別に、IIC自体にユーザーアカウントを作成することができます。このIICユーザーには、IAMポリシーの代わりにIICの「許可セット」を設定することができ、これによってOrganization内の他のAWSアカウントへのアクセス権限を付与します。

このIICユーザーを、同じOrganization配下の各AWSアカウントにAssume Roleするためのスイッチ元アカウントとして使うことができます。


## 設定手順

### 管理用AWSアカウントでAWS Organizationsをセットアップ

- AWS Organizationsを有効にする
- 作業用AWSアカウントを組織に追加する

### 管理用AWSアカウントでIAM IICをセットアップ

- IAM IICを有効化する
- IAM IICユーザーを作成する
- IICユーザーに許可セットを設定する

### IICユーザーにMFAをセットアップ

- AWSアクセスポータルでIICユーザーにMFAデバイスを設定する

### 作業用PCに認証情報をセットアップ

- AWS CLI v2をインストールする
- aws configure ssoでSSO認証情報を設定する

```ini: ~/.aws/config（自動作成）
[sso-session my-sso]
sso_start_url = https://xxxxxx.awsapps.com/start
sso_region = ap-northeast-1
sso_registration_scopes = sso:account:access

[profile admin]
sso_session = my-sso
sso_account_id = XXXXXXXXXX
sso_role_name = AdministratorAccess
region = ap-northeast-1
```

## 毎回のアクセス手順

### GUI（マネジメントコンソール）の場合

- AWSアクセスポータルにアクセスする
- 作業用AWSアカウントの、必要な許可セットを選択してアクセスする

### AWS CLIの場合

- `aws sso login --profile <プロファイル名>` で作業用AWSアカウントの一時権限を取得する
- 実施したいコマンドに `--profile <プロファイル名>` を付けて実行する

```zsh: S3バケットを一覧表示するコマンドの例
aws s3 ls --profile admin
```

### AWS SDKの場合（Boto3の例）

- Boto3の `client` メソッドを実行する前に、プロファイル名を指定してAssumeRoleを行う

```python: test.py
import boto3

# AssumeRoleを実施
session = boto3.Session(profile_name="admin")

# 目的の処理を実行
s3 = session.client("s3")
response = s3.list_buckets()
print(response)
```


# AWS Organizations & IAM Identity Centerが使えない場合

お仕事でリセラー制約下のAWSアカウントを使う場合など、IAM IICが利用できない場合のベスプラも紹介します。


## 理想的なアクセス方式

個人用IAMユーザー（Assume Role権限のみ） -> Assume Roleを実施（MFA認証必須） -> 作業対象AWSアカウント（作業に必要なIAMロールを利用）

![スクリーンショット 2025-03-16 17.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/bf55e384-c99d-4b61-8ad5-403909c18229.png)


※AWSアカウントを分けているのは、個人用IAMユーザーを作業先アカウントごとに複数持たなくてもいいよう、1箇所に統一するためです。


## 設定手順

### 管理用AWSアカウントに個人用IAMユーザーを作成

- 個人用IAMユーザーを作成する（下記2ポリシーを適用）

```json: AllowAssuumeRole
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<アカウントID>:role/<遷移先ロール名>",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        }
    ]
}
```

```json: AllowManageOwnMFA
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowListActions",
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowUserToCreateVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "Sid": "AllowUserToManageTheirOwnMFA",
            "Effect": "Allow",
            "Action": [
                "iam:EnableMFADevice",
                "iam:GetMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Sid": "AllowUserToDeactivateTheirOwnMFAOnlyWhenUsingMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice"
            ],
            "Resource": [
                "arn:aws:iam::*:user/${aws:username}"
            ],
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        },
        {
            "Sid": "BlockMostAccessUnlessSignedInWithMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ListUsers",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}
```

### 作業用AWSアカウントにIAMロールを作成

- 作業用IAMロールを作成する

```json: 信頼ポリシーの例
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<遷移元アカウントID>:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
                }
            }
        }
    ]
}
```

### IAMユーザーにMFAをセットアップ

- 管理用AWSアカウントのマネジメントコンソールに個人用IAMユーザーでサインインし、MFAデバイスを設定する

### 作業用PCに認証情報をセットアップ

- AWS CLI v2をインストールする
- `aws configure --profile <プロファイル名>` でIAM認証情報を設定する

```ini: ~/.aws/credentialの例（自動作成）
[profile assume-only]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <シークレットアクセスキー>
```

- configファイルに作業用IAMロールの情報を追記する

```ini: ~/.aws/configの例（自動作成＋手動追記）
[profile assume-only]
region = ap-northeast-1

[profile admin-role]
role_arn = arn:aws:iam::<アカウントID>:role/<遷移先ロール名>
mfa_serial = arn:aws:iam::<アカウントID>:mfa/<MFAデバイス名>
source_profile = assume-only
region = ap-northeast-1
```

## 毎回のアクセス手順

### GUI（マネジメントコンソール）の場合

- 管理用AWSアカウントに個人用IAMユーザーでサインインする
- 作業用AWSアカウントのIAMロールへスイッチロールする

### AWS CLIの場合

- 作業コマンド実行時に `--profile` オプションで作業用IAMロールのプロファイル名を指定するだけ

※スイッチ元の認証情報もcredentialsファイルに記録されているため、CLI利用時に認証コマンドを手動で2段階実施する必要はありません。

### AWS SDKの場合（Boto3の例）

- Boto3の `client` メソッドを実行する前に、プロファイル名を指定してAssumeRoleを行う

```python: test.py
import boto3

# AssumeRoleを実施
session = boto3.Session(profile_name="admin-role")

# 目的の処理を実行
s3 = session.client("s3")
response = s3.list_buckets()
print(response)
```

※MFA認証が必要な場合は、ターミナル上でMFAコードを尋ねてくれます。


# 次のステップ

このへんの作業を便利にしてくれるOSS「AWSume」があります！

https://dev.classmethod.jp/articles/awsume/

私はこれに1Password用のプラグインを設定して、MFA入力まで自動化しました。

※ちなみに読み方は不明です。脳内ではアウシュームと呼んでます。