LAC WATCH

セキュリティとITの最新情報

RSS

株式会社ラック

メールマガジン

サイバーセキュリティや
ラックに関する情報をお届けします。

Facebook X Instagram
サービス・製品 | 

AWS Organizationsで、マルチアカウント運用を安全に効率化してみた

「このリソース、誰が何のために作ったんだっけ?」

技術検証を重ねるほど、1つのAWSアカウントの中は次第に混沌としていきます。気付けば不要なリソースが残り、ゴミがどんどん増えていきます。さらに、アカウントを個別発行で運用すると、コストの把握が難しくなり、設定の統一や監視も追いつかない......。私たちもまさにこの壁に直面しました。

検証は迅速に、環境は手軽に払い出し、そして不要になれば迷いなく削除できるようにしたい。そして、アカウント作成を手動に頼らず、セキュリティやコスト管理は一元化したい。これらをまとめて解決してくれるのがAWS Organizationsです。

本記事では、AWS Organizationsを軸に私たちが設計・運用しているマルチアカウント環境の考え方と具体的な工夫を、実運用の視点から解説します。なお、執筆はAI技術部の小林と、AI技術部で業務に携わっている株式会社ラックテクノロジーズ 向井が共同で担当しています。

構築した環境について

今回構築した環境の全体像が以下の図です。

構築した環境の全体像

AWS Organizationsを軸とし、関連するサービスを設定して、安全かつ効率的に運用ができる構成としています。ここからは使用した各サービスと具体的な設定について、ご紹介します。

AWS Organizationsとは

AWS Organizationsは、複数のAWSアカウントを組織としてまとめ、OU(Organizational Unit)で階層管理し、SCP(Service Control Policy)を含むポリシーを組織単位で適用できるサービスです。請求の一元化に加え、環境分離・権限分離に基づくガバナンス強化と、コストの可視化を同時に実現します。

組織には管理アカウントが1つだけ存在し、他はすべてメンバーアカウントになります。複数のメンバーアカウントをOUでグループ化でき、OU単位でポリシーを適用できるため、統制の効いたアカウント管理が可能です。

ただし、Organizationsだけではアカウント設計やポリシー適用は自ら設計・実装する必要があり、運用負荷が課題になりがちです。こうした初期設計の複雑さを解消し、セキュアなマルチアカウント運用を迅速に立ち上げられるのがControl Towerです。統制と効率を両立した運用を大幅な工数削減とともに実現できます。

Control Towerによるアカウント初期設定の自動化

AWS Control Towerは、AWSのマルチアカウント環境を自動構築するマネージドサービスです。AWSのベストプラクティスに基づいたLanding Zone(管理基盤)を迅速に構築し、コントロールによりセキュリティ・コンプライアンスのチェックと強制を自動化します。

Control Towerを使うメリット

Control Towerを使う最大のメリットは、マルチアカウント運用が圧倒的に楽になることです。企業では部門・用途ごとにAWSアカウントを分けるのがベストプラクティスですが、手動での統制は、設定のバラつきや作業負荷が問題になります。Control Towerはこれらの統制を自動化・標準化することで設定漏れや属人化を防ぎます。アカウント内での危険な操作はコントロールによって危険なイベントがブロックもしくは検出され、ダッシュボードに可視化されます。

セットアップの過程で作成されるアカウント

Control Towerを有効化すると、セットアップの過程でSecurity OUが作成され、配下にAuditアカウントとLog Archiveアカウントが作成されます。Auditアカウントは組織内のアカウントに対して監査を行う権限をもったアカウントで、主に以下のことを行います。

  • AWS Configの集約先(設定違反の検知が集まる)
  • Security Hub、GuardDutyなどを利用した組織全体のセキュリティ可視化

Log Archiveアカウントは組織内のアカウントからログデータを集約するためのアカウントです。各アカウントのCloudTrailログ、Configログ、S3アクセスログをまとめて保管し、ログの改ざんを防止します。

コントロール(ガードレール)の適用

コントロール(ガードレール)は、運用ルールを標準化して継続的に守らせる仕組みです。コントロールには予防コントロール、検出コントロール、プロアクティブコントロールの3種類があります。

予防コントロール ルール違反の操作をブロック
検出コントロール ルール違反を検知し、CloudTrailなどに記録
プロアクティブコントロール プロビジョニング時に準拠確認、非準拠なら作成を拒否

必須レベルのコントロールは自動有効化されますが、Strongly Recommended(強く推奨)レベルのコントロールも内容を確認のうえ積極的に適用することをおすすめします。

ID一元管理

今回作成したAWS Organizations環境においては、AWS IAM Identity Center(旧SSO)と既存のユーザー管理システムを組み合わせ、SAML 2.0によるシングルサインオンと、SCIM 2.0による自動プロビジョニングによりID基盤を一元化し、セキュアで快適な環境を用意しました。

また、他部署のユーザーが所属できる当AWS Organizations環境へのアクセス専用グループも用意し、他部署ユーザーも一元的に管理できるようにしました。

Security Hub

アカウント数が増えるに従い、また各アカウントで利用するサービスが多様化すると、「今どのアカウント(およびリージョン)で何が起きているのか」「対処すべきリスクが顕在化していないか」といったことを把握するのが難しくなります。そこで、今回作成したAWS Organizations環境においては、組織全体の標準チェックを実施し、脅威検知、設定不備などのセキュリティ状況を一元管理、可視化するため、AWS Security Hubを有効化しています。

通知

ガードレール違反や監査イベントなどを通知して、運用者が早期に異常に気付ける体制を整えます。EventBridgeやSNSと連携させることで、緊急性の高いイベントをメールなどで通知でき、初動遅れや見落としのリスクを大幅に低減できます。

ラックでは、Security HubやGuardDutyで検知した高リスクイベントをEventBridgeとAmazon SNSでメール配信する運用を採用しています。そのため、常にコンソールを見ていなくても重要インシデントを即座に把握可能です。

以下はメール通知に使っているEventBridgeルールのイベントパターンです。CRITICALとHIGHのイベントをこのルールでキャッチして、ターゲットのSNSトピックに連携しています。

{
  "source": ["aws.securityhub"],
  "detail-type": ["Security Hub Findings - Imported"],
  "detail": {
    "findings": {
      "Severity": {
        "Label": ["HIGH", "CRITICAL"]
      },
      "Region": ["ap-northeast-1"],
      "Workflow": {
        "Status": ["NEW"]
      }
    }
  }
}

SNSトピック側ではサブスクリプションにメーリングリストのアドレスを登録して通知が関係者に届くように設定しています。

CfCTによるControl Towerのカスタマイズ

CfCT(Customizations for AWS Control Tower)は、Control Tower標準の外側に「追加の標準設定」をコードで載せるための仕組みです。

AWS Control Towerはあくまでマルチアカウント環境のベースラインを提供するものなので、独自の要件はAWS Control Towerコンソール上の設定では反映できません。そこで、AWS Control Towerが構築した下地の上に独自カスタマイズを自動で反映する仕組みを用意して、より最適化されたマルチアカウント環境を構築できるようにすることを目的とした、AWS公式のソリューションがCfCTです。OU/アカウントに対してCloudFormation(多くはStackSets)等で追加リソースや設定を自動展開し、Control Towerのアップデート追従性を保ちながら拡張できます(追加の監査設定、セキュリティサービス有効化、共通IAMロール配布など)。

スタックの作成と保存は、AWS Solutions LibraryのGitHubリポジトリに用意されているCloudFormationテンプレートを利用します。CodePipelineのソースの置き場所は、スタックの詳細 - パラメータとして選択できます。

※ 2026年2月現在、以下から選択可能です。
・Amazon S3
・AWS CodeCommit
・GitHub

今回は、CodePipelineのソースの置き場所をAmazon S3としてスタックを作成しました。スタックの作成が完了すると、「custom-control-tower-configuration-<account-ID>-<region>」というS3バケットが自動で生成され、「_custom-control-tower-configuration.zip」という名前の初期ファイルが配置されています。

注意点として、このzipファイルは初期状態だとAWS KMSのキーによって保護されておりアクセスできません。そのため、マネジメントコンソール上でAWS KMSに移動して、必要なロールを付与する必要があります。

CfCTでは、マニフェストファイル、リソーステンプレート、その他のファイルに独自のカスタマイズ設定を記述していきます。そして「custom-control-tower-configuration.zip」という名称のzipファイルとしてCodePipelineのソース置き場にアップロードすることで、OU/アカウントに対して設定やリソースが自動展開されます。

今回は、このようなマニフェストファイルによりAWS Control Towerの独自設定を追加しています。

#Default region for deploying Custom Control Tower: Code Pipeline, Step functions, Lambda, SSM parameters, and StackSets
region: ap-northeast-1
version: 2021-03-15
# Control Tower Custom CloudFormation Resources
resources:
  - name: iam-password-policy
    resource_file: iam-password-policy/templates/iam-password-policy.template
    parameter_file: iam-password-policy/parameters/iam-password-policy.json
    deploy_method: stack_set
    deployment_targets:
      organizational_units:
        - Sandbox
    regions:
      - ap-northeast-1
  - name: enforce-mfa-policy
    resource_file: enforce-mfa-policy/templates/enforce-mfa-policy.template
    parameter_file: enforce-mfa-policy/parameters/enforce-mfa-policy.json
    deploy_method: stack_set
    deployment_targets:
      organizational_units:
        - Sandbox
    regions:
      - ap-northeast-1
  - name: source-ip-restriction-policy
    resource_file: source-ip-restriction-policy/templates/source-ip-restriction-policy.template
    parameter_file: source-ip-restriction-policy/parameters/source-ip-restriction-policy.json
    deploy_method: stack_set
    deployment_targets:
      organizational_units:
        - Sandbox
    regions:
      - ap-northeast-1

それぞれ設定した内容は、以下のとおりです。

  • IAMパスワードポリシーの自動適用
  • MFA設定を強制するポリシーの自動配布
  • 接続元IPアドレスを制限するポリシーの自動配布

Account Factoryを使ったアカウント作成の効率化

Account Factoryは、Control Tower配下でテンプレ化されたアカウント作成を行う仕組みです。新しいアカウントを作るたびに、OU配置・標準設定・ネットワークやアクセス設定(必要に応じて)などを揃えて作成できるため、払い出し作業の工数削減と品質の均一化に効きます。運用面では、申請から払い出し、標準適用をワークフロー化しやすいのも利点です。

新規AWSアカウントの発行の流れ

ラックでは、Account Factoryと各種サービスを連携することでAWSアカウントの発行を効率化しています。

新規AWSアカウントの発行の流れ

AWSアカウントを新規で利用したい人は、Microsoft Formsのフォームにアカウント名や使用するユーザーのメールアドレスを指定して申請します。申請後、自動的にPower Automateのフローが実行され、Teamsの管理者チームに承認依頼が届きます。利用申請が承認されると、Excelの管理ファイルにアカウント情報が書き込まれ、アカウント情報を含む登録用のJSONファイルがSharePoint上に発行されます。このファイルを担当者がS3の所定の場所にアップロードすると、EventBridgeのルールからAWSアカウント発行用Lambda関数が呼び出されて、この関数がAccount FactoryのAPIを呼び出してAWSアカウントを作成する構成としました。

実際に上記構成で運用したところ、Lambda関数がアカウント作成を待機するのですが、これがLambda最大実行時間の15分を超える可能性があることがわかりました。そのため、EventBridgeから呼び出す対象をStep Functionsのステートマシンとして、こちらで実行を待機する構成に変更しました。ステートマシンの大まかな処理の流れは以下の通りです。

  • EventBridgeから呼び出しで起動
  • アカウント情報を含むJSONファイルを読み込み
  • Account Factoryに対してAWSアカウント発行を実行
  • AWSアカウントの発行完了を待機し、作成後にユーザーをAWSアカウントに紐づけ

今後の課題

上記によって、AWSアカウントの作成を半自動化しています。本来であれば完全自動化を目指したいところですが、当初の案だとマイクロソフトのプラットフォーム上からAWSへの連携時にセキュリティ上の懸念があることが分かり、現状はこの形をとっています。将来的には完全自動化を目指したいと考えています。

SCP

SCP(Service Control Policy)は、AWS Organizationsが提供するポリシーで、アカウント内のIAM権限に対して上限として働く仕組みです。IAMユーザー・IAMロール・ルートユーザーを含むアカウント内すべてのプリンシパルに適用され、許可される操作の最大範囲を制御します。

SCPはルートまたはOU(組織単位)に適用することで、配下のアカウントへ継承されます。そのため、Control Tower環境ではOUに紐づく統制手段の1つとして活用されており、禁止したい操作・利用サービスを組織全体で一貫して制御できる点が特徴です。例えば以下のような用途に役立ちます。

  • ルートユーザーによる特定操作の禁止
  • IAMの高リスク操作(例:Access Key発行)の抑制
  • 特定サービスの利用禁止

一方で、SCPは適用範囲が広く組織全体に強い制御を及ぼすため、思わぬ副作用を招く場合があります。

ラックでは、指定したIPアドレス以外からAWSサービスへのアクセスをブロックするためにSCPを検討したところ、AWSのサービスロールや一部の内部コンポーネントまでブロックされてしまい、正常動作に影響を与えるという課題がありました。このようにOUに適用したSCPは配下のすべてのアカウントに影響します。そのため本番適用前には、検証用OU(アカウント)を用意し、想定通りに動作するか十分なテストを行うことが不可欠です。

また、適用する統制レベルに応じてOUを細かく分けるなど、SCPを活用するためには組織構造側にも一工夫が必要と感じました。

運用してわかった課題

上記の環境を数か月運用し、セキュアなアカウントを迅速に発行できる、アカウントのセキュリティを一元管理しているので安心して運用できるなど多数のメリットを感じましたが、いくつか課題も出てきました。

メンバーアカウントを作成できる上限数が低い

デフォルトでは、AWS Organizationsで作成できるメンバーアカウントの上限が10に設定されているためすぐに上限に達してしまい、アカウント作成ができないという問題にぶつかりました。特に今回はOrganizationsの検証用にテストアカウントを数アカウント作成していたこともあり、すぐに上限に達してしまいました。

このメンバーアカウントの上限はService Quotas > AWS Organizations > "Default maximum number of accounts"で確認できます。引き上げのリクエストは同画面右上の「アカウントレベルでの引き上げをリクエスト」ボタンから行えます。

コストの確認を簡単に行いたい

メンバーアカウントが増えてくると、コストの管理を定期的にかつ簡単に行いたいという要望が出てきました。マネジメントコンソールのCost Explorerからも確認できますが、一度AWS環境にログインする必要があり、アカウント別に確認したいという要望もありました。そのためアカウント別にコスト集計し、Teamsのチャネルに投稿するLambda関数を作成し、EventBridgeを使って毎朝実行されるように設定しました。

設定は以下の手順で行えます。

  • 通知をしたいTeamsのチャネルを右クリックし、「チャネルの管理」をクリック
  • コネクタの編集ボタンからIncoming Webhookを設定
  • 設定が完了すると連携のためのURLが発行されるため、これを控えておく
  • AWSのLambdaから以下のLambda関数を作成
import os
import json
import urllib.request
from datetime import date
import boto3
def _post_to_teams(webhook_url: str, text: str):
    payload = {"text": text}
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        webhook_url,
        data=data,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return resp.read().decode("utf-8")
def _list_account_names():
    org = boto3.client("organizations")
    paginator = org.get_paginator("list_accounts")
    m = {}
    for page in paginator.paginate():
        for a in page["Accounts"]:
            m[a["Id"]] = a["Name"]
    return m
def lambda_handler(event, context):
    webhook_url = os.environ["TEAMS_WEBHOOK_URL"]
    today = date.today()
    start = today.replace(day=1).isoformat()
    end = today.isoformat()
    account_names = _list_account_names()
    ce = boto3.client("ce", region_name=os.environ.get("AWS_REGION", "us-east-1"))
    res = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="MONTHLY",
        Metrics=["UnblendedCost"],
        GroupBy=[{"Type": "DIMENSION", "Key": "LINKED_ACCOUNT"}],
    )
    groups = res["ResultsByTime"][0]["Groups"]
    items = []
    for g in groups:
        account_id = g["Keys"][0]
        amount = float(g["Metrics"]["UnblendedCost"]["Amount"])
        unit = g["Metrics"]["UnblendedCost"]["Unit"]
        name = account_names.get(account_id, "Unknown")
        items.append((amount, unit, account_id, name))
    items.sort(reverse=True, key=lambda x: x[0])
    total_amount = sum(x[0] for x in items)
    unit = items[0][1] if items else "USD"
    lines = []
    lines.append(f"今月の累計コスト(MTD): {start} 〜 {end}")
    lines.append(f"合計: {total_amount:,.2f} {unit}")
    lines.append("")
    lines.append("アカウント別:")
    for amount, unit, account_id, name in items:
        lines.append(f"- {name} ({account_id}): {amount:,.2f} {unit}")
    _post_to_teams(webhook_url, "\n".join(lines))
    return {"ok": True, "count": len(items), "start": start, "end": end}

Lambda関数を設定したあと、環境変数にTEAMS_WEBHOOK_URLを作成し、値に先ほど控えたURLを入力します。一般設定のタイムアウトもデフォルト値の場合タイムアウトしてしまう可能性が高いため、変更が必要です。ラックの環境では1分に設定しています。

実行するとコストの通知がTeamsのチャネルに届きます。ラックでは、先ほどのLambda関数をカスタマイズして、全体とアカウント別の前日比での増加分が分かるようにしています。

AWSOrganizationsCostReport

最後にEventBridgeからスケジュールを作成し、定期的に実行されるように設定します。

メンバーアカウントの閉鎖には時間がかかる

メンバーアカウントの閉鎖は管理アカウントから行えますが、完全に閉鎖されてアカウント数のカウントから外れるまでには約3か月かかるので、それも踏まえてアカウント数の管理を行うことが必要です。

さいごに

今回は、AWS Organizationsを使ったAWSアカウントの一元管理をご紹介してきました。これまで手作業に頼っていたセキュリティポリシーの適用やコストの管理を組織単位で統制できるため、運用効率は大きく向上し、設定漏れのリスク低減にもつながります。マルチアカウント運用の煩雑さや統制のばらつきに課題を感じている場合は、ぜひ導入・活用を検討してみてください。

この記事は役に立ちましたか?

はい いいえ

page top