15
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS IAM Identity Center で一時的な権限昇格フローを作る

Last updated at Posted at 2025-12-01

メタップスアドベントカレンダー二日目の記事です。

今回はAWS IAM Identity Centerで一時的な権限昇格フローを作るということで、Slack + AWS Chatbot + SSM AutomationでPIMっぽい仕組みの作成方法をご紹介します。

はじめに

AWS の運用をしていると、こんな悩みがありました。

  • 普段は ReadOnly / 開発者権限で十分だけど、たまにだけ管理者権限がほしい
  • とはいえ、常にAdmin権限を付けっぱなしにするのは怖い
  • 「誰がいつ権限を上げたのか」もちゃんと残しておきたい

本記事では、AWS IAM Identity Center(以下 IIC)を使って、

  • 通常時は弱い権限だけ
  • 申請 → 承認 → 一定時間だけ強い権限を付与
  • 時間が来たら自動で権限を剥がす

という、いわゆる一時的な権限昇格(PIMっぽい仕組み)を実現した方法を紹介します。

もともとは Systems Manager Change Manager を使っていましたが、サービス終了に伴い、現在は

  • Slack ワークフロー
  • AWS Chatbot
  • AWS Systems Manager Automation

の組み合わせで同じ体験を再現しています。


ゴール

この記事のゴールは下記です。

  • IICのPermission Set/グループを使って「一時管理者」権限を用意する
  • SSM Automation で「追加 → 待機 → 削除」の一連の流れを自動化する
  • Slackから「申請 → 承認 → 自動実行」までを流せる

アーキテクチャ概要

全体アーキテクチャ図

  • 普段はDevelopersグループだけ
  • 権限昇格中だけIIC-Admin-Tempにも入るイメージ

Change Manager時代との比較

この記事では現行のSlack + AWS Chatbot + SSM Automation構成を中心に紹介していますが、もともとはAWS Systems Manager Change Managerを使っていました。

ざっくりいうと

  • やっていること(IIC のグループに一時的に入れる / 外す)は同じ
  • 「承認フローの UI と実行トリガー」だけをChange ManagerからSlackに置き換えた

という形になります。

ざっくり比較表

項目 Change Manager 時代 現行(Slack + Chatbot + Automation)
申請 UI Change Request画面から入力 Slackワークフローのフォーム
承認 UI Change Managerの承認画面 Slackメッセージの「承認」ボタン
承認ロジック Change Templateで承認ステップを定義 Slackワークフロー側で承認者を限定
実行トリガー 承認後に Change Manager が Document 実行 承認後にChatbot経由で Automation 実行
実行フロー本体 SSM Document(権限昇格フロー)は同じ SSM Document(権限昇格フロー)は同じ
通知 Change Managerの通知設定 Slackメッセージ/Chatbotのレスポンス
スケジュール Change Templateで実行時間を指定可能 必要なら別途EventBridgeなどで実装
仕組み自体の変更管理 Change Manager自体の変更も承認制 管理者のみ変更履歴を確認可

「Change Managerのテンプレートが担っていた仕事を、SlackワークフローとChatbotにバラして移植した」イメージです。
申請はSlackの方がしやすいですが、仕組み自体の変更管理はChange Managerの方がガチガチに組めました。


Change Manager版の構成イメージ

当時の構成は、おおよそ次のような流れでした。

Change Templateに

  • 実行する Document(=一時権限昇格フロー)
  • 承認ステップ(承認者)
  • 申請時の通知
  • 実行スケジュール
    をまとめて定義しておき、Change Requestからそれを呼び出す形でした。

実装ステップ

Step 1. IAM Identity Centerの準備

  1. 一時管理者用 Permission Setを作成

    • コンソールから IIC →「Permission sets」 →「Create」

    • ベースの AWS マネージドポリシーとしてAdministratorAccessなどを選択

      • 実運用では、必要な操作だけに絞ったカスタムポリシーにするのがおすすめ
    • 名前例: Temporary-AdministratorAccess

  2. 一時管理者用グループを作成

    • IIC の「Groups」からIIC-Admin-Tempのようなグループを作成
    • さきほどのTemporary-AdministratorAccessPermission Setを、このグループに紐付け
  3. 対象アカウントへの割り当て

    • 権限昇格したいAWSアカウントに対して、IIC-Admin-Tempグループを割り当て
    • これで、グループにユーザーを追加すると、そのユーザーが対象アカウントで一時管理者権限を使える状態になります

Step 2. SSM Automation Documentの作成

次に、ユーザーをグループへ追加するAutomation Documentを作成します。

やりたいことは、

  1. ユーザーをグループに追加
  2. 指定時間スリープ
  3. ユーザーをグループから削除

という 3 ステップです。

YAML の骨組み

schemaVersion: '0.3'
description: ''
parameters:
  username:
    type: String
  groupname:
    type: String
  sleeptime:
    type: String
assumeRole: arn:aws:iam::123456789012:role/testrole
mainSteps:
  - name: AddUserFromGroup
    action: aws:executeScript
    nextStep: Sleep
    isEnd: false
    inputs:
      Runtime: python3.11
      Handler: script_handler
      Script: |
        import boto3
        import os

        identitystore = boto3.client('identitystore')

        IDENTITY_STORE_ID = 'd-*****'

        def script_handler(event, context):
            username = event['username']  # 例: user@example.com
            group_name = event['group_name']  # 例: "AdminGroup"

            # ユーザーの ID を取得
            user_response = identitystore.list_users(
                IdentityStoreId=IDENTITY_STORE_ID,
                Filters=[{
                    'AttributePath': 'UserName',
                    'AttributeValue': username
                }]
            )
            
            if not user_response['Users']:
                return {"status": "error", "message": f"User '{username}' not found."}
            
            user_id = user_response['Users'][0]['UserId']

            # グループの ID を取得
            group_response = identitystore.list_groups(
                IdentityStoreId=IDENTITY_STORE_ID,
                Filters=[{
                    'AttributePath': 'DisplayName',
                    'AttributeValue': group_name
                }]
            )
            
            if not group_response['Groups']:
                return {"status": "error", "message": f"Group '{group_name}' not found."}
            
            group_id = group_response['Groups'][0]['GroupId']

            # すでにメンバーかチェック(冪等性を保つ)
            existing_memberships = identitystore.list_group_memberships(
                IdentityStoreId=IDENTITY_STORE_ID,
                GroupId=group_id
            )

            for membership in existing_memberships.get('GroupMemberships', []):
                if membership['MemberId']['UserId'] == user_id:
                    return {"status": "ok", "message": f"User '{username}' is already in group '{group_name}'."}

            # グループにユーザーを追加
            identitystore.create_group_membership(
                IdentityStoreId=IDENTITY_STORE_ID,
                GroupId=group_id,
                MemberId={'UserId': user_id}
            )

            return {"status": "success", "message": f"User '{username}' added to group '{group_name}'."}
      InputPayload:
        username: '{{ username }}'
        group_name: '{{ groupname }}'
  - name: Sleep
    action: aws:sleep
    nextStep: DeleteUserFromGroup
    isEnd: false
    inputs:
      Duration: PT{{ sleeptime }}M
  - name: DeleteUserFromGroup
    action: aws:executeScript
    isEnd: true
    inputs:
      Runtime: python3.11
      Handler: script_handler
      Script: |
        import boto3
        import os

        identitystore = boto3.client('identitystore')

        IDENTITY_STORE_ID = 'd-******'

        def script_handler(event, context):
            username = event['username']        # 例: user@example.com
            group_name = event['group_name']    # 例: AdminGroup

            # ユーザーの ID を取得
            user_response = identitystore.list_users(
                IdentityStoreId=IDENTITY_STORE_ID,
                Filters=[{
                    'AttributePath': 'UserName',
                    'AttributeValue': username
                }]
            )

            if not user_response['Users']:
                return {"status": "error", "message": f"User '{username}' not found."}

            user_id = user_response['Users'][0]['UserId']

            # グループの ID を取得
            group_response = identitystore.list_groups(
                IdentityStoreId=IDENTITY_STORE_ID,
                Filters=[{
                    'AttributePath': 'DisplayName',
                    'AttributeValue': group_name
                }]
            )

            if not group_response['Groups']:
                return {"status": "error", "message": f"Group '{group_name}' not found."}

            group_id = group_response['Groups'][0]['GroupId']

            # グループメンバーシップを検索
            memberships = identitystore.list_group_memberships(
                IdentityStoreId=IDENTITY_STORE_ID,
                GroupId=group_id
            )

            for membership in memberships.get('GroupMemberships', []):
                if membership['MemberId']['UserId'] == user_id:
                    identitystore.delete_group_membership(
                        IdentityStoreId=IDENTITY_STORE_ID,
                        MembershipId=membership['MembershipId']
                    )
                    return {"status": "success", "message": f"User '{username}' removed from group '{group_name}'."}

            return {"status": "ok", "message": f"User '{username}' is not a member of group '{group_name}'."}
      InputPayload:
        username: '{{ username }}'
        group_name: '{{ groupname }}'
outputs:
  - AddUserFromGroup.OutputPayload
  - DeleteUserFromGroup.OutputPayload

Documentsの実行ロールには、少なくとも以下のような権限が必要です。

  • identitystore:ListUsers
  • identitystore:ListGroupMemberships
  • identitystore:CreateGroupMembership
  • identitystore:DeleteGroupMembership

Step 3. Slackワークフロー + AWS Chatbot

Change Managerが使えていた頃は、Change TemplateにAutomationをぶら下げて承認フローを実現していました。
現在は、同じ思想をSlack ワークフロー + Chatbotで組み直しています。

3-1. 全体フロー(シーケンス図)


3-2. Slackワークフローの設計例

Slack Workflow Builderで、次のようなフローを作成します。

  1. トリガー

    • 「ショートカット」や「チャンネルトリガー」など、運用しやすい形で
  2. 入力フォーム

    • 対象ユーザー名
    • 付与時間(分)
    • 理由
    • どの権限(グループ)を付与したいか
  3. 承認メッセージ

    • 専用チャンネル(例: #aws-privilege-request)に、申請内容を投稿
    • Block Kit などで「承認」「却下」ボタン付きのメッセージにする
  4. 承認ボタン押下時のアクション

    • AWS Chatbotへ、以下のようなコマンドを送信(パラメータを埋める)

概念的なイメージ:

@Amazon Q ssm start-automation-execution --document-name <Document名> --parameters {“username”:[<入力>],“groupname”:[<入力>],“sleeptime”:[<入力>]} --region ap-northeast-1

※事前にSlackチャンネルにAmazon Qを連携させておいてください。


4-3. AWS Chatbotの設定ポイント

  • 対象の Slackワークスペース/チャンネルにChatbotを紐付ける

  • ChatbotのIAMロール(ガードレール)に以下の権限を付与

    • ssm:StartAutomationExecution
    • ssm:GetAutomationExecution
    • 必要に応じて CloudWatch Logs など

Chatbotの権限が不足していると、コマンドは送れているのに Amazon Qからエラーが返ってきてしまうので最初にしっかり確認しておくと安心です。


動作確認の流れ

  1. 申請者がSlackワークフローから申請を送る
  2. 承認チャンネルに申請内容が投稿される
  3. 承認者が内容を確認し、「承認」ボタンを押す
  4. Chatbot経由でSSM Automationが起動する
  5. IICアクセスポータルを開き、一時管理者用Permission Setが選択できることを確認
  6. 設定した時間を過ぎたら、自動的にアクセス権が消えることを確認

※ IIC のセッション反映の都合で、一度ログアウト/再ログインが必要になる場合がある点には注意してください。


これでSlackワークフローを使って権限の付与、削除を自動で行うことができました。

まとめ

  • 常時管理者権限を持たせるのではなく、
    「申請 → 承認 → 一時的な昇格 → 自動剥奪」のフローにすることでリスクを下げられる
  • IAM Identity Centerのグループにユーザーを出し入れする仕組みを作ると、
    Permission Setの設計を使い回せてシンプル
  • Systems Manager Documentsで、
    「追加 → 待機 → 削除」の一連の流れをコードに閉じ込められる
15
4
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
15
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?