Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@syoimin

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

More than 1 year has passed since last update.

概要

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 にログインしなくても誰でもリソース情報が確認できるようになるので便利です。
特にインスタンスが何台動いているのか、無駄なインスタンスが動いていないかをすぐに把握できます。

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

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
syoimin
Web 系エンジニアです。インプットしたらアウトプットする癖つける練習中です。 基本何でもやりますがデザインできないのでバックエンドエンジニアにしておきます。 ReactNative/Vue/AWS/DevOps/CICD/Docker/Laravel/Python/Java

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?