サービスロールを使ったクロスアカウント連携の設定方法（S3 + Lambda + STS)



サービスロールとSTSを利用してクロスアカウント連携をしたい場合の設定方法です。

この設定ができると、サービスロールによるクロスアカウントのアクセス制御ができると、
IPが固定されないLambdaでも制御できるようになりますし、連携先のアカウントにもキーを発行などする必要がなくなるので
便利です。（ただ、テストのためにちょっと工夫する必要がありますが・・・。）

■こんな感じ

![サービスロールを利用したクロスアカウント連携.jpg](https://qiita-image-store.s3.amazonaws.com/0/36221/12afae9d-ed26-0d74-e696-03ac6fc9f20e.jpeg)


## サンプル実装

### 0. policyの作成

1. 「サービスの選択」＞ "STS"を選択
- 「アクション」の"All STS actions (sts:*)"を選択
- 「リソース」すべてのリソース
- 「Review policy」を押し、名前を入力して「Create Policy」から作成

### 1. サービスロールAの作成

1. サービス＞ IAM ＞ ロール
- ロールの作成
-「信頼されたエンティティの種類を選択」で「AWSサービス」の「Lambda」を選択
- ポリシーに上記 「0. policyの作成」で作成したポリシーを指定
- ロール名を入力し作成


### 2. サービスロールBの作成

1. サービス＞ IAM ＞ ロール
- ロールの作成
- 「信頼されたエンティティの種類を選択」で「AWSサービス」の「Lambda」を選択
- ポリシーに「AmazonS3FullAccess」を選択
- ロール名を入力し作成
- 作成した`サービスロールB`を選択し、「信頼関係」タブをひらく。
- 「信頼関係の編集」をひらき、以下の内容で上書きして「信頼ポリシーの更新」をおして更新

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::`｛アカウントAのID｝`:role/`｛サービスロールAの名前｝`"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

```

### 3. データの準備（バケットの作成とテストファイルの作成）

- `AWSアカウントB`側に適当なバケットを作成する。
- sample.txtとしてアップロードする。

### 4. Lambdaファンクションの作成

- サービス ＞ Lambda ＞ 「関数の作成」
- 「一から作成」から適当に関数を作成。
 - "ランタイム" は Python3.6
 - "ロール"は、`サービスロールA`を指定
- 「関数コード」セクションの「コードエントリータイプ」を"コードをインラインで編集"に設定
 - lambda_functionに書きのコードをコピーして貼る


```
# -*- Coding: utf-8 -*-
from __future__ import print_function

import boto3
import botocore


ROLE_ARN='arn:aws:iam::｛アカウントBのID｝:role/｛サービスロールBの名前｝'

def get_credentials():
    # // STS Client
    sts_client = boto3.client('sts', 'ap-northeast-1')
    assumeRoleObject = sts_client.assume_role(
        RoleArn=ROLE_ARN,
        RoleSessionName="s3-access"
    )
    return assumeRoleObject['Credentials']

def s3_resource():
    credentials = get_credentials()
    s3_resource = boto3.resource(
        's3',
        aws_access_key_id = credentials['AccessKeyId'],
        aws_secret_access_key = credentials['SecretAccessKey'],
        aws_session_token = credentials['SessionToken']
    )
    return s3_resource


def lambda_handler(event, context):

    bucket = '{作成したバケット名}'
    key = 'sample.txt'

    try:
        obj = s3_resource().Object(bucket, key)
        response = obj.get()

        print("CONTENT TYPE: " + response['ContentType'])
        print("Body        : " + response['Body'].read(1024*256).decode('utf-8'))

#        with open('sample.txt','a') as output:
#            chunk = response['Body'].read(1024*256)
#            while chunk:
#                output.write(chunk.decode('utf-8'))
#                chunk = response['Body'].read(1024*256)

        return response['ContentType']
    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e


```

■こんな感じで。

![lambdaファンクションの作成１.PNG](https://qiita-image-store.s3.amazonaws.com/0/36221/445e3974-2c0e-7c0d-1041-60b478c6ff78.png)


■サービスロールがSTSにアクセス権限があることがこれで確認できます
![lambdaファンクションの作成２.PNG](https://qiita-image-store.s3.amazonaws.com/0/36221/e0aa56b1-a7eb-8cab-7b80-c26cb4be4f0b.png)



### 動作確認

・「３．Lambdaファンクションの作成」で作成した Lambdaでテストを実行すれば確認できるはず・・・。

![実行結果サンプル.PNG](https://qiita-image-store.s3.amazonaws.com/0/36221/e2684ef6-cba5-3349-e09a-4cecedeefb38.png)


### この機能のポイント

- リソースを提供する側（`アカウントB`側）が、`アカウントA`のアクションを制御できる。
- 設定する場合は、リソースを提供される側（`アカウントA`)が、連携するサービスロールの`ARN`を
提供する側に教える。
- 今回のようにLambdaが別アカウントのS3を利用するだけであれば、Lambdaのサービスロールに
Amazon S3のアクションを付与する必要はない。


### こんなときは？

- クロスアカウントでS3からS3へファイルを移したいとき。
 - 直接、S3間のコピーはできないので一度、一時ファイルを作成してから、2回にわけて移動させる。
  ⇒この場合は、`サービスロールA`側にもAmazonS3へのアクセス権限が必要になる。
- 開発環境と本番環境どうわける？
 - `ROLE_ARN`をきりかえられるようにすれば自動的に環境先のサービスロールが切り替わります。
  ⇒ `サービスロールB`のポリシーでうまいことアクセスを絞ると良いかと。

以上です。
アカウント二つないと試せないですが、どういう設定をすればいいのかがわかってもらえたらいいな・・。


明日は、@maemoriさんです。