継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

CloudWatch Logs のログをアカウントまたぎで共有する

タダです。

この記事は、「challenge-every-month全員でアウトプット芸人 Advent Calendar」と「後回し改善ウィーク」の1日目の記事になります。

業務で CloudWatch Logs のログをアカウントまたぎで共有し、ログ分析に活用する要件がありました。そのための検証を行なったので、まとめていきます。

実現方法

実現するには、CloudWatch Logs のログを送信先アカウントで、 Kinesis Data Streams の設定が必要です。仮に、アカウント A(111111111111)からアカウント B(999999999999)にログを連携する場合で考えます。

以下が手順です。

  1. アカウント B で、 Kinesis Data Streams の送信先ストリーム、データ入力を行うサービスロールの作成と送信先ストリームへの適用
  2. アカウント A で、連携するログのサブスクリプションフィルターを作成
  3. アカウント B で、 Kinesis Data Streams のシャードにログが送信されているかを確認

参考情報

今回の手順は下記のドキュメントに記載があるため、手順に則って検証を行いました。なお、確認した環境は以下になります。

docs.aws.amazon.com

1.Kinesis Data Streams の送信先ストリーム、データ入力を行うサービスロールの作成と送信先ストリームへの適用

まず、ログの送信先のアカウントの設定を行いますが、AWS CLI を使って実施していきます。

最初に、 Kinesis Data Streams に送信先ストリームを作ります。ストリームが有効化されるまで1,2分ほどかかります。

aws kinesis create-stream --stream-name "RecipientStream" --shard-count 1

続いて、サービスロールの作成を行います。なお、サービスロールと適用するポリシーの中身は以下の通りです。

# サービスロール
{
  "Statement": {
    "Effect": "Allow",
    "Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }
}

# ポリシー
{
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "kinesis:PutRecord",
            "Resource": "arn:aws:kinesis:ap-northeast-1:999999999999:stream/RecipientStream"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::999999999999:role/CWLtoKinesisRole"
        }
    ]
}

サービスロールの作成と、ポリシーの適用は以下の通りです。

# サービスロールの作成
aws iam create-role \
--role-name CWLtoKinesisRole \
--assume-role-policy-document file://TrustPolicyForCWL.json

# サービスロールへのポリシーアタッチ
aws iam put-role-policy \
    --role-name CWLtoKinesisRole \
    --policy-name Permissions-Policy-For-CWL \
    --policy-document file://PermissionsForCWL.json

# CloudWatch Logs の書き込み先の作成
aws logs put-destination --destination-name "testDestination" --target-arn "arn:aws:kinesis:ap-northeast-1:999999999999:stream/RecipientStream" --role-arn "arn:aws:iam::999999999999:role/CWLtoKinesisRole"
{
    "destination": {
        "roleArn": "arn:aws:iam::999999999999:role/CWLtoKinesisRole",
        "creationTime": 1555918710180,
        "destinationName": "testDestination",
        "accessPolicy": "{\n  \"Version\" : \"2012-10-17\",\n  \"Statement\" : [\n    {\n      \"Sid\" : \"\",\n      \"Effect\" : \"Allow\",\n      \"Principal\" : {\n        \"AWS\" : \"111111111111\"\n      },\n      \"Action\" : \"logs:PutSubscriptionFilter\",\n      \"Resource\" : \"arn:aws:logs:ap-northeast-1:999999999999:destination:testDestination\"\n    }\n  ]\n}\n\n",
        "targetArn": "arn:aws:kinesis:ap-northeast-1:999999999999:stream/RecipientStream",
        "arn": "arn:aws:logs:ap-northeast-1:999999999999:destination:testDestination"
    }
}

# CloudWatch Logs の書き込み先のポリシー作成
aws logs put-destination-policy \
    --destination-name "testDestination" \
    --access-policy file://AccessPolicy.json

2. アカウント A で、連携するログのサブスクリプションフィルターを作成

続いて、ログの送信元アカウントで Kinesis Data Streams にログを共有するためのサブスクリプションフィルターを作成します。今回は、 CloudTrail のログに記録された「tada」に関するログを送る例を書きます。

aws logs put-subscription-filter \
    --log-group-name "test-trail" \
    --filter-name "RecipientStream" \
    --filter-pattern "{$.userIdentity.type = tada}" \
    --destination-arn "arn:aws:logs:ap-northeast-1:999999999999:destination:testDestination"

3. アカウント B で、 Kinesis Data Streams のシャードにログが送信されているかを確認

最後に、Kinesis Data Streams のシャードにログが送信されているかを確認します。

# シャードのイテレーター情報を取得する
aws kinesis get-shard-iterator \
      --stream-name RecipientStream \
      --shard-id shardId-000000000000 \
      --shard-iterator-type TRIM_HORIZON

# 出力例(ShardIterator の情報をメモしておきます)
{
    "ShardIterator": "AAAAAAAAAAE8Lco6O3cLC0izP1f5u1D1UT01g5ojvj035Tb/XuvIMH+mnthnrGr6MDNi7fSAFVnJnrF2gtGWyn7Qi/dGf+5eOO0iriz8y3K3iBHaBMX9ESB3Zg2nnC2CGntkUyOCQZxRez5BSWmWqrhp1WtvQXvICCbhjIrtTPLRMpPcKyXPIqVa9X4n/f99l8IR9cSQ0+t7Ev1mzFclVD0Lat2EGcn+"
}

# シャードのデータ情報を取得する
aws kinesis get-records \
      --limit 10 \
      --shard-iterator "AAAAAAAAAAE8Lco6O3cLC0izP1f5u1D1UT01g5ojvj035Tb/XuvIMH+mnthnrGr6MDNi7fSAFVnJnrF2gtGWyn7Qi/dGf+5eOO0iriz8y3K3iBHaBMX9ESB3Zg2nnC2CGntkUyOCQZxRez5BSWmWqrhp1WtvQXvICCbhjIrtTPLRMpPcKyXPIqVa9X4n/f99l8IR9cSQ0+t7Ev1mzFclVD0Lat2EGcn+"

# 出力例(Data の情報をメモしておきます)
{
    "Records": [
        {
            "Data": "H4sIAAAAAAAAADWOTQuCQBRF/8ow64jMPsBdiLXIEjJoERKTvvSRzsi8MYnwvzdqLQ/3cu/58AqIRA7ndw3c4350PJ+i8HYI4nizC/iEq1aC7pNSNVkrTFqEKicblCrfadXUNhspNhpENSI1d0o11gaV3GJpQBP3rsnQC14gTY8fjtlYN2g1jKjsmLNcrty1u5jNXceZ/PV6gUvIfnrsp+cxv4D0iTJnBYjSFEw9WGaXUIr+me1RAiExGtSmvEu6Lwa4ORDyAAAA",
            "PartitionKey": "3e21f5e8240cbb048271af4fdb892a1c",
            "ApproximateArrivalTimestamp": 1556373403.028,
            "SequenceNumber": "49595189074146188426156213207759355357749573689819529218"
        }
    ],
    "NextShardIterator": "AAAAAAAAAAGBjdvkN0Th99yo7tnUiLUxwXX7dgG4TinEGCRQrpVR7Y+2euYlNhuDA7KvfYOwC9LdS+ZNj8sSA5boHkLhWsdsLNuo/+Cn2qtzBeJkE1JtcYlhCr7qZowctmxtNHU3qfPSTF/ywSqEjstCEaPoxs083K+AKrj+OvHHNC6fqxkKjeoi51GodxIhnkyRWL3E12ib6teL0JwXSVYg9iUIUc15",
    "MillisBehindLatest": 0
}

# Data の中身を確認する(Base64 でエンコードされているためデコードする.ドキュメントに書いている、「base64 -d」ではデコードできなかったので注意です)
echo -n "H4sIAAAAAAAAADWOTQuCQBRF/8ow64jMPsBdiLXIEjJoERKTvvSRzsi8MYnwvzdqLQ/3cu/58AqIRA7ndw3c4350PJ+i8HYI4nizC/iEq1aC7pNSNVkrTFqEKicblCrfadXUNhspNhpENSI1d0o11gaV3GJpQBP3rsnQC14gTY8fjtlYN2g1jKjsmLNcrty1u5jNXceZ/PV6gUvIfnrsp+cxv4D0iTJnBYjSFEw9WGaXUIr+me1RAiExGtSmvEu6Lwa4ORDyAAAA" | base64 -D | zcat

# 出力例(確認する段階が早すぎたのか、テストメッセージが表示されました)
{"messageType":"CONTROL_MESSAGE","owner":"CloudwatchLogs","logGroup":"","logStream":"","subscriptionFilters":[],"logEvents":[{"id":"","timestamp":1556373402311,"message":"CWL CONTROL MESSAGE: Checking health of destination Kinesis stream."}]}

上記の設定で Kinesis Data Streams にログデータが共有されるようになりました。これでログを Kinesis Data Firehose と連携して S3に出力する構成も可能になりました。

まとめ

CloudWatch Logs のログを Kinesis Data Streamsを使った共有する方法を紹介しました。通常の業務システムのアカウントとログ分析基盤のアカウントを分離したり、ログアーカイブや統合するためのアカウントのような場合に検討できる設定かと思います。何か役に立てば幸いです。

challenge-every-month全員でアウトプット芸人 Advent Calendar」の2日目は @zucky_zakizaki さんの記事になります。お楽しみに!