はじめに
こんにちは、すずきです。
前編に続き、AWS Organizationsの一人アドミン検証の後編です。前編と同様、それぞれ主要な操作はコンソールとCLIの両方で行います。
- 前編: 組織の作成とアカウント管理
- 後編: SCP・タグポリシーによる統制 ← 本記事
前編のおさらい
前編では、個人アカウント1つを管理アカウントにして、次の構成を作りました。
- Root配下に
SandboxOUを作成 - 新規メンバーアカウントを1つ作成して
SandboxOUへ移動 - 手持ちの別の既存アカウントを組織に招待し、同じ
SandboxOUへ移動
この時点ではSandbox OUにSCPやタグポリシーはまだアタッチしておらず、配下のアカウントは何でも操作できる状態のままでした。
後編の本記事では、このSandbox OUにSCPとタグポリシーをアタッチし、統制が実際に効いていることを確認していきます。
この記事でわかること
- SCPを使ってOU配下のアカウントにできる操作を制限する方法
- タグポリシーを使ってタグ付けのルールを強制する方法(と、強制されない書き方をした場合にどうなるか)
- 統制が実際に効いていることを確認する方法
ターゲット
- SCPやタグポリシーを触ったことがない方
- マルチアカウント環境でガバナンスを効かせる方法を知りたい方
先に結論
- タグポリシー単体ではタグなしのリソース作成をブロックできません。「非準拠な値のタグ付けをブロックする」のがタグポリシーの役目で、「タグ付け自体を強制する」のはSCPの役目です。この2つを組み合わせて初めて、
envタグが必須かつ値も正しいLambda関数しか作れない状態になります - 実際に3パターンで検証したところ、想定通り
env=sandboxのみ作成に成功し、タグなし・env=productionはそれぞれSCP・タグポリシーによって個別にブロックされました
1. SCPとタグポリシーとは
SCP(サービスコントロールポリシー)は組織内のアカウントが利用できるAWSのアクションの最大範囲を定義するポリシーです。IAMポリシーと似たJSON構文で書きますが、SCPは権限を「付与」することはなく、あくまでアカウント内のIAMユーザーやロールが持つ権限に対する上限(ガードレール)として働きます。
一方、タグポリシーは、組織内のリソースに付けるタグの表記(キーの大文字小文字、許可する値)を標準化するためのポリシーです。ひとことで言うと、SCPは「できる操作の上限を決める」もの、タグポリシーは「タグの付け方のルールを決める」ものです。
両者に共通する重要な仕様として、管理アカウント自身にはSCP・タグポリシーのどちらも適用されません。統制の効果を確認するには、必ずメンバーアカウント側で操作する必要があります。
2. 今回つくる統制内容
SandboxOUに対して、以下の2つのポリシーをアタッチします。対象はどちらもLambda関数です。
-
SCP「
envタグなしのLambda関数作成を拒否」:lambda:CreateFunction実行時にenvタグが付いていなければ、そもそも作成自体をブロックする -
タグポリシー「
envタグの値を標準化」:envタグを付けたLambda関数は、値が許可した値(sandbox)でなければ弾かれるようにする。enforced_forでlambda:functionを指定し、実際にブロックされる状態にする
SCPが「タグ付け自体を強制」し、タグポリシーが「非準拠な値のタグ付けをブロック」する、という役割分担です。
前提条件
- 前編で作成した組織・Sandbox OU・メンバーアカウント
- AWS CLI v2のセットアップ済み環境
- 管理アカウントの認証情報(ポリシーの作成・アタッチ用)と、メンバーアカウントへのスイッチロール(動作確認用)
- 動作確認用にLambda関数を作成できる環境(実行ロールなど)
3. タグポリシーの有効化
SCPは前編で組織を作成した時点で自動的に有効化されていましたが、タグポリシーは別途有効化が必要でした。
コンソールの場合
Organizationsコンソールの「ポリシー」ページから「タグポリシー」を選び、ポリシータイプのページで「タグポリシーを有効にする」を選択します。
CLIの場合
# RootのIDを確認する(前編で確認済みの場合は不要)
$ aws organizations list-roots
# Rootでタグポリシーを有効化する
$ aws organizations enable-policy-type \
--root-id r-xxxx \
--policy-type TAG_POLICY
- 実行結果
{
"Root": {
"Id": "r-xxxx",
"Arn": "arn:aws:organizations::123456789012:root/o-xxxxxxxxxx/r-xxxx",
"Name": "Root",
"PolicyTypes": [
{
"Type": "SERVICE_CONTROL_POLICY",
"Status": "ENABLED"
},
{
"Type": "TAG_POLICY",
"Status": "ENABLED"
}
]
}
}
4. タグポリシーの作成とアタッチ
タグポリシーの内容
envタグの値をsandboxに標準化します。enforced_forでlambda:functionを指定し、非準拠な値でのタグ付けが実際にブロックされる状態にします。
{
"tags": {
"env": {
"tag_key": {
"@@assign": "env"
},
"tag_value": {
"@@assign": ["sandbox"]
},
"enforced_for": {
"@@assign": ["lambda:function"]
}
}
}
}
enforced_forを指定しない場合、このポリシーはタグの表記を強制しません。コンプライアンスレポート上で非準拠と表示されるだけで、実際の作成・タグ付け操作はブロックされない点に注意してください。また、タグポリシーは非準拠な「値」のタグ付けをブロックするものであり、タグが付いていないリソースはそもそも評価対象外です。タグ付け自体を強制したい場合はSCPと組み合わせる必要があります。
コンソールの場合
Organizationsコンソールの「ポリシー」→「タグポリシー」ページで「ポリシーを作成」を選びます。
任意のポリシー名(例:StandardizeEnvTag)とポリシーの説明を入力し、JSONタブにタグポリシーの内容のJSONをペーストし、タグポリシーを作成します。
作成したポリシーの詳細画面で「ターゲット」タブを開き「アタッチ」を選択、対象のSandbox OUを選んで「ポリシーをアタッチ」します。
CLIの場合
--contentにはJSONファイルのパスを指定するので、先にタグポリシーの内容のJSONをstandardize-env-tag.jsonという名前でカレントディレクトリに保存しておきます(テキストエディタで保存でも、cat > standardize-env-tag.jsonのようなヒアドキュメントでも構いません)。
# タグポリシーを作成する(--contentには事前に保存したJSONファイルのパスを指定)
$ aws organizations create-policy \
--name "StandardizeEnvTag" \
--description "envタグの値をsandboxに標準化する(Lambda関数のみ強制)" \
--type TAG_POLICY \
--content file://standardize-env-tag.json
# 作成したタグポリシーをSandbox OUにアタッチする
$ aws organizations attach-policy \
--policy-id p-yyyyyyyy \
--target-id ou-xxxx-xxxxxxxx
- 実行結果
create-policyのレスポンス:
{
"Policy": {
"PolicySummary": {
"Id": "p-yyyyyyyyyy",
"Arn": "arn:aws:organizations::123456789012:policy/o-qxovooonbe/tag_policy/p-yyyyyyyyyy",
"Name": "StandardizeEnvTag",
"Description": "envタグの値をsandboxに標準化する(Lambda関数のみ強制)",
"Type": "TAG_POLICY",
"AwsManaged": false
},
"Content": "{\n \"tags\": {\n \"env\": {\n \"tag_key\": {\n \"@@assign\": \"env\"\n },\n \"tag_value\": {\n \"@@assign\": [\n \"sandbox\"\n ]\n },\n \"enforced_for\": {\n \"@@assign\": [\n \"lambda:function\"\n ]\n }\n }\n }\n}\n"
}
}
※ attach-policyは成功時は何も出力されませんでした。
5. SCPの作成とアタッチ
SCPの内容
envタグが付いていない状態でのlambda:CreateFunctionだけを拒否する、影響範囲の狭いポリシーです。Null条件で「aws:RequestTag/envキーが存在しない(=true)」場合に絞ってDenyしています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "lambda:CreateFunction",
"Resource": "*",
"Condition": {
"Null": {
"aws:RequestTag/env": "true"
}
}
}
]
}
コンソールの場合
Organizationsコンソールの「ポリシー」→「サービスコントロールポリシー」ページで「ポリシーを作成」を選びます。
任意のポリシー名(例:DenyUntaggedLambdaCreation)とポリシーの説明を入力し、JSONタブにSCPの内容のJSONをペーストし、SCPを作成します。
作成したポリシーの詳細画面で「ターゲット」タブを開き「アタッチ」を選択、対象のSandbox OUを選んで「ポリシーをアタッチ」します。
CLIの場合
こちらも--contentにJSONファイルのパスを指定するので、先にSCPの内容のJSONをdeny-untagged-lambda-creation.jsonという名前でカレントディレクトリに保存しておきます。
# SCPを作成する(--contentには事前に保存したJSONファイルのパスを指定)
$ aws organizations create-policy \
--name "DenyUntaggedLambdaCreation" \
--description "envタグなしのLambda関数作成を拒否する" \
--type SERVICE_CONTROL_POLICY \
--content file://deny-untagged-lambda-creation.json
# 作成したSCPをSandbox OUにアタッチする
$ aws organizations attach-policy \
--policy-id p-xxxxxxxx \
--target-id ou-xxxx-xxxxxxxx
- 実行結果
create-policyのレスポンス:
{
"Policy": {
"PolicySummary": {
"Id": "p-xxxxxxxx",
"Arn": "arn:aws:organizations::123456789012:policy/o-qxovooonbe/service_control_policy/p-xxxxxxxx",
"Name": "DenyUntaggedLambdaCreation",
"Description": "envタグなしのLambda関数作成を拒否する",
"Type": "SERVICE_CONTROL_POLICY",
"AwsManaged": false
},
"Content": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Deny\",\n \"Action\": \"lambda:CreateFunction\",\n \"Resource\": \"*\",\n \"Condition\": {\n \"Null\": {\n \"aws:RequestTag/env\": \"true\"\n }\n }\n }\n ]\n}\n"
}
}
※ attach-policyは成功時は何も出力されませんでした。
以上で、タグポリシーとSCPの設定は完了です!
6. 動作確認
管理アカウントにはSCP・タグポリシーのどちらも適用されないため、確認は必ずSandbox OU配下のメンバーアカウントにスイッチロールして行います。Lambdaコンソールから関数を3パターンで作成し、それぞれの挙動を確認します。
パターン1: envタグを正しい値で付けて作成した場合
関数名はtest-fn-compliantとし、envタグの値をsandboxにして関数を作成します。SCP・タグポリシーどちらの条件も満たすため、作成に成功するはずです。
無事成功しました!
気持ち作成完了まで時間がかかった気がしたのでドキドキしました。
パターン2: envタグを付けずに作成した場合
関数名はtest-fn-no-tagとし、envタグを付けずに関数を作成します。SCPのNull条件に引っかかり、作成自体がブロックされるはずです。
こちらは「関数を作成」をクリックして2,3秒でエラーメッセージが表示されて作成が弾かれました。
「管理アカウントのSCPで明示的にブロックされてるから作れない」旨が表示されているので期待通りです。

パターン3: envタグを間違った値で付けて作成した場合
関数名はtest-fn-noncompliantとし、envタグの値をproduction(許可されていない値)にして関数を作成します。タグは付いているのでSCPは通過しますが、タグポリシーのenforced_forに引っかかり、作成がブロックされるはずです。
しっかりブロックされました。
「envに許可していない値が入っている」程度のシンプルなエラーメッセージでわかりやすいですね。ただ、できるなら「どんな値を許容(期待)しているか」までわかると嬉しいですね...。
アタッチ状況の確認
今回は設定後すぐ試しましたが、本来は操作を試す前にポリシーが意図通りアタッチされているかを確認しておくと安心です。
$ aws organizations list-policies-for-target \
--target-id ou-xxxx-xxxxxxxx \
--filter SERVICE_CONTROL_POLICY
$ aws organizations list-policies-for-target \
--target-id ou-xxxx-xxxxxxxx \
--filter TAG_POLICY
注意点(ハマりどころ)
-
タグポリシーだけでは「タグなし」を防げない:
enforced_forを指定しても、ブロックされるのは非準拠な「値」でタグ付けした場合だけです。タグを一切付けずに作成した場合はそもそも評価対象外になるため、タグ付け自体を強制するにはSCPを組み合わせる必要があります(公式ドキュメントにも同様の記載があります) - 管理アカウントでは効果を確認できない: SCP・タグポリシーのどちらも管理アカウント自身には適用されないため、動作確認は必ずメンバーアカウントにスイッチロールして行う必要があります
まとめ
前編・後編を通して、個人アカウント1つを起点に、組織の作成からSCP・タグポリシーによる統制までを一人で一通り体験しました。
実際に3パターンでLambda関数を作成してみると、envタグなしはSCPで、env=productionのような非準拠な値はタグポリシーでそれぞれ個別にブロックされることが確認できました。「タグ付け自体を強制するSCP」と「タグの中身を検証するタグポリシー」という役割分担は、ドキュメントを読んだだけではいまいちピンと来ていなかったのですが、実際に手を動かしてブロックされる瞬間を見て、ようやく腹落ちした感覚があります。
次はAWS Control TowerやAWS Configも試してみて、もう少し実践的な(本番運用に近い)ガバナンス構成にも挑戦していきたいと思います。














