CloudWatch Logs のログを別アカウントへ転送!Data Firehose(S3) とのクロスアカウント設定方法

記事タイトルとURLをコピーする

こんにちは。AWS CLIが好きな福島です。

はじめに

今回は、CloudWatch Logs のログを別アカウントに Data Firehose を介して S3 に出力する設定をご紹介します。

概要図

補足

昨今、大規模に AWS を利用する場合は、マルチアカウント構成が主流になっておりますが、 その際にログを集約する AWS アカウントを作成することが多いと思います。

また、AWS サービスによっては、ログを S3 に直接出力できないケースもあるため、 その際に今回の構成が参考になるかと存じます。

今回私は EC2 のサーバーログやRoute 53 Public Host Zone をログ集約する想定で考えてみました。

参考

Firehose を使用したクロスアカウント、クロスリージョンのログデータ共有 - Amazon CloudWatch Logs

ポイント

CloudWatch Logs Destination について

クロスアカウントを実現するためには、受信アカウントで CloudWatch Logs Destination(以降、Destination) というリソースが必要となります。 Destination は、送信アカウントの CoudWatch Logs からログを送信する宛先となるリソースになり、以下赤枠の部分です。

また、Destination は、CloudWatch Logs、Data Firehose、S3を全て同一のアカウントに作成する際は不要かつマネジメントコンソールから作成や確認などができないため、あまり知られていない存在な気がするため、ポイントです。

CloudWatch Logs Destination 関連の権限設定

Destination 関連の権限設定として、以下2つがあります。

  • ①Destination から Data Firehose へのアクセス設定
    • Destination用の IAM ロールにて、アクセス設定を行います
    • 受信および送信アカウント両方の CloudWatch Logs サービスからアクセスできる必要があるようです
    • ドキュメントでは、SourceArnによる許可を行っていましたが、実際の運用を考え、アカウントが増えるたびに設定変更せずに済むようSourceOrgIdを利用します
  • ②送信アカウントからのアクセス許可設定
    • アクセスポリシーにて、アクセス許可設定を行います
    • 実際の運用を考え、SourceOrgId が利用したかったのですが、アクセスポリシーでは非サポートなため、PrincipalでアカウントIDを制限します。(※1)

権限イメージ

※1 アクセスポリシーにPrincipalOrgIDを利用する方法もありますが、これを利用する場合、送信アカウントに CloudWatch Logs のサブスクリプション用 IAM ロールが必要です。追加のリソース作成は避けたいので、今回はPrincipalでアカウントIDを制限します。(※1)

{
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Sid" : "",
        "Effect" : "Allow",
        "Principal" : "*",
        "Action" : "logs:PutSubscriptionFilter",
        "Resource" : "arn:aws:logs:ap-northeast-1:【受信アカウントのID】:destination:test-logs-destination",
        "Condition": {
            "StringEquals":{
                "aws:PrincipalOrgID":"【組織ID】"
            }
        }
      }
    ]
  }

設定の流れ

  • 受信アカウント(222222222222)で実施
    • ①S3 の作成
    • ②Data Firehose の作成
    • ③CloudWatch Logs の Destination 用 IAM ロールの作成
    • ④CloudWatch Logs の Destination の作成
    • ⑤CloudWatch Logs の Destination のアクセスポリシー設定
  • 送信アカウント(111111111111)で実施
    • ⑥ロググループの作成
    • ⑦CloudWatch Logs のサブスクリプションフィルターの作成
  • ⑧動作確認

①S3 の作成(受信アカウントで実施)

S3を作成します。バケットポリシーなどの設定は特に不要です。

aws s3api create-bucket \
--bucket central-logs-$(aws sts get-caller-identity --query Account --output text) \
--create-bucket-configuration LocationConstraint=ap-northeast-1

上記コマンドで作成した S3 のバケット名は、「central-logs-アカウントID」になります。

②Data Firehose の作成(受信アカウントで実施)

  • Data Firehose のページに移動し、「Firehose ストリームを作成」を押下

  • ソース を「Direct Put」、送信先に「Amazon S3」を指定

  • Firehose ストリーム名を「test-data-firehose」(任意)に設定

  • 送信先の設定で S3 バケットに作成したバケットを指定

  • それ以外はデフォルト設定で、「Firehose ストリームを作成」を押下

③CloudWatch Logs の Destination 用 IAM ロールの作成(受信アカウントで実施)

  • IAM のページへ移動し、「ロールを作成」を押下

  • カスタム信頼ポリシーを指定し、以下のポリシーを設定

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Principal": {
            "Service": "logs.ap-northeast-1.amazonaws.com"
        },
        "Action": "sts:AssumeRole",
        "Condition": {
            "StringLike": {
                "aws:SourceOrgId": "【組織ID】"
            }
        }
    }
}

組織IDが分からない場合は、以下のコマンドで確認できます。

aws organizations describe-organization \
   --query "Organization.Id" --output text
  • ポリシーは付与せず、「次へ」を押下

  • IAM ロール名に「logs-destination-role」(任意)を指定し、「ロールを作成」を押下

  • 作成したロールの「許可を追加」>「インラインポリシーを作成」を押下

  • 以下のポリシーを設定

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "firehose:*"
            ],
            "Resource": [
                "arn:aws:firehose:ap-northeast-1:【受信アカウントのIDを指定】:deliverystream/test-data-firehose"
            ]
        }
    ]
}
  • ポリシー名に「logs-destination-policy」(任意)を指定し、「ポリシーの作成」を押下

④CloudWatch Logs の Destination の作成(受信アカウントで実施)

  • Destination の作成
    • 冒頭にも記載した通り、Destination はマネジメントコンソールから作成できないため、AWS CLI を使います。
    • Destination の名前、ターゲットとなる Data Firehose の ARN、Destination 用の IAM ロールを指定します。
aws logs put-destination \
    --destination-name "test-logs-destination" \
    --target-arn "arn:aws:firehose:ap-northeast-1:【受信アカウントのIDを指定】:deliverystream/test-data-firehose" \
    --role-arn "arn:aws:iam::【受信アカウントのIDを指定】:role/logs-destination-role"

実行結果例)

$ aws logs put-destination \
  --destination-name "test-logs-destination" \
  --target-arn "arn:aws:firehose:ap-northeast-1:222222222222:deliverystream/test-data-firehose" \
  --role-arn "arn:aws:iam::222222222222:role/logs-destination-role"
{
    "destination": {
        "destinationName": "test-logs-destination",
        "targetArn": "arn:aws:firehose:ap-northeast-1:222222222222:deliverystream/test-data-firehose",
        "roleArn": "arn:aws:iam::222222222222:role/logs-destination-role",
        "arn": "arn:aws:logs:ap-northeast-1:222222222222:destination:test-logs-destination",
        "creationTime": 1741654446093
    }
}
$

作成した destination の ARN を控えておきます。 様々な ARN が表示されますが、上記における"arn": "arn:aws:logs:ap-northeast-1:222222222222:destination:test-logs-destination"の箇所です。 targetArnと間違えたり、混乱するポイントなので、よく確認しましょう。

⑤CloudWatch Logs の Destination のアクセスポリシー設定(受信アカウントで実施)

  • アクセスポリシー設定用の json ファイル(AccessPolicy.json)作成
{
  "Version" : "2012-10-17",
  "Statement" : [
    {
      "Sid" : "",
      "Effect" : "Allow",
      "Principal" : {
        "AWS" : "【送信アカウントのIDを指定】"
      },
      "Action" : "logs:PutSubscriptionFilter",
      "Resource" : "arn:aws:logs:ap-northeast-1:【受信アカウントのIDを指定】:destination:test-logs-destination"
    }
  ]
}
  • アクセスポリシー設定
aws logs put-destination-policy \
    --destination-name "test-logs-destination" \
    --access-policy file://AccessPolicy.json

実行結果が何も表示されないため、次のコマンドで設定を確認します。

  • アクセスポリシーの確認
aws logs describe-destinations \
--query "destinations[].accessPolicy"  --output text | jq

実行結果例)

$ aws logs describe-destinations --query "destinations[].accessPolicy"  --output text | jq
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "111111111111"
      },
      "Action": "logs:PutSubscriptionFilter",
      "Resource": "arn:aws:logs:ap-northeast-1:222222222222:destination:test-logs-destination"
    }
  ]
}
$

⑥ロググループの作成(送信アカウントで実施)

今回はテスト用のロググループを作成しますが、既に転送したいロググループが存在する場合、本手順はスキップします。

  • CloudWatch Logs のページへ遷移し、「ロググループを作成」を押下

  • ロググループ名に「test-log-group」(任意)を指定し、「作成」を押下

⑦CloudWatch Logs のサブスクリプションフィルターの作成(送信アカウントで実施)

  • 対象のロググループの「サブスクリプションフィルター」タブの「作成」を押下

  • 送信先アカウントで「クロスアカウント」を指定し、送信先ARNに④で控えた ARN を設定

  • サブスクリプションフィルター名に「test-subscription-filter」(任意)を設定

  • 「ストリーミングを開始」を押下

  • サブスクリプションフィルターの設定完了!

⑧動作確認

送信アカウントで実施

  • ログストリームの作成
aws logs create-log-stream \
--log-group-name test-log-group \
--log-stream-name test-log-stream
  • ログストリームにテストイベントを出力
aws logs put-log-events \
--log-group-name test-log-group \
--log-stream-name test-log-stream \
--log-events timestamp=$(date +%s)000,message='Test Log'

マネコンから確認すると以下のように出力できていることが分かります。

受信アカウントで実施

  • オブジェクトの確認

S3に転送されるまで少し時間がかかるため、以下のコマンドを定期的に実行し確認します。

aws s3api list-objects \
--bucket central-logs-$(aws sts get-caller-identity --query Account --output text) \
--query "Contents[].[LastModified,Key]" --output text

※ Data Firehose の設定をした際にテストログが S3 に出力されているため、テストログを出力後に生成されたオブジェクトがあることを確認します。

マネコンからは以下のように確認できます。

  • 最新のオブジェクトを取得
OBJECT_KEY=$(aws s3api list-objects \
--bucket central-logs-$(aws sts get-caller-identity --query Account --output text) \
--query "Contents[-1].Key" --output text)
  • 最新のオブジェクトをダウンロード
aws s3api get-object \
--bucket central-logs-$(aws sts get-caller-identity --query Account --output text) \
--key ${OBJECT_KEY} testfile.gz
  • ダウンロードしたファイルの確認

以下のようにlogEventsの中のmessageTest Logが記載されていることが分かります。

$ cat testfile| jq
{
  "messageType": "DATA_MESSAGE",
  "owner": "111111111111",
  "logGroup": "test-log-group",
  "logStream": "test-log-stream",
  "subscriptionFilters": [
    "test-subscription-filter"
  ],
  "logEvents": [
    {
      "id": "38840292444389641447107713806794005471082427043430137856",
      "timestamp": 1741658949000,
      "message": "Test Log"
    }
  ]
}

終わりに

今回は、CloudWatch Logs のログを別アカウントに転送する方法をご紹介しました。 どなたかのお役に立てれば幸いです。

福島 和弥 (記事一覧)

2019/10 入社

AWS CLIが好きです。