AWSマネジメントコンソールにおいて、IAMユーザごとに参照(表示)できるEC2インスタンスを制御したい


#はじめに

EC2インスタンス一覧を開いた時に、インスタンス名やらIPやらインスタンスidやらいろいろ見えちゃう。同じAWSアカウントのマネジメントコンソールをいろんな人が使う状況で、人によってはこのインスタンスの情報を見せたくない。

細かいケースは別として、そういったことを気にされる機会は多いのかなと思います。

##ケース例

- 同一のAWSアカウントに、複数のシステムのリソースを相乗りさせているとする。
- Aシステム用のEC2インスタンスと、Bシステム用のEC2インスタンスがあるとする。
- Aシステムの担当者とBシステムの担当者は、別々で、それぞれ異なるグループに属するIAMユーザーを払いだしている。

##やりたいこと
- 各担当者にはマネジメントコンソール経由で操作させたいが、Aシステムの担当者にはBシステム用のEC2インスタンスを参照させたくない。逆もまた然り。
- EC2インスタンスの一覧画面を見た時に、そもそもそこに表示されていない状態にしたい。

わざわざ絵で描くほどでもないのですが、こんなイメージです。

![マネコンイメージ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155963/dd52a1b2-867e-7927-b86c-3d69faa76e40.png)

VPCまで共有の相乗りかどうかは、ここではあまり区別しません。

#そんなことはできない
できません。残念でした。
結論としてはそれでおしまいなのですが、きちんと理由も添えます。

`DescribeInstances`が、細かい制御に対応していないためです。

##DescribeInstancesとは

マネジメントコンソールをポチポチしてEC2インスタンスの一覧画面に遷移する、という行為においても、裏ではAPIが実行されています。ここで呼び出されているAPIが`DescribeInstances`です。

試しに、actionとして`ec2:DescribeInstances`の権限が不足したIAMユーザーでEC2のダッシュボードを覗くと、以下のように権限不足のエラーが表示されます。

![EC2の権限不足エラー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155963/786a9df4-4832-acf1-34b1-6529fe4bbd6b.png)

コマンドそのものを叩くCLIと違い、APIを実行しているという行為が暗黙的になりがちなGUI操作ですが、裏側でどんな処理をしているかと、そこに紐づく権限を有しているかは意識するようにしましょう。

各APIは、公式ドキュメントで細かく仕様を確認することができます。
例えば`DescribeInstances`は以下のページから。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/API_DescribeInstances.html

##DescribeInstancesはリソースレベルの制御に対応していない

APIについて意識したところで、以下のページも見ていきましょう。

[Unsupported Resource-Level Permissions](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/ec2-api-permissions.html#ec2-api-unsupported-resource-permissions)

なかなか重要なことが書いてあります。

>All Amazon EC2 actions can be used in an IAM policy to either grant or deny users permission to use that action. However, not all Amazon EC2 actions support resource-level permissions, which enable you to specify the resources on which an action can be performed. The following Amazon EC2 API actions currently do not support resource-level permissions; therefore, to use these actions in an IAM policy, you must grant users permission to use all resources for the action by using a * wildcard for the Resource element in your statement. All Amazon EC2 actions support the ec2:Region condition key; however, you cannot use other Amazon EC2 condition keys for these actions. 

**筆者意訳：**
すべてのAmazon EC2アクションは、IAMポリシー内で定義することで、ユーザーによる実行を許可したり拒否したりすることができます。ただし、すべてのAmazon EC2アクションがリソースレベルのアクセス許可をサポートしているわけではありません。

リソースレベルのアクセス許可とは、権限制御を行うリソースを細かいレベルで指定できることを指します。後段のAmazon EC2 APIアクションは現在リソースレベルのアクセス許可をサポートしていません。

そのため、IAMポリシーでこれらのアクションを定義する際には、"Resource"部に*(ワイルドカード)を使用して、リソースを細かく絞らない書き方にする必要があります。また、"Condition"部において、`ec2：Region`条件キーはすべてのEC2 APIアクションに対応しているため、リソースレベルのアクセス許可に対応していないものにも使えます。それ以外の条件キーは使えません。

===

ちょっと意訳しすぎました。
`DescribeInstances`はリソースレベルのアクセス許可をサポートしていないAPIアクションのリストに含まれています。他にもなかなかの数のAPIアクションが列挙されており、特にDescribe系のアクションは軒並みサポート外です。

##リソースレベルのアクセス許可に対応しているアクション
イメージがつきづらいかもしれないので、逆にサポートされているアクションを例にとってみます。
公式ドキュメントのサンプルのIAMポリシーを引用します。
[Working with Instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ExamplePolicies_EC2.html#iam-example-instances)

```json:ExampleDescribeAllInstancesandStopStartandTerminateOnlyParticularInstances
{
   "Version": "2012-10-17",
   "Statement": [
   {
   "Effect": "Allow",
      "Action": "ec2:DescribeInstances",　　#リソースレベルがサポートされていないアクション
      "Resource": "*"　　#リソースはワイルドカード指定
   },
   {
      "Effect": "Allow",
      "Action": [
        "ec2:StopInstances", 　#リソースレベルのアクセス許可がサポートされているアクション
        "ec2:StartInstances"
      ],
      "Resource": [　#ARNで指定することでリソースレベルで対象を細かく指定している
      "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
      "arn:aws:ec2:us-east-1:123456789012:instance/i-0598c7d356eba48d7"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "ec2:TerminateInstances",　#サポートされているアクション
      "Resource": "arn:aws:ec2:us-east-1:123456789012:instance/*",　#us-east-1のインスタンスに限定
      "Condition": {
         "StringEquals": {
            "ec2:ResourceTag/purpose": "test"　#条件キーec2:ResourceTagを設定
　　　　##（キー:purpose、値:testのタグが付いているインスタンスだけTerminateできる）
         }
      }
   }

   ]
}
```
上記の例でイメージはついたでしょうか。
リソースレベルのアクセス許可がサポートされているアクションにおいても、対応した条件キーは様々異なりますので、以下のページで確認しましょう。

[Supported Resource-Level Permissions](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/ec2-api-permissions.html#ec2-api-supported-resource-permissions)

![リソースレベル.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155963/d756c531-e643-1ede-ad99-be28f8a05b59.png)

#代替策
やりたいことが実現できないのは分かったので、代わりにどうすればいいかを考えます。

##1.AWSアカウントを分ける
そもそものやつですね。代替策というよりは解決策です。
これができるなら始めから苦労しないよ、という感じでしょうか。

##2.リージョンを分ける
`DescribeInstances`も`ec2:Region`条件キーには対応しているので、今回のケースで言えばAシステムとBシステムを違うリージョンに構築し、担当外のリージョンは参照を拒否するポリシーを追加で設定すれば、おおむねやりたいことが実現できます。（明示的な拒否は明示的な許可より強いです。）

```json:担当外のリージョンのインスタンス参照を拒否するポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1556198178139",
      "Action": [
        "ec2:DescribeInstances"
      ],
      "Effect": "Deny",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:Region": "<見せたくないシステムのリージョン>"
        }
      }
    }
  ]
}
```
とはいえ、そのためだけにリージョン分けるの…？レイテンシーとか料金とか影響あるよ…？という気もするので、なかなか採りづらい案かもしれません。

##3.見えちゃうのはあきらめて禁止アクションを定義

もう見えてしまうのはあきらめて、せめて操作だけは制限しようという案です。

例えばインスタンスに「キー:System、値:ASystem」というようなタグを付与して、そこを条件に、リソースレベルのアクセス許可がサポートされているアクションを列挙して拒否する、という方式です。

```json:拒否するアクションを定義するポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1495531345000",
            "Effect": "Deny",　#拒否する
            "Action": [
                "ec2:RebootInstances",　#再起動
                "ec2:StartInstances",　#開始
                "ec2:StopInstances",　#停止
                "ec2:TerminateInstances"　#削除　を
            ],
            "Condition": {
                "StringEquals": { 　#タグが以下だったら
                   "ec2:ResourceTag/System": "<いじらせたくないシステム名>"
                }
            },
            "Resource": [
                "*"　#ここをさらに絞っても可
            ]
        }
    ]
}
```
やりたいことから結構外れているので、妥協策といったところでしょうか。

#おわりに
ベストプラクティスで言えば、見せたくなければ始めからAWSアカウントを分けるべきなのですが、そうは言ってもいろいろな事情で相乗りさせることは有りうるかと思います。同じリージョンにあるEC2を見せなくすることは現状どうしてもできないので、割り切ったり、運用でなんとかしたりしましょう。

とはいえ「なぜできないのか」、をきちんと説明できることは重要なので、この記事を読んで、「APIの仕様上不可避です」とドヤ顔で宣言しましょう。