IAMポリシーの Condition とは、「どんなときにそのステートメントを効かせるか」を、条件演算子・条件キー・値の3つの組み合わせで書く要素です。たとえば「接続元の IP アドレスがこの範囲のとき」「MFA で認証しているとき」「タグが一致するとき」といった条件を付けられます。

この記事では、条件演算子の全一覧表と、よく使うグローバル条件キーの早見表をまず示し、そのあとで「複数の条件は AND か OR か」「キーが無いときはどう判定されるか」という、つまずきやすい2つの規則を図と真理値表で整理します。コピーして使える JSON も10本用意しました。

確認環境:Ubuntu 24.04、Python 3.11、AWS CLI 1.46.1。評価規則は AWS IAM ユーザーガイドの記述をもとにし、その規則を Python で書き写した小さな評価器で、ガイドに載っている評価例の表 24 件と同じ結果になることを確かめています。掲載した JSON 10本は、AWS の API をローカルで再現するツール(moto 5.2.3)に登録して、文法が通ることを確かめました。

Condition の早見表

基本の形

"Condition": {
  "条件演算子": {
    "条件キー": "値"
  }
}

読み方は「条件キーの値を、条件演算子のやり方で、値と比べる」です。次の例は「リクエスト先のリージョン(aws:RequestedRegion)が、文字列として ap-northeast-1 に等しい(StringEquals)とき」という意味になります。

"Condition": {
  "StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}

評価規則の早見表

書き方 判定
条件演算子を複数並べる すべて満たすとき(AND)
1つの演算子の中に条件キーを複数並べる すべて満たすとき(AND)
1つの条件キーに値を配列で複数書く どれか1つに一致すればよい(OR)
否定の演算子(StringNotEquals など)に値を複数書く どれにも一致しないとき(NOR)
リクエストに条件キーが無い(肯定の演算子) 条件は false
リクエストに条件キーが無い(否定の演算子) 条件は true
リクエストに条件キーが無い(~IfExists) 条件は true
Null 演算子 キーが無いことを "true"、有ることを "false" で調べる

やりたいこと別の組み合わせ

やりたいこと 条件演算子 条件キー
接続元 IP で絞る IpAddress / NotIpAddress aws:SourceIp
VPC エンドポイント経由だけにする StringEquals / StringNotEquals aws:SourceVpce
MFA を必須にする BoolIfExists aws:MultiFactorAuthPresent
MFA 認証からの経過時間で絞る NumericLessThan aws:MultiFactorAuthAge
リソースのタグで絞る StringEquals aws:ResourceTag/タグキー
利用者のタグとリソースのタグを突き合わせる StringEquals aws:ResourceTag/タグキー と ${aws:PrincipalTag/タグキー}
作成時のタグを決める StringEquals、ForAllValues:StringEquals aws:RequestTag/タグキー、aws:TagKeys
リージョンを絞る StringEquals / StringNotEquals aws:RequestedRegion
期間を絞る DateGreaterThan / DateLessThan aws:CurrentTime
HTTPS だけにする Bool aws:SecureTransport
一時的な認証情報だけにする Null aws:TokenIssueTime

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IAMポリシーConditionの書き方|条件演算子と条件キー一覧」

条件演算子の全一覧

AWS IAM ユーザーガイドの「条件演算子」のページに沿って、型ごとにまとめます。

文字列

条件演算子 意味
StringEquals 完全一致(大文字と小文字を区別する)
StringNotEquals 一致しない
StringEqualsIgnoreCase 完全一致(大文字と小文字を区別しない)
StringNotEqualsIgnoreCase 一致しない(大文字と小文字を区別しない)
StringLike ワイルドカードつきの一致。* は0文字以上、? は1文字。大文字と小文字を区別する
StringNotLike ワイルドカードつきで一致しない

部分一致をしたいときは StringLike を使い、ワイルドカードを明示します。文字列の演算子では、値に ${aws:username} のようなポリシー変数を書けます。

数値

条件演算子 意味
NumericEquals 等しい
NumericNotEquals 等しくない
NumericLessThan より小さい
NumericLessThanEquals 以下
NumericGreaterThan より大きい
NumericGreaterThanEquals 以上

日付

条件演算子 意味
DateEquals その日時に等しい
DateNotEquals その日時に等しくない
DateLessThan その日時より前
DateLessThanEquals その日時以前
DateGreaterThan その日時より後
DateGreaterThanEquals その日時以後

aws:CurrentTime や aws:EpochTime と組み合わせます。値は ISO 8601 形式(例:2026-10-01T00:00:00Z)か、エポック秒で書きます。

Bool・バイナリ

条件演算子 意味
Bool true / false の一致
BinaryEquals バイナリ値の一致(値は Base64 で書く)

IP アドレス

条件演算子 意味
IpAddress 指定した IP アドレスまたは範囲に入っている
NotIpAddress 指定した IP アドレスまたは範囲に入っていない

値は CIDR 形式(例:203.0.113.0/24、2001:DB8:1234:5678::/64)で書きます。プレフィックスを省くと /32 として扱われます。

ARN

条件演算子 意味
ArnEquals、ArnLike ARN の一致。コロンで区切った6つの部分ごとに比べ、* と ? が使える。2つは同じ動きをする
ArnNotEquals、ArnNotLike ARN が一致しない。2つは同じ動きをする

キーの有無を調べる:Null

書き方 意味
"Null": { "キー": "true" } リクエストにそのキーが無い
"Null": { "キー": "false" } リクエストにそのキーが有り、値が入っている

末尾に付ける:IfExists

IfExists は、Null 以外のどの演算子にも末尾に付けられます(StringEqualsIfExists、BoolIfExists、NotIpAddressIfExists など)。意味は「キーが有れば普通に比べる。キーが無ければ、その条件は true とする」です。

先頭に付ける:ForAllValues と ForAnyValue

aws:TagKeys のように、1回のリクエストで複数の値を持つ条件キー(複数値のキー)には、演算子の先頭に次のどちらかを付けます。

修飾子 意味 キーが無いとき
ForAllValues: リクエストの値がすべて、ポリシーの値のどれかに一致する true(空の集合は「すべて一致」と見なされる)
ForAnyValue: リクエストの値のどれか1つが、ポリシーの値のどれかに一致する false

ForAllValues を Allow で使うときは、Null の "false" を組み合わせて「キーが有ること」も条件にします。キーが無いリクエストまで許可してしまうことを防ぐためで、ユーザーガイドでも勧められている書き方です。

"Condition": {
  "ForAllValues:StringEquals": { "aws:TagKeys": ["environment", "cost-center"] },
  "Null": { "aws:TagKeys": "false" }
}

aws:ResourceTag/タグキー のように値が1つだけのキー(単一値のキー)には、この修飾子は付けません。

型ごとの対応表

型 ワイルドカード ポリシー変数 主な条件キーの例
文字列 StringLike 系で使える 使える aws:RequestedRegion、aws:PrincipalTag/キー
数値 使えない 使えない aws:MultiFactorAuthAge
日付 使えない 使えない aws:CurrentTime、aws:TokenIssueTime
Bool 使えない 使える aws:SecureTransport、aws:MultiFactorAuthPresent
IP アドレス 使えない(CIDR で範囲を書く) 使えない aws:SourceIp
ARN 使える 使える aws:PrincipalArn、aws:SourceArn

よく使うグローバル条件キーの早見表

「aws:」で始まるキーは、サービスをまたいで使えるグローバル条件キーです。「リクエストに含まれるとき」の列が大切で、含まれない場面があるキーは、あとで説明する「キーが無いとき」の規則に関わります。

接続元(ネットワーク)

条件キー 型 内容 リクエストに含まれるとき
aws:SourceIp IP アドレス リクエスト元の IP アドレス VPC エンドポイント経由のリクエストには含まれない
aws:VpcSourceIp IP アドレス VPC の中でのリクエスト元 IP アドレス VPC エンドポイント経由のとき
aws:SourceVpc 文字列 経由した VPC の ID VPC エンドポイント経由のときだけ
aws:SourceVpce 文字列 経由した VPC エンドポイントの ID VPC エンドポイント経由のときだけ
aws:SecureTransport Bool SSL/TLS で送られたか 常に含まれる

認証の状態

条件キー 型 内容 リクエストに含まれるとき
aws:MultiFactorAuthPresent Bool 一時的な認証情報が MFA を使って発行されたか 一時的な認証情報を使うときだけ。アクセスキーで署名した CLI・API・SDK のリクエストには含まれない
aws:MultiFactorAuthAge 数値 MFA で認証してからの秒数 一時的な認証情報を使うときだけ
aws:TokenIssueTime 日付 一時的な認証情報が発行された日時 一時的な認証情報を使うときだけ

利用者(プリンシパル)

条件キー 型 内容 リクエストに含まれるとき
aws:PrincipalTag/タグキー 文字列 リクエストした利用者に付いているタグ ユーザーやロールにタグが付いているとき、またはセッションタグがあるとき
aws:PrincipalArn ARN リクエストした利用者の ARN 署名つきのリクエストすべて
aws:PrincipalAccount 文字列 利用者が属するアカウント すべてのリクエスト
aws:PrincipalOrgID 文字列 利用者が属する組織(AWS Organizations)の ID 利用者が組織のメンバーのとき
aws:PrincipalType 文字列 利用者の種類 すべてのリクエスト
aws:username 文字列 IAM ユーザー名 IAM ユーザーのとき(ロールやルートユーザーには無い)
aws:userid 文字列 利用者の一意の ID すべてのリクエスト
aws:PrincipalIsAWSService Bool AWS のサービス自身による呼び出しか 署名つきのリクエストすべて

リクエストの内容とリソース

条件キー 型 内容 リクエストに含まれるとき
aws:RequestedRegion 文字列 リクエスト先のリージョン リージョンを持つリクエスト
aws:CurrentTime 日付 現在の日時 常に含まれる
aws:EpochTime 数値(エポック秒) 現在の日時 常に含まれる
aws:RequestTag/タグキー 文字列 リクエストで指定したタグの値 リクエストでタグを指定したとき
aws:TagKeys 文字列(複数値) リクエストで指定したタグキーの一覧 リクエストでタグを指定したとき
aws:ResourceTag/タグキー 文字列 操作対象のリソースに付いているタグ 対象のリソースにタグがあるとき
aws:ResourceAccount 文字列 リソースが属するアカウント リクエストに含まれる
aws:ResourceOrgID 文字列 リソースが属する組織の ID リクエストに含まれる
aws:SourceArn ARN AWS のサービスが呼び出すとき、きっかけになったリソースの ARN AWS のサービスが直接呼び出すときだけ
aws:SourceAccount 文字列 AWS のサービスが呼び出すとき、きっかけになったリソースのアカウント AWS のサービスが直接呼び出すときだけ
aws:CalledVia 文字列(複数値) リクエストが経由した AWS サービスの一覧 AWS サービスを経由したとき
aws:ViaAWSService Bool AWS サービスを経由したリクエストか AWS サービスを経由したとき
aws:UserAgent 文字列 ユーザーエージェント 常に含まれる

このほかに、s3:prefix や ec2:InstanceType のような、サービスごとの条件キーがあります。使えるキーは操作やリソースの種類によって決まっています。

複数条件の評価規則:AND と OR

規則は2つだけです。

  • 演算子どうし、条件キーどうしは AND(全部を満たす必要がある)
  • 1つの条件キーに書いた複数の値は OR(どれか1つに一致すればよい)
flowchart TD
  C["Condition(すべて AND)"] --> O1["StringEquals"]
  C --> O2["IpAddress"]
  O1 --> K1["aws:PrincipalTag/team<br>(値どうしは OR)"]
  O1 --> K2["aws:RequestedRegion<br>(値どうしは OR)"]
  O2 --> K3["aws:SourceIp<br>(値どうしは OR)"]
  K1 --> V1["web"]
  K1 --> V2["data"]
  K2 --> V3["ap-northeast-1"]
  K2 --> V4["ap-northeast-3"]
  K3 --> V5["203.0.113.0/24"]

図を JSON にすると、次のとおりです。

"Condition": {
  "StringEquals": {
    "aws:PrincipalTag/team": ["web", "data"],
    "aws:RequestedRegion": ["ap-northeast-1", "ap-northeast-3"]
  },
  "IpAddress": {
    "aws:SourceIp": "203.0.113.0/24"
  }
}

これは「(team が web または data)かつ(リージョンが東京 または 大阪)かつ(接続元が 203.0.113.0/24 の中)」という意味です。

team リージョン 接続元 IP 条件
web ap-northeast-1 203.0.113.25 true(3つとも満たす)
data ap-northeast-3 203.0.113.25 true
web us-east-1 203.0.113.25 false(リージョンが合わない)
web ap-northeast-1 198.51.100.7 false(IP が合わない)
タグなし ap-northeast-1 203.0.113.25 false(キーが無い)

否定の演算子に複数の値を書くと「どれにも一致しない」

"Condition": {
  "StringNotEquals": {
    "aws:RequestedRegion": ["ap-northeast-1", "ap-northeast-3"]
  }
}

これは「東京でも大阪でもない」ときに true になります。「東京ではない、または、大阪ではない」(=いつも true)にはならないので、否定の演算子に許可したい値を並べる書き方は、そのまま「それ以外」を表せます。

同じ演算子は1回だけ書く

JSON では、同じ階層に同じ名前のキーを2回書けません。同じ演算子で複数の条件キーを調べたいときは、1つの演算子の中にまとめます。

"Condition": {
  "StringEquals": {
    "aws:PrincipalTag/team": "web",
    "aws:RequestedRegion": "ap-northeast-1"
  }
}

「同じキーについて、ある日時以後かつ別の日時より前」のように、同じ条件キーに2つの比較をしたいときは、DateGreaterThanEquals と DateLessThan のように、演算子を分けて書きます(コピペ用 JSON の 8 を参照)。

キーが無いときの評価:Deny と BoolIfExists の真理値表

条件キーは、いつもリクエストに含まれるとは限りません。たとえば aws:MultiFactorAuthPresent は、アクセスキーで署名した CLI のリクエストには含まれません。このときの判定は、ユーザーガイドで次のように決められています。

演算子の種類 キーが無いときの条件の結果
肯定の演算子(StringEquals、Bool、IpAddress、NumericLessThan など) false
否定の演算子(StringNotEquals、NotIpAddress、ArnNotLike など) true
~IfExists を付けた演算子 true
ForAllValues: を付けた演算子 true
ForAnyValue: を付けた演算子 false
Null "true" と書いていれば true、"false" と書いていれば false

これを「MFA を必須にする」書き方に当てはめると、次の真理値表になります。列は、リクエストに含まれる aws:MultiFactorAuthPresent の状態です。

Effect 条件 true(MFA あり) false(MFA なし) キーなし(アクセスキーなど)
Deny "Bool": {"aws:MultiFactorAuthPresent": "false"} 拒否しない 拒否する 拒否しない
Deny "BoolIfExists": {"aws:MultiFactorAuthPresent": "false"} 拒否しない 拒否する 拒否する
Allow "Bool": {"aws:MultiFactorAuthPresent": "true"} 許可する 許可しない 許可しない
Allow "BoolIfExists": {"aws:MultiFactorAuthPresent": "true"} 許可する 許可しない 許可する

読み取れることは3つあります。

  • Deny で MFA を必須にするなら BoolIfExists と "false" の組み合わせを使う。Bool だけだと、キーが無いリクエスト(アクセスキーでの呼び出し)は条件が false になり、拒否の対象から外れる
  • Allow で MFA を条件にするなら、IfExists を付けない Bool と "true"を使う。BoolIfExists にすると、キーが無いリクエストまで許可される
  • 迷ったら「キーが無いリクエストを、拒否したいのか、通したいのか」を先に決め、上の表から選ぶ

ユーザーガイドも、MFA の確認には Deny・BoolIfExists・false の組み合わせを勧めており、この形は MFA なしの一時的な認証情報と、アクセスキーなどの長期の認証情報によるリクエストの両方を拒否する、と説明しています。

評価規則を手元で試す:Python の小さな評価器

規則を文章で覚えるより、値を変えて試したほうが早く身に付きます。ここまでの規則を Python に書き写した、学習用の評価器を載せます。実際の判定は AWS の側で行われるもので、これは規則を理解するための道具です。cond_eval.py として保存します。

"""IAM の Condition の評価規則を、公式ドキュメントの記述どおりに写した学習用の評価器。
context は {条件キー: 値 または 値のリスト}。キーが無い = リクエストにその情報が含まれない。"""
import ipaddress
import re
from datetime import datetime


def _like(pattern, value):
    # * は0文字以上、? は1文字に一致
    rx = "".join(".*" if c == "*" else "." if c == "?" else re.escape(c) for c in pattern)
    return re.fullmatch(rx, value) is not None


def _arn(pattern, value):
    p, v = pattern.split(":", 5), value.split(":", 5)
    return len(p) == len(v) and all(_like(a, b) for a, b in zip(p, v))


def _date(s):
    return datetime.fromisoformat(str(s).replace("Z", "+00:00"))


def _bool(s):
    return str(s).lower() == "true"


# 演算子名 -> (1つの値どうしを比べる関数, 否定の演算子か)
OPS = {
    "StringEquals": (lambda p, v: p == v, False),
    "StringNotEquals": (lambda p, v: p == v, True),
    "StringEqualsIgnoreCase": (lambda p, v: p.lower() == v.lower(), False),
    "StringNotEqualsIgnoreCase": (lambda p, v: p.lower() == v.lower(), True),
    "StringLike": (_like, False),
    "StringNotLike": (_like, True),
    "NumericEquals": (lambda p, v: float(v) == float(p), False),
    "NumericNotEquals": (lambda p, v: float(v) == float(p), True),
    "NumericLessThan": (lambda p, v: float(v) < float(p), False),
    "NumericLessThanEquals": (lambda p, v: float(v) <= float(p), False),
    "NumericGreaterThan": (lambda p, v: float(v) > float(p), False),
    "NumericGreaterThanEquals": (lambda p, v: float(v) >= float(p), False),
    "DateEquals": (lambda p, v: _date(v) == _date(p), False),
    "DateNotEquals": (lambda p, v: _date(v) == _date(p), True),
    "DateLessThan": (lambda p, v: _date(v) < _date(p), False),
    "DateLessThanEquals": (lambda p, v: _date(v) <= _date(p), False),
    "DateGreaterThan": (lambda p, v: _date(v) > _date(p), False),
    "DateGreaterThanEquals": (lambda p, v: _date(v) >= _date(p), False),
    "Bool": (lambda p, v: _bool(p) == _bool(v), False),
    "IpAddress": (lambda p, v: ipaddress.ip_address(v) in ipaddress.ip_network(p, strict=False), False),
    "NotIpAddress": (lambda p, v: ipaddress.ip_address(v) in ipaddress.ip_network(p, strict=False), True),
    "ArnEquals": (_arn, False),
    "ArnLike": (_arn, False),
    "ArnNotEquals": (_arn, True),
    "ArnNotLike": (_arn, True),
}


def _as_list(x):
    return x if isinstance(x, list) else [x]


def test_key(operator, key, policy_values, context):
    """1つの演算子・1つの条件キーの判定"""
    policy_values = _as_list(policy_values)
    present = key in context
    if operator == "Null":  # "true" = キーが無い、"false" = キーが有る
        return _bool(policy_values[0]) == (not present)

    qualifier = None
    if ":" in operator:  # ForAllValues: / ForAnyValue:
        qualifier, operator = operator.split(":", 1)
    if_exists = operator.endswith("IfExists")
    if if_exists:
        operator = operator[: -len("IfExists")]
    match, negated = OPS[operator]

    if not present:
        if if_exists:
            return True  # IfExists:キーが無ければ true
        if qualifier == "ForAllValues":
            return True  # 空の集合は「すべて一致」とみなされる
        if qualifier == "ForAnyValue":
            return False
        return negated  # 肯定の演算子は false、否定の演算子は true

    def hit(v):  # リクエストの値1つが、ポリシーの値のどれかに一致するか(OR)
        return any(match(p, v) for p in policy_values)

    values = _as_list(context[key])
    if qualifier == "ForAllValues":
        return all(hit(v) != negated for v in values)
    if qualifier == "ForAnyValue":
        return any(hit(v) != negated for v in values)
    return hit(values[0]) != negated  # 否定の演算子は「どれにも一致しない」(NOR)


def evaluate(condition, context):
    """Condition ブロック全体:演算子どうし、キーどうしはすべて AND"""
    return all(
        test_key(op, key, vals, context)
        for op, keys in condition.items()
        for key, vals in keys.items()
    )

使い方

from cond_eval import evaluate

condition = {
    "StringEquals": {"aws:RequestedRegion": ["ap-northeast-1", "ap-northeast-3"]},
    "IpAddress": {"aws:SourceIp": "203.0.113.0/24"},
}
print(evaluate(condition, {"aws:RequestedRegion": "ap-northeast-3", "aws:SourceIp": "203.0.113.25"}))
print(evaluate(condition, {"aws:RequestedRegion": "us-east-1", "aws:SourceIp": "203.0.113.25"}))
print(evaluate(condition, {"aws:RequestedRegion": "ap-northeast-1", "aws:SourceIp": "198.51.100.7"}))
print(evaluate(condition, {"aws:RequestedRegion": "ap-northeast-1"}))
True
False
False
False

1行目は両方の条件を満たす場合、2行目はリージョンが合わない場合、3行目は IP が合わない場合、4行目は aws:SourceIp が無い場合です。

ユーザーガイドの評価例と突き合わせる

ユーザーガイドには、条件とリクエストの組み合わせごとに「一致する/しない」を示した表が載っています。AND と OR の例、否定の演算子の例、IfExists の例、ForAllValues と ForAnyValue の例、あわせて 24 件を評価器に通したところ、すべて同じ結果になりました。

AND/OR 1        評価器=True  ドキュメント=True  一致
AND/OR 2        評価器=False ドキュメント=False 一致
…(省略)…
IfExists 3      評価器=True  ドキュメント=True  一致
ForAllValues 1  評価器=True  ドキュメント=True  一致
…(省略)…
ForAnyValue 6   評価器=False ドキュメント=False 一致
24/24 件が一致

前の章の真理値表も、この評価器で {"Bool": {...}} と {"BoolIfExists": {...}} を、キーの有無を変えながら評価して作ったものです。

Effect | 演算子 | 値 | true | false | キーなし
Deny | Bool | false | 拒否しない | 拒否する | 拒否しない
Deny | BoolIfExists | false | 拒否しない | 拒否する | 拒否する
Allow | Bool | true | 許可する | 許可しない | 許可しない
Allow | BoolIfExists | true | 許可する | 許可しない | 許可する

AWS のシミュレーターで確かめる

AWS の側で判定を確かめたいときは、aws iam simulate-custom-policy が使えます。--context-entries で条件キーの値を渡せるので、Condition の動きを、実際に操作を行わずに調べられます。AWS CLI のリファレンスに載っている例は次のとおりです。

aws iam simulate-custom-policy \
    --policy-input-list '{"Version":"2012-10-17","Statement":{"Effect":"Allow","Action":"dynamodb:*","Resource":"*","Condition":{"DateGreaterThan":{"aws:CurrentTime":"2018-08-16T12:00:00Z"}}}}' \
    --action-names dynamodb:CreateBackup \
    --context-entries "ContextKeyName='aws:CurrentTime',ContextKeyValues='2019-04-25T11:00:00Z',ContextKeyType=date"

リファレンスの出力例では、EvalDecision が allowed になっています。EvalDecision の値は allowed(許可)、explicitDeny(明示的な拒否)、implicitDeny(許可が無いための拒否)の3種類です。ContextKeyType には string、stringList、numeric、boolean、ip、date などを指定します。

コピペ用 JSON 10本

バケット名、IP アドレス、ID などは例の値です。自分の環境の値に置き換えて使います。各 JSON の下に、Condition を上の評価器に通した結果を「リクエストの情報 → 条件の結果」の形で添えました。Deny のステートメントでは true が「拒否する」、Allow では true が「許可の対象になる」を表します。

1. IP 制限:決めた範囲の外からの操作を拒否する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOutsideOffice",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "2001:db8:1234:5678::/64"
          ]
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
SourceIp=203.0.113.25 false(拒否しない)
SourceIp=198.51.100.7 true(拒否する)
SourceIp=2001:db8:1234:5678::1 false(拒否しない)
キーなし true(拒否する)

NotIpAddress は否定の演算子なので、aws:SourceIp が無いリクエストも拒否の対象になります。VPC エンドポイント経由のリクエストには aws:SourceIp が含まれないため、VPC の中からも使う場合は、次の 2 のように aws:SourceVpce を使う条件と使い分けます。また、ユーザーガイドには、AWS のサービスが代わりに行う呼び出しを Deny で止めてしまわないよう、aws:PrincipalIsAWSService が false であることを条件に加える方法が紹介されています。

2. IP 制限(VPC 版):決めた VPC エンドポイント経由だけにする

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOutsideVpce",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::example-report-bucket",
        "arn:aws:s3:::example-report-bucket/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpce": "vpce-0123456789abcdef0"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
SourceVpce=vpce-0123456789abcdef0 false(拒否しない)
SourceVpce=vpce-0fedcba9876543210 true(拒否する)
キーなし(インターネット経由) true(拒否する)

3. MFA 必須:停止と削除は MFA で認証したときだけ

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyStopWithoutMfa",
      "Effect": "Deny",
      "Action": [
        "ec2:StopInstances",
        "ec2:TerminateInstances"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
MultiFactorAuthPresent=true false(拒否しない)
MultiFactorAuthPresent=false true(拒否する)
キーなし true(拒否する)

このステートメントは拒否だけを定めています。操作そのものの許可は、別の Allow のステートメントで与えます。

4. MFA 必須(時間つき):MFA 認証から1時間以内だけ許可する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowWithinOneHourOfMfa",
      "Effect": "Allow",
      "Action": "ec2:TerminateInstances",
      "Resource": "*",
      "Condition": {
        "NumericLessThan": {
          "aws:MultiFactorAuthAge": "3600"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
MultiFactorAuthAge=600 true(許可の対象)
MultiFactorAuthAge=7200 false
キーなし false

NumericLessThan は肯定の演算子なので、キーが無いリクエストは許可の対象になりません。

5. タグ一致:自分と同じ team タグのインスタンスだけ起動・停止できる

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSameTeam",
      "Effect": "Allow",
      "Action": [
        "ec2:StartInstances",
        "ec2:StopInstances"
      ],
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/team": "${aws:PrincipalTag/team}"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
利用者の team=web、リソースの team=web true(許可の対象)
利用者の team=web、リソースの team=data false

${aws:PrincipalTag/team} はポリシー変数で、評価のときに利用者のタグの値に置き換えられます。チームが増えてもポリシーを書き足さずに済むのが、この書き方の良いところです。

6. タグ一致(作成時):決めたタグを付けたときだけ作成できる

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCreateWithTags",
      "Effect": "Allow",
      "Action": "ec2:CreateVolume",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/env": [
            "dev",
            "stg"
          ]
        },
        "ForAllValues:StringEquals": {
          "aws:TagKeys": [
            "env",
            "team"
          ]
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
RequestTag/env=dev、TagKeys=[env, team] true(許可の対象)
RequestTag/env=prod、TagKeys=[env] false(env の値が合わない)
RequestTag/env=dev、TagKeys=[env, owner] false(決めていないタグキーがある)
キーなし(タグを付けていない) false

StringEquals が「env タグの値は dev か stg」、ForAllValues:StringEquals が「付けてよいタグキーは env と team だけ」を表します。aws:RequestTag/env の条件があるので、タグを付けないリクエストは許可の対象になりません。

7. リージョン制限:東京と大阪以外での操作を拒否する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOutsideJapan",
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "sts:*",
        "support:*",
        "cloudfront:*",
        "route53:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "ap-northeast-1",
            "ap-northeast-3"
          ]
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
RequestedRegion=ap-northeast-1 false(拒否しない)
RequestedRegion=ap-northeast-3 false(拒否しない)
RequestedRegion=us-east-1 true(拒否する)

IAM や CloudFront のように、リージョンを選ばずに使うサービスは NotAction で対象から外しています。外すサービスは、使っているものに合わせて見直します。

8. 期間限定:2026年10月の間だけ読み取りを許可する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowDuringOctober",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::example-report-bucket/*",
      "Condition": {
        "DateGreaterThanEquals": {
          "aws:CurrentTime": "2026-09-30T15:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2026-10-31T15:00:00Z"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
CurrentTime=2026-10-05T06:00:00Z true(許可の対象)
CurrentTime=2026-09-30T14:59:59Z false(開始前)
CurrentTime=2026-10-31T15:00:00Z false(終了後)

日時は UTC で書いています。日本時間の 10月1日 0:00 は、UTC では 9月30日 15:00 です。始まりと終わりを、別々の演算子(DateGreaterThanEquals と DateLessThan)に分けて書くのがポイントです。

9. HTTPS 必須:暗号化されていない接続を拒否する(バケットポリシー)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyHttp",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::example-report-bucket",
        "arn:aws:s3:::example-report-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
SecureTransport=true false(拒否しない)
SecureTransport=false true(拒否する)

これは S3 のバケットポリシー(リソースに付けるポリシー)なので、Principal を書いています。

10. 一時的な認証情報だけ許可する(Null の例)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowOnlyTemporaryCredentials",
      "Effect": "Allow",
      "Action": "ec2:Describe*",
      "Resource": "*",
      "Condition": {
        "Null": {
          "aws:TokenIssueTime": "false"
        }
      }
    }
  ]
}
リクエストの情報 条件の結果
TokenIssueTime=2026-10-05T05:00:00Z true(許可の対象)
キーなし(アクセスキーなどの長期の認証情報) false

aws:TokenIssueTime は一時的な認証情報を使うときだけ含まれるので、「キーが有ること」(Null が "false")を条件にすると、ロールなどの一時的な認証情報による操作だけを許可できます。

つまずきやすい点のチェックリスト

確認すること 理由
演算子の名前のつづりと大文字・小文字 StringEquals、IpAddress、ArnLike など、決まった名前で書く
同じ演算子を2回書いていないか JSON のキーは重複できない。1つの演算子の中にまとめる
Bool の値を引用符つきの文字列で書いたか "true" / "false" と書く
否定の演算子で、キーが無い場合を考えたか キーが無いと true になる
Deny で「キーが無いリクエスト」も止めたいか 止めたいなら IfExists を付ける
Allow に IfExists を付けていないか キーが無いリクエストまで許可の対象になる
ForAllValues を Allow で使うとき、Null の "false" を添えたか キーが無いと true になる
部分一致に StringLike とワイルドカードを使ったか StringEquals は完全一致
その条件キーが、対象の操作で使えるか サービスごとの条件キーは、操作やリソースの種類で決まっている

よくある質問

Q. Condition の中で OR を書くには、どうすればよいですか? A. 同じ条件キーについての OR は、値を配列で並べます。別々の条件キーについての OR(A または B)は、1つの Condition の中では書けないので、ステートメントを2つに分けます。ステートメントどうしは、どれか1つが当てはまれば効くためです。

Q. StringEquals と StringLike は、どう使い分けますか? A. 値がはっきり決まっているときは StringEquals、前方一致などの部分一致をしたいときは StringLike とワイルドカードを使います。

Q. IfExists は、どんなときに付けますか? A. 「キーが有るときだけ比べたい」ときです。1つのステートメントが複数の種類のリソースにまたがっていて、一部のリソースにしか無い条件キーを使う場合や、Deny でキーが無いリクエストも止めたい場合に使います。

Q. 書いた Condition が思ったとおりに効くか、確かめる方法はありますか? A. aws iam simulate-custom-policy に --context-entries で条件キーの値を渡すと、操作を行わずに判定だけを確かめられます。規則の理解には、この記事の評価器で値を変えて試す方法も役立ちます。

まとめ

  • Condition は 条件演算子・条件キー・値 の3つで書く
  • 演算子どうし・キーどうしは AND、1つのキーの複数の値は OR。否定の演算子の複数の値は「どれにも一致しない」
  • リクエストにキーが無いと、肯定の演算子は false、否定の演算子は true、IfExists は true
  • Deny で MFA を必須にするなら BoolIfExists と "false"。Allow なら Bool と "true"
  • 複数値のキーには ForAllValues / ForAnyValue。ForAllValues を Allow で使うなら Null の "false" を添える
  • 条件キーは 「リクエストに含まれるとき」 まで確かめて選ぶ
  • 判定は simulate-custom-policy で、操作を行わずに確かめられる

参考・出典

  • AWS IAM ユーザーガイド「IAM JSON ポリシー要素:条件演算子(reference_policies_elements_condition_operators)」
  • AWS IAM ユーザーガイド「複数のコンテキストキーまたは値による条件(reference_policies_condition-logic-multiple-context-keys-or-values)」
  • AWS IAM ユーザーガイド「単一値と複数値のコンテキストキー(reference_policies_condition-single-vs-multi-valued-context-keys)」
  • AWS IAM ユーザーガイド「AWS グローバル条件コンテキストキー(reference_policies_condition-keys)」
  • AWS CLI Command Reference「iam simulate-custom-policy」

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IAMポリシーConditionの書き方|条件演算子と条件キー一覧」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?