AssumeRoleを使って他のロールの情報を取得する方法

はじめに

STSのAssumeRoleを利用して他のロールの一時クレデンシャルを取得し
CostExplorerの情報取得を行う機会があったが、少々複雑だったため書いておきます。

行ったこと

  1. 環境AのAWSのサービス(今回はCostExplorer)にアクセスし情報を取得した。
  2. AssumeRoleの機能を使い、環境Bの一時クレデンシャルを取得し、 
    ロールBに許可されたAWSのサービス(今回はCostExplorer)にアクセスし情報を取得した。(下図)
  3. slackへ送信した。

用意するもの

以下のものを用意し、lambda関数を実行すれば環境Aと環境BのCostExplorerを参照し、取得した価格情報が指定したSlackのURL先へと送信されます。(アカウントIDなど任意の物に書き直す必要があり、対応したSlackのURLも用意する必要があります)

  • 環境A(ローカル)
    ロールA(STSのAssumeRoleでロールBを指定したポリシー+環境Aの情報取得に必要なポリシーがアタッチされたもの)
    lambda関数

  • 環境B(リモート)
    ロールB(信頼関係の設定でロールAを指定+環境Bの情報取得に必要なポリシーがアタッチされたもの)

環境A(ローカル)

lambda関数

import boto3
from datetime import datetime,timedelta
import re
import urllib.request
import json
from boto3.session import Session


# 異なるAWSアカウント/ロールのクレデンシャル取得を実行する
def sts_assume_role(account_id,role_name):
    role_arn = "arn:aws:iam::" + account_id + ":role/" + role_name
    session_name = "foobar"
    region = "ap-northeast-1"

    client = boto3.client('sts')

    # AssumeRoleで一時クレデンシャルを取得
    response = client.assume_role(
        RoleArn=role_arn,
        RoleSessionName=session_name
    )

    session = Session(
        aws_access_key_id=response['Credentials']['AccessKeyId'],
        aws_secret_access_key=response['Credentials']['SecretAccessKey'],
        aws_session_token=response['Credentials']['SessionToken'],
        region_name=region
    )

    return session

def get_cost(client):
        today_year = datetime.now().year
        today_month = datetime.now().strftime('%m')
        today_day = datetime.now().strftime('%d') #today_str
        yesterday_day = datetime.now() - timedelta(days=1) #yesterday_datetime
        yesterday_month=yesterday_day.strftime('%m')
        yesterday_year=yesterday_day.year
        yesterday_day = yesterday_day.strftime('%d') #yesterday_str

        end = str(today_year) + "-" + str(today_month) + "-" + str(today_day) #today_str_year-month-day
        start = str(yesterday_year) + "-" + str(yesterday_month) + "-" + str(yesterday_day) #yesterday_str_year-month-day error_point

        response = client.get_cost_and_usage(
                TimePeriod={
                        'Start': start,
                        'End' :  end,
                },
                Granularity='DAILY',
                Metrics= [
                        'UnblendedCost'
                ]
        )
        answer = re.findall('[0-9]+.[0-9]+',str(response))
        return answer,start

def lambda_handler(event,context):
        region1 = "ap-northeast-1"
        region2 = "us-east-1"

        client = boto3.client(
        'ce',
        region_name = region2
        )

        account_id = "環境BアカウントID"
        role_name = "ロールB"
        answer,start=get_cost(client)
        # print(answer[2])
        #イベントで指定されたAWSアカウント/ロールのクレデンシャルを取得
        session = sts_assume_role(account_id,role_name)
        client2 = session.client('ce')

        answer2,start=get_cost(client2)
        # print(answer2[2])

        obj={"text":"環境A   : "+start+"=>"+answer[2]+"$"+"\n環境B : "+start+"=>"+answer2[2]+"$"}
        json_data = json.dumps(obj).encode("utf-8")
        request = urllib.request.Request("slackのURL", data=json_data, method="POST", headers={"Content-Type" : "application/json"})
        with urllib.request.urlopen(request) as response:
                 response_body = response.read().decode("utf-8")

ロールA– ポリシー1(環境Aの情報取得に必要なポリシー)
              |-ポリシー2(STSのAssumeRoleでロールBを指定したポリシー)

  • ポリシー1(環境Aの情報取得に必要なポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "sns:*",
                "aws-portal:*Usage",
                "aws-portal:*PaymentMethods",
                "aws-portal:*Billing",
                "ce:*"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}
  • ポリシー2(STSのAssumeRoleでロールBを指定したポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::環境BアカウントID:role/ロールB"
        }
    ]
}

環境B(リモート)

ロールB– ポリシー(環境Bの情報取得に必要なポリシー)
              |-信頼関係(AsuumeRole,ロールAを指定)

  • ポリシー(環境Bの情報取得に必要なポリシー)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "aws-portal:*Billing",
                "aws-portal:*Usage",
                "aws-portal:*PaymentMethods"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "ce:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "sns:*",
            "Resource": "*"
        }
    ]
}
  • 信頼関係(AsuumeRole,ロールAを指定)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::環境AアカウントID:role/ロールA"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

送信結果

環境A   : 2018-11-18=>〇〇〇〇〇〇〇$
環境B   : 2018-11-18=>〇〇〇〇〇〇〇$

参考

1つのAWSアカウントで複数のAWS環境の操作を行う