0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Lambda で Switch ロールに対応した複数アカウントのインスタンス一覧の取得

Last updated at Posted at 2019-12-08

概要

AWS を複数アカウントで運用する場合、Switch ロールでアカウントユーザを切り替えることがベストプラクティスになっています。
これはマスターアカウントでユーザを一元管理し、マスターアカウントに紐づく子アカウントではユーザを作成せずロールの切り替えによって権限管理ができるからです。

この方法を利用することでユーザの管理はマスターで一元管理できるので便利ですが、一方で、不用意なユーザを子アカウントに作成しないといった制限もかかります。ユーザを作成できなければ不用意なシークレットキー、アクセスキーの管理からも開放されます。

一方、プログラムから AWS リソースにアクセスする場合、一般的にシークレットキー、アクセスキーを発行することがあると思います。しかし、Switch ロールを利用する場合は子アカウントへのユーザ作成を極力避けなければなりません

今回はこのような場合でもリソースにアクセスできるような **「Switch ロールに対応した複数アカウントのインスタンス一覧の取得」**の例を解説します。

仕組み

Lambda を利用して、引数で渡されたアカウントIDと切り替え先のロール名によって、複数アカウントの切り替えを行います。
また、切り替え先のロールには、インスタンス一覧が取得できる権限を付与しておきます。

lambda コード

今回の Lambda コードです。Python で書いています。

handler.py
import boto3
import json
from boto3.session import Session
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
# 引数で渡された AWS アカウント/ロールのクレデンシャルを取得
def sts_assume_role(account_id, role_name, region):
    role_arn = "arn:aws:iam::" + account_id + ":role/" + role_name
    session_name = "test"

    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 lambda_handler(event,context):
    print("Received event: " + json.dumps(event, indent=2))

    account_id = event.get('body')['account']
    role_name = event.get('body')['role']
    region = event.get('body')['region']
 
    # account_id = event['account']
    # role_name = event['role']
    # region = event['region']
    
    # イベントで指定されたAWSアカウント/ロールのクレデンシャルを取得
    session = sts_assume_role(account_id, role_name, region)
 
    ec2 = session.client('ec2')
 
    # 取得したクレデンシャルを使ってインスタンスリソースを取得
    instances = ec2.describe_instances(MaxResults=123)["Reservations"]
    # logger.info(instances)

    instanceList = []

    # インスタンス一覧を取得
    for i in instances:
        for j in i["Instances"]: 
            values= {
                'InstanceId':        j.get("InstanceId"),
                'Status':            j.get("State")["Name"],
                'InstanceType':      j.get("InstanceType"),
                'PrivateIpAddress':  j.get("PrivateIpAddress"),
                'SubnetId':          j.get("SubnetId"),
                'AvailabilityZone':  j.get("Placement")["AvailabilityZone"],
                'PublicIpAddress':   j.get("PublicIpAddress"),
                'ImageId':           j.get("ImageId"),
                'KeyName':           j.get("KeyName"),
                'Tags':              j.get("Tags"),
            }

            instanceList.append(values)

    return {
        'isBase64Encoded': False,
        'statusCode': 200,
        'headers': {},
        'body': instanceList
    }

マスターアカウント(1111111111)のロールです。
このロールは Lambda にアタッチします。
複数の AWS の子アカウントに対して lambda から Switch ロールが実行できるように権限を付与します。

AWSGetResourceByLambdaReadOnlyRole
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::99999999999:role/AWSGetResourceByLambdaReadOnlyRole",
            "Effect": "Allow"
        },
        {
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::88888888888:role/AWSGetResourceByLambdaReadOnlyRole",
            "Effect": "Allow"
        },
    ]
}

子アカウントのロールです。
信頼関係をマスターアカウントのIDからのアクセスを許可するように付与します。
子アカウントのロールは、単にEC2のリソースを取得できる権限を付与しているだけです。

信頼されたエンティティ
arn:aws:iam::1111111111:role/AWSGetResourceByLambdaReadOnlyRole
AWSGetResourceByLambdaReadOnlyRole
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:Describe*",
                "ec2:SearchTransitGatewayRoutes",
                "ec2:Get*"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

解説

注目すべき点は下記のコードです。

handler.py
def sts_assume_role(account_id, role_name, region):
    role_arn = "arn:aws:iam::" + account_id + ":role/" + role_name
    session_name = "foobar"

    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

引数にとったアカウントIDとロール名によって一時的な Switch ロール用のクレデンシャルを発行できます。この一時クレデンシャルを利用して、Switch ロール先の子アカウントのリソース情報を取得することができます。

あとは、シークレットキー、アクセスキーとを用いたときの AWS SDK の利用方法と同じです。必要なリソース情報を取得し、煮るなり焼くなりしましょう。

私は、これを API GateWay にデプロイし外部から API を叩けるようにし、スプレッドシートでインスタンス一覧を管理するようにしています。

まとめ

やっぱり表計算で AWS リソースを管理したくなるのが、エンジニアのさがなのですかね。
一度スプレッドシートで管理しておけば、AWS にログインしなくても誰でもリソース情報が確認できるようになるので便利です。
特にインスタンスが何台動いているのか、無駄なインスタンスが動いていないかをすぐに把握できます。

また、インフラへの問い合わせも減って、苦労も削減できてみんなはっぴーです。
みんなでインフラの問い合わせを減らしましょうー!!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?