「リージョン拒否コントロール」とは、特定のAWSリージョンでのリソースの作成や使用を制限する機能です。Control Towerでは2種類の方法があります。
①ランディングゾーン全体(全組織単位(OU)とアカウント)に適用されるリージョン拒否コントロール
https://docs.aws.amazon.com/controltower/latest/userguide/primary-region-deny-policy.html

②特定の組織単位(OU)に適用されるリージョン拒否コントロール
https://docs.aws.amazon.com/controltower/latest/userguide/ou-region-deny.html

これらのコントロールの違いを把握するため、両方を有効にして検証を行いました。

結果

①ランディングゾーン全体に適用されるコントロール ②OU単位に適用されるコントロール
適用する対象 全OUに自動適用(Root OUを除く) 特定のOUを選択して適用できる
拒否するリージョン Control Towerの対象としたリージョン以外はすべて拒否リージョンとする(固定) 拒否リージョンを個別に選択できる
拒否リージョンに対して許可するアクション 追加できない 追加できる
拒否の対象から除外するIAMプリンシパル 設定できない 設定できる

①ランディングゾーン全体に適用する手順

設定方法

コントロールの設定は、「ランディングゾーン設定」ページから「設定を変更する」を選択し、「リージョン拒否設定」を有効にすることで行います。

SCPの適用対象

リージョン拒否コントロールを有効にすると、SCPが作成されます。作成されたSCPを使用してリージョン拒否を実現しています。

  • 作成されたSCPは、Root OUを除くすべてのOUにアタッチされます。
  • Control Towerに登録されていない未登録のOUにも適用されます。
  • Control Towerの管理対象外リージョンへのアクセスが拒否されます。

挙動の確認

AdministratorAccess管理ポリシーをアタッチしたIAMロールを使用して、SCPが適用されたアカウントにログインし確認を行います。適用されたSCPによりs3:CreateBucket(S3バケットの作成)のアクセス権が制限されているため、許可・拒否されたリージョンでS3バケットを作成できるかどうかを確認します。SCPの内容は下部に記載しています。

  • 許可リージョンではバケットの作成が可能でしたが、拒否リージョンではすべてのアカウント※でバケットを作成できませんでした。
    ※「すべてのアカウント」とは、管理アカウントを除くすべてのアカウントを指します。管理アカウントではSCPの制限が適用されないため、拒否リージョンでもS3バケットを作成できます。

②OU単位に適用する手順

設定方法

コントロールを有効化する際、コンソールから「リージョンの選択」「許可するアクションの追加」「除外するIAMプリンシパル」を設定できます。これらは②OU単位のリージョン拒否コントロールでしか設定できません。

OUの選択

  • Control Towerに登録されているOUのみが選択可能です。
  • 「Security OU」は選択できませんが、監査アカウントとログアーカイブアカウントを別のOUに移動することで、これらのアカウントに対してもコントロールを適用可能です。
  • 「Infrastructure」というOUを選択しました。このOUには「region-deny-control-sim-account」というアカウントを含めています。

リージョンの選択

  • 管理対象リージョン「オレゴン」と管理対象外リージョン「ハイデラバード」を選択しました。ホームリージョンとして設定されている東京はデフォルトで許可されています。

許可するアクションの追加

  • Lambdaへのアクセスを許可するために、lambda:*を追加しました。

除外するIAMプリンシパルの設定

  • リージョン拒否コントロールの制約を受けない特別なIAMを設定できます。
  • 複数のIAMプリンシパルを指定できます。
  • 有効化対象のアカウント(ここでは「region-deny-control-sim-account」アカウント)内に存在するIAMロールのARNを指定します。

SCPの適用対象

選択した「Infrastructure」 OUにのみSCPがアタッチされます。SCPはOU内の全アカウントへ継承されるため、「region-deny-control-sim-account」アカウントに対しても適用されます。

挙動の確認

AdministratorAccess管理ポリシーをアタッチしたIAMロールを使用して、SCPが適用された「region-deny-control-sim-account」アカウントにログインし確認を行います。許可・拒否されたリージョンでS3バケットを作成できるかを確認します。

  • IAMプリンシパルに指定されていないIAMロールでログインした場合
    • 許可リージョンではバケットの作成が可能ですが、拒否リージョンでは作成できませんでした。
  • 指定したIAMプリンシパルを使用してログインした場合
    • 許可・拒否リージョンともにS3バケットを作成できました。
  • オプション設定でLambdaへのアクセスが許可しているため、リージョンに関係なくLambda関数を作成できました。

気になった点の検証

Control Towerから適用されたSCPを直接操作した場合の挙動を検証しました。

検証① SCPのデタッチ

OUにアタッチされたSCPをOrganizationsのコンソールからデタッチしたところ、Control Towerのコンソールにドリフトが発生しました。

手動で再アタッチしてもドリフトは解消されず、OUを再登録する必要があります。

検証② SCPの直接編集

SCPを直接編集した場合も、ドリフトが発生しました。解決するには、SCPがアタッチされている全OUでの再登録が必要のようです。

再編集し手動で元に戻してみたところ、Control Towerが一時的に利用できない状態になりました。

この場合、ランディングゾーンを修復する必要があります。

作成されたSCPの比較

基本的な構造は同じですが、オプション設定で指定したリージョン、IAMプリンシパル、アクションが反映される点で差異があります。

まとめ

以上の検証から、OU単位でリージョン拒否コントロールを設定する方が、SCPをカスタマイズできるため便利だと思いました。

以下は適用されたSCPのサンプルです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "ap-northeast-1" // ここに記載ないリージョンが拒否される
          ]
        },
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/AWSControlTowerExecution" // ここに記載ないIAMプリンシパルが拒否される
          ]
        }
      },
      "Resource": "*",
      "Effect": "Deny",
      "NotAction": [ // ここに記載ないアクションが拒否される
        "a4b:*",
        "access-analyzer:*",
        "account:*",
        "acm:*",
        "activate:*",
        "artifact:*",
        "aws-marketplace-management:*",
        "aws-marketplace:*",
        "aws-portal:*",
        "billing:*",
        "billingconductor:*",
        "budgets:*",
        "ce:*",
        "chatbot:*",
        "chime:*",
        "cloudfront:*",
        "cloudtrail:LookupEvents",
        "compute-optimizer:*",
        "config:*",
        "consoleapp:*",
        "consolidatedbilling:*",
        "cur:*",
        "datapipeline:GetAccountLimits",
        "devicefarm:*",
        "directconnect:*",
        "ec2:DescribeRegions",
        "ec2:DescribeTransitGateways",
        "ec2:DescribeVpnGateways",
        "ecr-public:*",
        "fms:*",
        "freetier:*",
        "globalaccelerator:*",
        "health:*",
        "iam:*",
        "importexport:*",
        "invoicing:*",
        "iq:*",
        "kms:*",
        "license-manager:ListReceivedLicenses",
        "lightsail:Get*",
        "mobileanalytics:*",
        "networkmanager:*",
        "notifications-contacts:*",
        "notifications:*",
        "organizations:*",
        "payments:*",
        "pricing:*",
        "quicksight:DescribeAccountSubscription",
        "resource-explorer-2:*",
        "route53-recovery-cluster:*",
        "route53-recovery-control-config:*",
        "route53-recovery-readiness:*",
        "route53:*",
        "route53domains:*",
        "s3:CreateMultiRegionAccessPoint",
        "s3:DeleteMultiRegionAccessPoint",
        "s3:DescribeMultiRegionAccessPointOperation",
        "s3:GetAccountPublicAccessBlock",
        "s3:GetBucketLocation",
        "s3:GetBucketPolicyStatus",
        "s3:GetBucketPublicAccessBlock",
        "s3:GetMultiRegionAccessPoint",
        "s3:GetMultiRegionAccessPointPolicy",
        "s3:GetMultiRegionAccessPointPolicyStatus",
        "s3:GetStorageLensConfiguration",
        "s3:GetStorageLensDashboard",
        "s3:ListAllMyBuckets",
        "s3:ListMultiRegionAccessPoints",
        "s3:ListStorageLensConfigurations",
        "s3:PutAccountPublicAccessBlock",
        "s3:PutMultiRegionAccessPointPolicy",
        "savingsplans:*",
        "shield:*",
        "sso:*",
        "sts:*",
        "support:*",
        "supportapp:*",
        "supportplans:*",
        "sustainability:*",
        "tag:GetResources",
        "tax:*",
        "trustedadvisor:*",
        "vendor-insights:ListEntitledSecurityProfiles",
        "waf-regional:*",
        "waf:*",
        "wafv2:*"
      ],
      "Sid": "GRREGIONDENY"
    }
  ]
}