MNTSQ Techブログ

リーガルテック・カンパニー「MNTSQ(モンテスキュー)」のTechブログです。

Entra ID ユーザでの AWS への SSO ログインを IAM Identity Center で実現する

MNTSQ Tech Blog TOP > 記事一覧 > Entra ID ユーザでの AWS への SSO ログインを IAM Identity Center で実現する

はじめに

AWS Organizations にて複数の AWS アカウントを管理する場合において、各アカウントへどのようにアクセスするかは色々と検討の余地があると思います。 弊社では長らくこれを以下のような手法で運用していました。

  • 踏み台的用途の AWS アカウントを1つ用意し、そこに作業者が使う IAM ユーザ及びスイッチ用 IAM ロールを用意
  • AWS アカウントにもスイッチ先 IAM ロールを用意
  • 踏み台 AWS アカウント上の IAM ユーザから、作業対象 AWS アカウント上の IAM ロールへスイッチすることで、対象 AWS アカウントへアクセス

今回このあたりを全て IAM Identity Center を用いて以下のような運用に改めました。

  • IAM ユーザを考慮しなくてよくした
  • IAM ロールも考慮しなくてよくした
  • 全社的な ID 基盤(弊社では Entra ID)を IdP としての AWS への SSO ログイン及び各 AWS アカウントへのアクセスに寄せた

これら作業にかかる考慮事項や実際の作業内容、設定完了後の利用感等、コード例を交えつつ解説します。

これまでの状況

構成

弊社における AWS アカウントの状況は以下の通りです。おおむね AWS のホワイトペーパー*1 に則った構成になっているはずです。

  • AWS Organizations で全ての AWS アカウントを一元管理
  • AWS アカウントの内訳は以下のとおり
    1. AWS Organizations 管理用 AWS アカウント
    2. AWS アカウントへスイッチする為の IAM ユーザ / ロール等を管理する為の AWS アカウント
    3. プロダクトのワークロードが存在する AWS アカウント
      • 開発環境
      • QA 環境
      • ステージング環境
      • 本番環境
      • etc.
    4. 各種内部用途の為の AWS アカウント

全ての AWS アカウントへは 上述 2. に存在する IAM ユーザを使用し、AWS アカウント間で IAM ロールをスイッチする構成をとっています。これを図にしたものが以下です。

スイッチロール構成。前述 2.admin に、それ以外の AWS アカウントを member-X(X は適当な数)に、それぞれ置き換えている点に注意

課題

いっぽうで実際の運用上、弊社では以下のような課題感がありました。

  1. IAM ユーザ管理コストが一定発生する
    • 現存 IAM ユーザが在籍者に紐付くものか把握しておく必要がある
    • 入退職や異動の際の IAM ユーザ管理作業が都度発生する
  2. IAM ロール(スイッチ元 / 先両方)の管理コストも一定生じる
    • 現在は principal tag を使った制御*2にてスイッチ先 AWS アカウントおよび IAM ロールの制限を実施している
    • 状況に応じ特殊な権限設定 / スイッチ先アカウント設定が必要になるケースがあり、タグ設計を検討する場合がある
  3. スイッチロール時の煩雑な操作を緩和するために外部ツールに頼る必要がある
    • 実際に使っているもの
    • 外部ツールを使うこと自体が問題なのではなく、ツールに依存性が生じる事(= コントールが難しい領域が発生する事)に課題感がある

3. については AWS が公式に提供 / メンテナンスをしているツール群のみの利用とできるのが理想です。

また 1. については、現在弊社では全社的な ID 基盤として Entra ID を使用しており、ここで管理されるユーザ情報が AWS で利用出来れば IAM ユーザを管理する必要がなくなるため、かなり嬉しくなれます。

ただし IAM ユーザのみを考慮した場合 2. がクリアできず、このあたりも吸収できるようなうまい策を考えなければなりません。

IAM Identity Center を導入する

前項で述べた課題感にほぼ対処できる策として IAM Identity Center があります。詳細はリンク先に譲るとして、これは早い話が AWS 上で利用できる ID 基盤です。AWS Organizations とも特段複雑なことをせずとも連携できます。これを従来の IAM ユーザ / ロールの代わりに用いることで以下が達成できます。

  • 外部 IdP をユーザ / グループの情報ソースとして利用が可能。当該ユーザを使用しての SAML 認証による SSO も実現可
  • IAM ユーザの管理が不要
    • IdP から連携されたユーザを用いることでユーザ管理の責務は IdP に移る
  • IAM ロールの考慮も不要
    • IAM Identity Center 内には権限セット *3 という概念があり、どのような権限を認可するかを IAM ポリシドキュメントの形で設定可能
    • 権限セットの効力範囲は AWS アカウント別に設定可能

実際に採った構成を以下に示します。

SSO 構成

作業

作業は以下の段取りで進める格好としました。

  1. Entra ID と IAM Identity Center との連携設定投入
  2. 権限セットの整備
  3. Entra ID から IAM Identity Center に連携されてきたユーザに対しての権限セット / AWS アカウント紐付け
    • ユーザとグループが連携されているがグループは今回考慮していない。後述

1. のみ手作業での対応とし、他は Terraform にて IaC した状態で作業しています。

1. Entra ID と IAM Identity Center との連携設定投入

基本的には Configure SAML and SCIM with Microsoft Entra ID and IAM Identity Center に従うことで作業は完了となります。弊社では今回以下を前提としました。

  • Entra ID および AWS アカウントについては既存構成をそのまま使う
  • Entra ID 側でのユーザ / グループへの変更が自動で IAM Identity Center へ連携されてくる構成*4とする

実際には前掲ドキュメント中の以下を適宜読み替えての実施としました。

本来このあたりも Terraform にて管理できるのが理想ですが、2025年5月現在 Terraform では IAM Identity Center 組織インスタンス*5 の構成管理が出来ないため、本作業は手作業での実施としています。

2. 権限セットの整備

IAM Identity Center を利用してアクセスした各 AWS アカウント内でどういった操作を認可するかを定義するものが権限セット*6です。利用者が IAM Identity Center 経由で各 AWS アカウントへアクセスする際に使う IAM ロール / ポリシの定義、という感覚で大方問題ないはずです(当方もその理解でいます)。

実際の定義方法も

  • IAM Identity Center 側で事前に定義された権限セットから選択
    • IAM ポリシにおける AWS マネージドなものに大方対応する
  • 既存 / 新設した IAM ポリシを割り当てる形式で設定

といった形で、概念としては IAM ロールの定義に似ています。弊社の事例では事前定義の権限セットで充分であった為、こちらを使っての設定としています。

ただしここには微妙に落とし穴があり、現在 IAM Identity Center が設定を推奨している identity-enhanced console session*7 を有効にする場合、事前定義の権限セットを素直に使うのみでは AWS アカウントへのアクセス時に HTTP 400 が発生して難儀することになります。

対処としては Granting permissions to use identity-aware console sessions で挙げられている IAM ポリシステートメントを権限セット側に設定すれば解決しますが、当該ドキュメントのコード例は resource に AWS アカウント ID を指定する厳格なもので、不特定多数の AWS アカウントへのアクセスが想定される IAM Identity Center とは食い合わせが悪いです。弊社では

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:SetContext",
      "Resource": "*"
    }
  ]
}

のように、少々ゆとりを持たせたステートメントを使うようにしました。

3. Entra ID から IAM Identity Center に連携されてきたユーザに対しての権限セット / AWS アカウント紐付け

Entra ID と IAM Identity Center との連携が無事に済むと、連携対象とした Entra ID 上のユーザとグループが IAM Identity Center 上で見えるようになります。これらに権限セットと操作対象 AWS アカウントとを設定してやれば晴れて Entra ID 側情報を利用しての AWS への SSO アクセスが可能となります。

ユーザとグループのどちらに権限セット / AWS アカウント割り当て設定を行うかは運用形態によって検討の余地があると思います。弊社では ユーザ単位での設定 としました。というのも以下事情が有ったためです。

  • Entra ID は社内の別部門が管理しており、IAM Identity Center と Entra ID とが密結合になるような状況は避けたかった
  • Entra ID グループ単位での権限設計ではカバーが難しい例外的な設定が求められるケースが一部にあり Entra ID ユーザ単位での設定に利があった

Entra ID ユーザ個別に権限セットや AWS アカウントの設定をおこなう煩雑さはコードレベルで可能な限り吸収するようにしました。

コード例

おまたせしました。IAM Identity Center で権限セットと AWS アカウントとを IAM Identity Center ユーザに紐付けする Terraform コード例を示します。実際に運用しているコードを元にしていますが適宜フィクションを交えての内容となる点はご容赦ください。

main.tf

/*
 AWS Organizations で管理される AWS アカウント一覧を得るためのもの
 */
data "aws_organizations_organization" "main" {}

/*
 IAM Identity Center インスタンスに対し必要な設定が行われる
 これは Terraform リソースでの管理が難しいので、設定は AWS マネジメントコンソール上から行い、Terraform からは参照に留める
 */
data "aws_ssoadmin_instances" "main" {}

/*
 Identity-aware console sessions を有効にしている場合、以下を権限セットに設定する必要がある
 これが無いと IAM Identity Center 経由で AWS アカウントへログインができない
 */
data "aws_iam_policy_document" "identity_aware_sessions" {
  statement {
    actions = [
      "sts:SetContext"
    ]
    resources = [
      "*" # 不特定多数の AWS アカウントにログインするのでリソースを絞るのが難しい
    ]
  }
}

# 権限セットに割り当てる IAM ポリシ定義を取得
data "aws_iam_policy" "main" {
  for_each = local.permission_sets
  name     = each.value.managed_policy_name
}

# 権限セットを定義する。権限セットへの IAM ポリシの割り当てもここでやる
resource "aws_ssoadmin_permission_set" "main" {
  for_each = local.permission_sets

  name             = each.key
  description      = "Allow permissions defined as AdministratorAccess"
  instance_arn     = tolist(data.aws_ssoadmin_instances.main.arns)[0]
  
  # セッションを8時間維持する。ISO 8601 の記法で記載
  session_duration = "PT8H"
}

resource "aws_ssoadmin_managed_policy_attachment" "main" {
  for_each = local.permission_sets

  instance_arn       = tolist(data.aws_ssoadmin_instances.main.arns)[0]
  managed_policy_arn = data.aws_iam_policy.main[each.key].arn
  permission_set_arn = aws_ssoadmin_permission_set.main[each.key].arn
}

resource "aws_ssoadmin_permission_set_inline_policy" "main" {
  for_each = local.permission_sets

  inline_policy      = data.aws_iam_policy_document.identity_aware_sessions.json
  instance_arn       = tolist(data.aws_ssoadmin_instances.main.arns)[0]
  permission_set_arn = aws_ssoadmin_permission_set.main[each.key].arn
}

/*
 SCIM 経由で連携された Entra ID ユーザへ権限セットと AWS アカウントとを紐付ける
 紐付け関係は locals.tf を参照
 */
data "aws_identitystore_user" "main" {
  for_each = local.users

  identity_store_id = tolist(data.aws_ssoadmin_instances.main.identity_store_ids)[0]
  alternate_identifier {
    unique_attribute {
      attribute_path  = "UserName"
      attribute_value = "${each.key}@${local.domain_name[each.value.type]}"
    }
  }
}

resource "aws_ssoadmin_account_assignment" "main" {
  # locals.tf 側 user_account_mapping も参照のこと
  for_each = tomap({
    for element in local.user_account_mapping :
    "${element.user}@${element.aws_account_id}" => element
  })

  instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]

  /*
   locals.tf 内で定義される local.users で admin = true が設定されている場合は Administrator 一択
   そうでない場合は同 local.permission で定義されている権限セットを指定
   */
  permission_set_arn = (
    try(each.value.admin, false)
    ? aws_ssoadmin_permission_set.main["Administrator"].arn
    : aws_ssoadmin_permission_set.main[local.permissions[each.value.type][each.value.role]].arn
  )

  principal_id   = data.aws_identitystore_user.main[each.value.user].id
  principal_type = "USER"

  target_id   = each.value.aws_account_id
  target_type = "AWS_ACCOUNT"
}

locals.tf

locals {
  permission_sets = {
    Administrator = {
      description         = "Allow permissions defined as AdministratorAccess"
      managed_policy_name = "AdministratorAccess"
    }
    Developer = {
      description         = "Allow permissions defined as PowerUserAccess"
      managed_policy_name = "PowerUserAccess"
    }
    ReadOnly = {
      description         = "Allow permissions defined as ReadOnlyAccess"
      managed_policy_name = "ReadOnlyAccess"
    }
  }
}

locals {
  # 職責の一覧
  roles = {
    sre   = "sre"   # SRE
    swe   = "swe"   # 開発(フロントエンド / バックエンド)
    algo  = "algo"  # Algo
    qa    = "qa"    # QA
    cre   = "cre"   # CRE
    pdm   = "pdm"   # PdM
    sales = "sales" # セールス
    cs    = "cs"    # CS
  }

  # 雇用区分の一覧
  types = {
    employee = "employee" # 正社員
    partner  = "partner"  # 業務委託
  }

  /*
   Entra ID 上で設定されるユーザ名で使用されるドメイン部
   もちろん実際に MNTSQ 内で使われるものとは一致しない
   */
  domain_name = {
    employee = "example.com"
    partner  = "partner.example.com"
  }
}

locals {
  environment = {
    production = [
      /*
       プロダクトのワークロードが載っている AWS アカウントで本番環境として扱うべきものを列記
       AWS アカウント名が期待される
       */
    ]
    non_production = [
      /*
       プロダクトのワークロードが載っている AWS アカウントで本番環境として扱う必要は無いものを列記
       AWS アカウント名が期待される
       */
    ]
  }
  aws_accounts = {
    # AWS Organizations 管理下にある全ての有効な AWS アカウント
    for account in data.aws_organizations_organization.main.accounts :
    account.name => account.id
    if account.status == "ACTIVE"
  }
}

/*
 雇用区分と職責とによってどの権限セットをアサインするかを決定する
 内訳
 - permissions:職責と権限セットとの対応
 - mappings:職責と AWS アカウントとの対応
 - users:ユーザ(Entra ID 管理) / 雇用区分 / 職責の対応
 */
locals {
  permissions = {
    /*
     雇用区分と職責とで異なる権限セットを設定できるようにする:
     - 雇用区分:local.types を参照
     - 職責:local.roles を参照
     */
    employee = {
      sre   = "Administrator"
      swe   = "Developer"
      algo  = "Developer"
      qa    = "Developer"
      cre   = "Developer"
      pdm   = "Developer"
      sales = "ReadOnly"
      cs    = "ReadOnly"
    }
    partner = {
      sre  = "Developer"
      swe  = "Developer"
      algo = "Developer"
      qa   = "Developer"
    }
  }



  mappings = {
    /*
     職責によって IAM Identity Center 経由でアクセス可能な AWS アカウントを制御する
     職責の定義は local.roles を参照
     */
    employee = {
      sre = values(local.aws_accounts)
      swe = concat(
        local.environment.production,
        local.environment.non_production
      )
      algo = concat(
        local.environment.production,
        local.environment.non_production
      )
      qa = concat(
        local.environment.production,
        local.environment.non_production
      )
      cre = concat(
        local.environment.production,
        local.environment.non_production,
      )
      pdm = concat(
        local.environment.production,
        local.environment.non_production,
      )
      sales = local.environment.production,
      cs    = local.environment.production,
    }
    partner = {
      sre  = local.environment.non_production,
      swe  = local.environment.non_production
      algo = local.environment.non_production
      qa   = local.environment.non_production
      cre  = local.environment.non_production,
    }
  }

  users = {
    /*
     **Entra ID で** 管理されるユーザであって AWS を利用する者を宣言する
     ユーザ名はドメイン名を省く形で指定する
     ユーザ名をキーとし、値には以下をもつ map を宣言する:
     - 雇用区分 (type):local.types を参照
     - 職責 (role):local.roles を参照
     - 操作許可 AWS アカウント内で Administrator 権限が必要な場合は admin を true に設定
     */

    # # <名>.<姓> でユーザ名が定義されるとする
    "taro.sre"    = { type = local.types.employee, role = local.roles.sre },
    "hanako.swe"  = { type = local.types.employee, role = local.roles.swe, admin = true },
    "jiro.swe"    = { type = local.types.partner, role = local.roles.swe },
    "saburo.qa"   = { type = local.types.employee, role = local.roles.qa, },
    "shiro.algo"  = { type = local.types.employee, role = local.roles.algo },
    "goro.sales"  = { type = local.types.employee, role = local.roles.sales },
  }
}

/*
 IAM Identity Center 上でのユーザ / 権限セット / AWS アカウントとの紐付けには aws_ssoadmin_account_assignment というリソースを使う
 https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_account_assignment
 このリソースはユーザと AWS アカウントを一度にそれぞれ1つしか設定できない
 窮余の策として以下値をもつ list を定義し、当該リソースの for_each では "USERNAME@AWS_ACCOUNT_ID" をキーとする map として処理する:
 - ユーザ名 (user)(例:"hoge.fuga")
 - 職責 (role)(例:"sre")
 - 雇用区分 (type):"employee"(正社員)または "partner"(業務委託)
 - AWS アカウント ID (aws_account_id)
 参考:https://developer.hashicorp.com/terraform/language/functions/flatten
 */
locals {
  user_account_mapping = flatten([
    for user_key, user in local.users : [
      for aws_account_id in local.mappings[user.type][user.role] : {
        user           = user_key
        role           = user.role
        type           = user.type
        admin          = try(user.admin, false)
        aws_account_id = aws_account_id
      }
    ]
  ])
}

使用感

Web ブラウザ

IAM Identity Center のセットアップが無事に完了すると IAM Identity Center 設定時に指定した AWS アクセスポータル*8 の URL から SSO ログインが行えます。

権限セットや AWS アカウントが割り当ていない状態でもアクセスは可能ですが、AWS アカウントへはアクセスできません。一方で両者が設定されている場合は SSO ログイン後に AWS アカウントの選択肢が表示され、設定された権限セットでもってのアクセスが可能となります。

実際に AWS アクセスポータルへアクセスした際の状況は以下のようになります。伏字が多い点はご寛容いただければと思います。

権限セット / AWS アカウント割当前 権限セット / AWS アカウント割当後

AWS アクセスポータルから各 AWS アカウントへアクセスした後はいつものマネジメントコンソール上での操作になります。アクセス先を切り替える為には AWS アクセスポータルへの都度アクセスが必要ですが、都度アクセスの面倒を避けたい向きにサードパーティの拡張がいくつか提供されています。本稿筆者は AWS SSO Extender を使用しています*9

CLI

新しめの AWS CLI を使っている限りにおいて Configuring IAM Identity Center authentication with the AWS CLI に従い設定を済ませれば、以後は

aws sso login --profile <プロファイル名>

を都度実行することで認証が済み、aws コマンドでの操作が可能です。これはクレデンシャル管理が必要であった IAM ユーザ利用に比べ大きなメリットと言えるでしょう。AWS アカウントの切り替えも --profile オプションの指定ないしは環境変数 AWS_PROFILE の宣言で済みます。

AWS CLI の設定内容については aws configure sso を使い都度設定してゆく方法もありますが、弊社では各職責毎にテンプレート化した設定ファイルを用意し、各作業者が利用している PC に設定を保存してもらう方針をとりました。

ほか Terraform に関しても AWS CLI 向けの設定が済んでいれば aws sso login が完了している前提にて特段支障なく操作が可能です。ただし Terraform 1.6.0 以前では AWS CLI 用の設定に改変を加える必要があり*10、弊社でも当初この事象を踏みました。最終的には Terraform バージョンを最新版(作業時点で 1.11.4)にしてしまうことで対処としました。

おわりに

複数の AWS アカウントを管理する構成において IAM ロールをスイッチする運用を改め、IAM Identity Center による一元的かつ柔軟な AWS アカウント横断のアクセスを可能とするための作業とその後の所感について扱いました。

現在弊社では IAM Identity Center による AWS への SSO ログインの利用浸透を全社で行っており、これが完了した暁には個々人向けに払い出していた IAM ユーザおよびスイッチロール用の IAM ロールの削除をし、IAM Identity Center 経由での AWS アクセスに完全に移行するよう計画しています。

ユーザから見た場合に各 AWS アカウントへのアクセスが透過的になるのも IAM Identity Center 導入での嬉しさのひとつですが、IAM Identity Center 自体が IdP としての利用も可能であるという点から、SRE 主体での他のサービスへの SSO 化の試みについても着手し易くなってきました。今後は社内で独自のユーザ管理体系が存在する諸々のサービスを着実に SSO 化し、利用者の利便性向上やセキュアな体制構築に繋げてゆこうと計画しています。

IAM Identity Center と Entra ID との組み合わせ自体は然程珍しいものではなく、Web 上にも同様の事例が多数みられます。本稿がそれらのひとつとして数えられ、ひとつの事例としてお役に立てれば幸いです。

MNTSQ 株式会社 SRE 秋本

*1:Organizing Your AWS Environment Using Multiple Accounts

*2:Controlling access to and for IAM users and roles using tags に詳細があります。弊社では IAM ユーザに所定のタグを設定し、スイッチ先 IAM ロールでは trust policy 内でタグを評価し、タグのキーと値とが上限に合致する場合にスイッチを認可する、といった設定で運用している

*3:Manage AWS accounts with permission sets

*4:自動プロビジョニング。Provisioning an external identity provider into IAM Identity Center using SCIM が詳しい

*5:ここ が詳しい。IAM Identity Center にはその設定や効力範囲を制御するためのインスタンスという概念があり、AWS Organizations 管理者アカウントにひもづくインスタンスを組織インスタンス、同メンバーアカウントにひもづくインスタンスをアカウントインスタンスという。組織インスタンスで IdP 連携や SSO 時の設定等を管理する

*6:Manage AWS accounts with permission sets

*7:本文中で後述している "identity-aware" の語が本記事にかかる設定作業を実際に行っていた2025年4月時点では使われていたが、2025年5月時点では "identity-enhanced" に変わっていた

*8:Using the AWS access portal

*9:外部ツールに頼らなくしたいという動機はどこにいったのだというツッコミはその通りです……

*10:https://sadayoshi-tada.hatenablog.com/entry/2023/10/06/001405 が詳しいです。事例公開多謝です