初めてのIAM：Lambda×CloudWatchで学んだ権限管理

# はじめに
先日業務でAWSに触れる機会があり、初めてIAMについて学びました。
その経験をもとに、実際のコード例を交えながら解説していきます。

# IAMとは？
AWSの各リソースに対するアクセス権限を管理し、セキュリティを向上させるためのサービスです。
全てのサービスへのアクセスは禁止されているデフォルトの状態から、**「どのリソースに対して、誰が何を行うことができるのか（できないのか）」** を定めていくことで、システムやデータの安全性が保たれます。

## IAMポリシー
「どのリソースに対して、誰が何を行うことができるのか（できないのか）」のうち、**「誰が」以外を定めたもの**です。

IAMポリシーは原則として、単体ではアクセスの主体者を規定しておらず、後述のIAMユーザー・IAMロールと呼ばれる主体者にアタッチ（関連付け）することで機能します。
詳細な定義方法は後述します。

## IAMユーザー
**「誰が」を規定するもの**です。

**主体者は運用者・開発者**であって、SNSのアカウントのように一人につき一つ作成し、**恒常的な権限**が付与されます。
理解には、ルートユーザーとの対比が分かりやすいです。

AWSアカウントを契約したときに作成されるルートユーザーは、アカウント内の全リソースに対するフルアクセス権限が付与されており、誤操作やアカウント乗っ取りの際のリスクが高いです。
それに対し、IAMユーザーはIAMポリシーによって**必要な権限のみを付与でき、リスクを最小限に抑えられる**ことから、ルートユーザーではなくこちらの使用が推奨されています。


### IAMグループ
IAMユーザーをグループ分けする機能です。

特定のIAMポリシーをグループ内の全ユーザーに一括して適用できるため、アクセス権限の管理が効率的に行えるようになります。

## IAMロール
**「誰が」を規定する2つ目の方法**です。

**主体者は限られておらず**、また、恒常的ではなく**一時的な権限**が付与されます。
主にAWSリソースが、他のリソースにアクセスする際に使用します。
例えば、Lambda関数がAmazon S3バケット内のファイルを取得する必要がある場合、Lambdaに「S3バケットのファイルにアクセスする権限」を持つIAMロールを割り当てます。

# 実体験で学んだIAM
ここからは、主にIAMユーザーとIAMロールの違いを、実際の使用例に当てはめて解説します。
## 背景
私が行ったのは、Nuxt アプリから、CloudWatch Logsにログを書き込む処理を追加することでした。
アプリ内でconsoleメソッドを使用することで、Lambda単体でも自動的にログを出力しますが、そこにはLambda自体のログを含みます。コスト面からそれを排し、必要なログのみを AWS SDK で出力する必要がありました。


## 手順・IAMの使い分け
1. Lambdaが自動でログ出力する、ロググループへのアクセス権限を拒否
1. AWS SDKでのログ出力先の、ロググループ・ログストリームを作成
1. AWS SDKでのログ出力関数を作成
1. ログ出力関数を実行するLambdaの **IAMロール** を作成し、**IAMポリシー** をアタッチ

4のようにサービス（Lambda）が他のサービス（CloudWatch Logs）にアクセスする場合はIAMロールを使用し、その設定や1, 2のような、開発者によるサービスの操作には、**IAMユーザー**を使用します。

## コード例

### ①Lambdaが自動でログ出力する、ロググループへのアクセス権限を拒否する
Lambdaはデフォルトでは、最初の呼び出し時に自動で作成するロググループに対し、consoleメソッドの内容を出力します。
このとき同時に出力される、consoleメソッドとは無関係なLambda自体のログ（↓）が今回不要なため、デフォルトのロググループへのログ出力権限を拒否します。
```
INIT_START Runtime Version: nodejs:22.v35
Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:8ce0861cbacc1c3e68742d7537616587fd39490817ec413514cf66613b6bed7d
```
下記が、権限を拒否するIAMポリシーです。
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:<account_id>:*"
        },
        {
            "Effect": "Deny",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:<account_id>:log-group:/aws/lambda/<function_name>:*"
            ]
        }
    ]
}
```

### ②ログ出力関数の書き込み先の、ロググループ・ログストリームを作成する
続いて、AWS SDKのログ出力先を作成します。
今回は出力自体に焦点を当てているため詳細な方法は省略します。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html


### ③AWS SDKでCloudWatchにログ出力する関数を作成
続いて、AWS SDKでログ出力関数を作成します。
`PutLogEventsCommand`のコンストラクタに、ロググループ名・ログストリーム名・ログイベント（出力内容）を渡しています。
```javascript
const {
  CloudWatchLogsClient,
  PutLogEventsCommand,
} = require('@aws-sdk/client-cloudwatch-logs');

const client = new CloudWatchLogsClient({
  region: "ap-northeast-1",
});

exports.handler = (log) => {
  const input = {
    logGroupName: "/aws/lambda/<log_group_name>",
    logStreamName: "<log_stream_name>",
    logEvents: [
      {
        timestamp: Date.now(),
        message: JSON.stringify(log),
      },
    ],
  };
  client.send(new PutLogEventsCommand(input));
};
```


### ④ログ出力関数を実行するLambda関数の、IAMロールを作成し、IAMポリシーをアタッチする
③で作成した関数を実行するLambdaのIAMロールに、②で作成したロググループへの書き込み権限を付与します。
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:PutLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "logs:CreateLogStream"
      ],
      "Resource": "arn:aws:logs:<region>:<account_id>:log-group:<log_group_name>:*"
    }
  ]
}
```

以上の手順によって、Lambda自体のログを排したログ出力が可能となります。


# IAMユーザーとIAMロールの違いまとめ
| 項目       | IAMユーザー                 | IAMロール                        |
|------------|----------------------------|---------------------------------|
| 主体    | 運用者・開発者          | 主にAWSサービス          |
| 権限    | 恒常的    | 一時的    |
| 使用例      | Lambdaの設定、IAMロールの作成 | LambdaがCloudWatchにログを送る   |

# 参考文献
https://zenn.dev/mi_01_24fu/articles/aws-iam-2024_03_26