別アカウントのS3バケットを利用する手順

2018.02.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS アカウントが分かれている (クロスアカウント) 環境で、S3 バケットを共有したいときってありますよね。

  • 本番環境と開発環境で AWS アカウントが分かれている
  • 自社のサービスを AWS 環境で提供していて、ユーザの AWS アカウントの S3 バケットを利用する

などなど、企業内/企業間を問わず、利用する機会は少なくないように思います。しかしながら、「どうやって使うの?」とお悩みの方も同じように少なくないのではないでしょうか?私もついこの間まで、そう思っていた側の人です。今回、別アカウントの S3 バケットを使用する機会がありましたので、同じお悩みをお持ちの方に向けて、私が実施した手順についてシェアしたいと思います。

概要

今回の手順で最終的に出来上がる環境は、以下のとおりです。

アカウントA(アカウントID:111111111111)

  • S3 バケットを所有しているアカウントです。
  • IAM ロール "Role-A" は、S3 バケットのアクセス権限を持っています。
  • また、"Role-A" は、アカウントBを信頼ポリシーに登録しています。

アカウントB(アカウントID:999999999999)

  • S3 バケットを使用したいアカウントです。
  • EC2 にアタッチしている IAM ロール "Role-B" は、アカウントAの IAM ロール "Role-A" に対し、一時クレデンシャルを要求する (AssumeRole) 権限を持っています。アカウントAの S3 バケットへのアクセス権限は持っていません。

できること

  • アカウントBの EC2 は、アカウントAの IAM ロール "Role-A" の一時クレデンシャルを取得することで、アカウントAの S3 バケットを操作することが出来るようになります。

動作環境

今回動作検証に利用した環境は下記のとおりです。
Amazon Linux AMI 2017.09.1.20180115 x86_64 HVM GP2

$ aws --version
aws-cli/1.14.9 Python/2.7.12 Linux/4.9.76-3.78.amzn1.x86_64 botocore/1.8.13

本手順では、AWS CLI の credential_source をインスタンスメタデータから取得していますが、これはbotocore 1.8.1以上で利用可能な機能となっています。バージョンが古い場合は awscli のアップデートが必要となります。 (参考:AWS CLIでインスタンスプロファイルからのAssumeRoleが簡単になりました)

手順

アカウントAで S3 バケットを操作できるポリシーを作る

まずアカウントAのコンソールから作業します。

  1. S3 バケットを操作できるポリシーを準備します。あとの手順で、このポリシーをロールにアタッチして利用します。AWS 管理コンソールから IAM のメニューを開き、左のナビゲーションペインで [ポリシー] を選択し、[ポリシーの作成] を開きます。
  2. [JSON] タブを選択して、以下のポリシーをコピーし、JSON テキストボックスに貼り付けます。your-bucketname は環境にあわせて実際の値に置き換えてください。
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "s3:ListAllMyBuckets",
                "Resource": "arn:aws:s3:::*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:GetBucketLocation"
                ],
                "Resource": "arn:aws:s3:::your-bucketname"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:DeleteObject"
                ],
                "Resource": "arn:aws:s3:::your-bucketname/*"
            }
        ]
    }
  3. 編集が完了したら、[Review policy] をクリックします。
  4. ポリシーの名前を入力し、[Create policy] をクリックしてポリシー作成は完了です。

アカウントAでロール (Role-A) を作る

  1. 次にロールを作成しますので、IAM のメニューから左のナビゲーションペインで [ロール] を選択し、[ロールの作成] を開きます。
  2. [別のAWSアカウント] ロールタイプを選択します。[アカウントID] で、アカウントBの ID を入力します。この設定により、アカウントBを信頼ポリシーに登録されます。今回はロールを引き受ける上で、外部 ID や多要素認証 (MFA) を使用しませんので、これらのオプションは選択せずに [次のステップ:アクセス権限] をクリックします。
  3. 先ほど作成したポリシーを選択し、[次のステップ:確認] をクリックします。
  4. 次にロール名を入力します。検証環境では "Role-A" としました。[ロールの作成] をクリックして作成は完了です。
  5. ロールの一覧から作成したばかりのロールをクリックし、概要を開きます。アカウントB側の作業で必要となりますので、[ロール ARN] を控えておきます。

アカウントBで AssumeRole できるポリシーを作る

ここからはアカウントBでの作業になります。

  1. アカウントAの "Role-A" に一時クレデンシャルを要求 (AssumeRole) できるポリシーを準備します。あとの手順で、このポリシーをロールにアタッチして利用します。AWS 管理コンソールから IAM のメニューを開き、左のナビゲーションペインで [ポリシー] を選択し、[ポリシーの作成] を開きます。
  2. [JSON] タブを選択して、以下のポリシーをコピーし、JSON テキストボックスに貼り付けます。Resource"arn:aws:iam::111111111111:role/Role-A" は、先ほど控えたアカウントAのロール ARN に置き換えてください。
    {
      "Version": "2012-10-17",
      "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::111111111111:role/Role-A"
      }
    }
  3. 編集が完了したら、[Review policy] をクリックします。
  4. ポリシーの名前を入力し、[Create policy] をクリックして作成は完了です。

アカウントBでロール (Role-B) を作る

  1. 次にロールを作成しますので、IAM のメニューから左のナビゲーションペインで [ロール] を選択し、[ロールの作成] を開きます。(既に EC2 にロールを割り当てている場合は作成不要です。EC2 に割り当て済みのロールに、先ほど作成したポリシーをアタッチしてください。)
  2. [AWSサービス] ロールタイプを選択します。[このロールを使用するサービス] および [ユースケースの選択] はいずれも [EC2] を選択 [次のステップ:アクセス権限] をクリックします。
  3. 先ほど作成したポリシーを選択し、[次のステップ:確認] をクリックします。
  4. 次にロール名を入力します。検証環境では "Role-B" としました。[ロールの作成] をクリックして作成は完了です。

ロールを新規に作成した場合は、EC2 にアタッチします。

アカウントBの EC2 でクレデンシャル取得の設定する

  1. アカウントBの EC2 にログインします。まず、通常の状態でアカウントAの S3 バケットが参照できないことを確認してみます。your-bucketname は環境にあわせて置き換えてください。
    $ aws s3 ls s3://your-bucketname
    An error occurred (AccessDenied) when calling the ListObjects operation: Access Denied
  2. それではクレデンシャルの設定をしましょう。ログインユーザのホームディレクトリ以下に、 ./aws/credentials を作成し、下記のように設定します。
    1行目の [role-a-profile] はプロファイル名になりますので任意の名称をつけます。
    2行目の role_arn はアカウントAのロール作成時に控えたロール ARN に置き換えてください。
    3行目の credential_sourceEc2InstanceMetadata を指定します。
    この設定により、従来のような一時クレデンシャルを $ aws sts assume-role で取得して環境変数に設定して・・・といった作業が不要になります。

    ~/.aws/credentials

    [role-a-profile]
    role_arn = arn:aws:iam::111111111111:role/Role-A
    credential_source=Ec2InstanceMetadata
  3. クレデンシャルの設定が完了しましたら、アカウントAの S3 バケットが参照できることを確認します。--profile で先ほど設定したプロファイルを呼び出すことで、バックグラウンドで一時クレデンシャルが取得され、アカウントAの S3 バケットが参照できるようになります。
    $ aws s3 ls s3://your-bucketname --profile role-a-profile
    2018-02-21 09:33:32    1182461 test000.txt

アカウントBからアカウントAの S3 のオブジェクト操作してみる

  1. S3 バケットにオブジェクトを作成します。
    $ aws s3 cp test001.txt s3://your-bucketname/test001.txt --profile role-a-profile
    upload: ./test001.txt to s3://your-bucketname/test001.txt
  2. アカウントBから作成されたオブジェクトですが、"Role-A" の一時クレデンシャルを使用していますので、オブジェクトの所有者はアカウントAのままです。[他のAWSアカウントのアクセス] に何も表示されていないことから判断できます。
  3. S3 バケット内のオブジェクトを削除します。
    $ aws s3 rm s3://your-bucketname/test001.txt --profile role-a-profile
    delete: s3://your-bucketname/test001.txt
    $ aws s3 ls s3://your-bucketname --profile role-a-profile
    2018-02-21 09:33:32    1182461 test000.txt

    問題なく別アカウントの S3 バケットを操作できることが確認できました!

さいごに

これまで AssumeRole についてなんとなくしか理解していませんでしたが、ひと通り手を動かして作業してみて、ようやく理解が深まりました。(手を動かすって大事ですね!)また、クロスアカウントについて参考にした弊社ブログの過去記事を見ると、以前までは一時クレデンシャルの期限切れなど、考慮する点が多々あったようですが、プロファイルの利用によって随分と簡単に設定できるようになったようですね!
今日できないことが、1年後、半年後、もしかしたら明日!?には出来るようになっているかもしれない。AWS の機能アップデートってステキですね。そして、そういった情報を多くのユーザにお届けできるよう、これからもブログ頑張ります!

以上、丸毛でした!

参照