クレデンシャルではなくAssume Roleを利用した外部AWSアカウントへのアクセス

# 概要
データエンジニアをしています。
直近で参画したプロジェクトにて、データマートのデータをファイル形式にして外部のAWS S3へエクスポートする機能を開発する必要がありました。

我々のチームではデータパイプラインをスクラッチで内製開発したという経緯があり、データマートをファイル出力する機能自体は存在していました。
一方で、外部のAWS S3へ出力する機能は存在しなかったので、機能追加とともに主にAssume Roleについて改めて学習する機会となりました。

以降、主にAssume Roleに焦点を当てた内容となります。

# Assume Roleの仕組み

まずはかんたんにAssume Roleの仕組みについて振り返ってみます。

IAMユーザーおよびリソースが、**STS経由でロールの権限を一時的に引き受ける**(Assume)仕組みとなります。引き受けた側は一時的にロールにアタッチされたポリシー相当の権限を行使することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/1233b416-33d2-37eb-80d8-3aea35a619b9.png)

使い所としては下記のようなものがあります。

- 外部AWSアカウントのAWリソースの操作
    - 外部AWSアカウントのロールを一時的に引き受けることで、外部AWSのリソースへアクセスできる
    - 一時的な認証情報が都度発行されるため、**クレデンシャルによるアクセスよりもセキュア**
- クロスアカウントアクセス
    - 例えばプロダクトや環境ごとに複数のAWSアカウントが存在するとき、それぞれのAWSアカウントにてIAMユーザーを設けるのではなく、それぞれのAWSアカウントにて作成したロールを使用して各アカウントへアクセスする

# Assume Roleに対応するためのソースコード改修

ざっくりAssume Roleについて捉えたところで、具体的な必要作業について見ていきます。

今回、Pythonにてアップロードの処理を記述していたため、そこにAssume Roleによる一時的な認証情報の取得処理を加えます。
下記はソースコードの追加内容を簡略化したものです。

```sample.py
# stsへのコネクションを確立
sts_connection = boto3.client('sts')

# stsから一時的なクレデンシャルを発行してもらう
sts_credentials = \
    sts_connection.assume_role(RoleArn="[Assume RoleのARN]", RoleSessionName="[任意のセッション名]")

STS_ACCESS_KEY = sts_credentials['Credentials']['AccessKeyId']
STS_SECRET_ACCESS_KEY = sts_credentials['Credentials']['SecretAccessKey']
SESSION_TOKEN =sts_credentials['Credentials']['SessionToken']

# stsの認証情報を用いて各リソースへ接続
s3 = boto3.resource(
    's3',
    aws_access_key_id=STS_ACCESS_KEY,
    aws_secret_access_key=STS_SECRET_ACCESS_KEY,
    aws_session_token=SESSION_TOKEN
)

# オブジェクトを外部S3へアップロードする処理
# ...
```

# Assume Roleに対応するためのIAMの編集

Assume Roleによる権限委譲のために、ソースコードだけではなくIAMの編集が必要となります。
「ロールを引き受ける側」と「ロール側」に分けて、設定例を示します。

## ロールを引き受ける側

まず、オレンジ色で示された「ロールを引き受ける側」部分の設定を実施します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/dfcf9243-4623-f30e-3ebe-adcf9c127614.png)


Assume Roleするためには、ロールを引き受けるIAMユーザーやリソースに、STSへのアクションを許可するポリシーを設定します。
これは、**STSに対してXXXロールを使用するための認証情報の発行を依頼するため**です。

```ポリシー.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::[AWSアカウントID]:role/[使用したいロール]"
        }
    ]
}
```

## ロール側

次に、オレンジ色で示された「ロール側」部分の設定を実施します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576571/81d58c56-1586-4bdb-4a04-6aae01c024c4.png)


Assume Roleするためには、ロール側に「AAA(ユーザーおよびリソース)にロールを使わせてもいいよ」と明記してあげる必要があります。
これは、**信頼ポリシー**にて表現します。

```信頼ポリシー.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::[AWSアカウントID]:user/[Assume Roleを許可するIAM]",
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

さらに、このロールにて**どのようなアクションを許可するか**も定義をしておきます。ここは要件に合わせて原則最小権限で設定をします。
例えば、AmazonS3ReadOnlyAccessを許可する場合はこのようになります。

```ポリシー.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*",
                "s3:Describe*",
                "s3-object-lambda:Get*",
                "s3-object-lambda:List*"
            ],
            "Resource": "*"
        }
    ]
}
```


# まとめ

- 外部のAWSアカウントへのアクセスをAssume Roleで実施した
- そのために、Assume Roleの概要を把握した後、ソースコードの改修とIAMの編集を実施した
- 既存のデータパイプラインの機能としては、IAMのクレデンシャルキーを用いたデータファイルのエクスポートにのみ対応していたが、Assume Roleによるアクセスも可能となった
    - 様々な要件に対応可能となったうえ、よりセキュアな方式を提案することができるようになった