データ転送用Lambdaの公式Cfnテンプレートを修正して複数台デプロイしてみた

## はじめに
こちらは[New Relic 使ってみた情報をシェアしよう！ by New Relic Advent Calendar 2024](https://qiita.com/advent-calendar/2024/newrelic)の17日目の記事になります。
S3バケットに格納しているログデータをLambdaを使って連携する際に、躓いたエピソードを解決策と共に紹介します。

※なお、本投稿はあくまで私個人の見解であり、所属組織の公式見解ではないことをご留意ください。
## Lambdaによるデータ転送とは
Lambdaによるデータ転送は、New Relicから提供されているAWSとの連携ソリューションの一つです。
ELBやCloudFront等のCloudWatch Logsにログを出力できないサービスのログをNew Relicに転送するというものです。
例えば、ALBのアクセスログを転送したという場合は以下のような構成で転送することができます。
![Lambda転送イメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940980/5204862b-d669-71e0-5d67-7cf7a968ba91.jpeg)
詳しくは以下で公式に紹介されているので、ぜひ読んでから本記事の続きを読み進めていただければと思います。
・[Amazon S3に保存したログをNew Relicに取り込む(newrelic.com)](https://newrelic.com/jp/blog/how-to-relic/import-s3-log)
・[S3からのログ送信のためのAWS Lambda(docs.newrelic.com)](https://docs.newrelic.com/jp/docs/logs/forward-logs/aws-lambda-sending-logs-s3/)

※忙しい人向けに実装方法を簡単に説明すると、以下の2ステップで簡単に実装できます
①[AWS Serverless Application Repository](https://serverlessrepo.aws.amazon.com/applications)からLambda関数をデプロイ
②ログデータを格納しているS3バケットをLambda関数のトリガーに設定

## 複数台のLambdaが必要な理由
本記事のゴールである「複数台のLambdaをデプロイしたい！」という思いを持った背景を説明します。
New Relicから提供されているログ解析機能を皆さんはご存じでしょうか？
簡単に説明すると、転送されたログデータを事前指定したlogtypeに応じて見やすく再構成して表示してくれる便利機能です。
例えば、logtypeで"apache"と設定しておくとApacheのアクセスログの構造を見やすいように再構成して表示してくれます。
そして、この機能を利用するためには以下の通り、転送時にlogtype属性を追加する必要があります。

>組み込みの解析ルールを利用するには、ログを転送するときにlogtype属性を追加します。
>[組み込みの解析ルール(newrelic.com)](https://docs.newrelic.com/jp/docs/logs/ui-data/parsing/#built-in-rules)

Lambda転送ではデプロイした関数のNRLogTypeという環境変数の値に追加したいlogtypeを設定することで、logtype属性の追加が実現できます。
ここで、複数台Lambda関数をデプロイしたい理由に繋がります。
例えば、CloudFrontとALBを同じシステムで採用している場合、二つの異なるlogtypeのログデータを転送することになります。
この場合、NRLogTypeの値を変えた二つの関数を用意してそれぞれのログを転送する必要があるのです。

しかし、ここで困ったことが起きました。
ALBのアクセスログを転送するために、"NRLogType:alb"とした関数をデプロイした後に、CloudFrontのログを転送するために"NRLogType:cloudfront-web"とした関数をデプロイしたらエラーが起きたのです。

![画像2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940980/9b8f9846-f4e3-b9bc-8505-cabb1947f8b5.jpeg)
※実際に確認したエラーがこちらです

## CloudFormationテンプレートを覗いてみた
ここからようやく本題に入ります。
エラーの原因を探るために、ALB用に作成したLambda関数のCloudFormationスタックからテンプレートを覗いてみました。
~~~yaml:Lambda関数の定義部分(一部抜粋)
Resources:
  NewRelicLogIngestion:
    Type: AWS::Serverless::Function
    Condition: NoCap
    Properties:
      Runtime: python3.11
      CodeUri:
        Bucket: awsserverlessrepo-changesets-1lsaqs4rvcm2
        Key: xxxxxxxxxxxx/arn:aws:serverlessrepo:us-west-2:533243300146:applications-NewRelic-log-ingestion-s3-versions-1.2.4/c344b825-17ed-4b96-b5fb-f093dd5a6217
      Handler: handler.lambda_handler
      FunctionName: NewRelic-s3-log-ingestion
      Timeout: 900
~~~
なんと、関数名が直書きで固定されてました...
そりゃ当然エラーになりますよね。

## テンプレートを修正してみる
ということでテンプレートを改修して、複数台デプロイできるようにしてみます。
変更ポイントは以下二点です。

①テンプレートにNRFunctionNameというパラメータを追加する
②直書きになっていたFunctionNameの値をNRFunctionNameパラメータから引用する

これによって、スタック作成時に任意の関数名を与られるようになり、関数名の重複エラーを避けられるはずです。
修正内容を以下に紹介します。

まずは①の修正
~~~diff_yaml:パラメータの追加
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Deployment Settings
      Parameters:
      - NRLicenseKey
      - FunctionRole
      - PermissionsBoundary
      - NRLogType
      - DebugEnabled
+     - NRFunctionName
Parameters:
  NRLicenseKey:
    Type: String
    Description: Your New Relic license key. Required to send logs to New Relic Logging.
    Default: YOUR_LICENSE_KEY
+ NRFunctionName
+   Type: String
+   Description: RECOMMEND -> NewRelic-s3-log-ingestion-<LogType>
+   Default: NewRelic-s3-log-ingestion-<LogType>
~~~

続いて、②の修正
※テンプレート内に二か所関数名が直書きされているので、二か所とも直しましょう。
~~~diff_yaml:関数名の変更
Resources:
  NewRelicLogIngestion:
    Type: AWS::Serverless::Function
    Condition: NoCap
    Properties:
      Runtime: python3.11
      CodeUri:
        Bucket: awsserverlessrepo-changesets-1lsaqs4rvcm2
        Key: xxxxxxxxxxxx/arn:aws:serverlessrepo:us-west-2:533243300146:applications-NewRelic-log-ingestion-s3-versions-1.2.4/c344b825-17ed-4b96-b5fb-f093dd5a6217
      Handler: handler.lambda_handler
-      FunctionName: NewRelic-s3-log-ingestion
+      FunctionName:
+        Ref: NRFunctionName
      Timeout: 900
~~~
## 修正したテンプレートでLambda関数をデプロイしてみる
では、早速修正したテンプレートを使って関数をデプロイしてみましょう。
CloudFormationから新たにスタックを作成します。

![スクリーンショット 2024-12-16 232947.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940980/7dd3788b-9df5-091e-960c-8885ac5bbba0.jpeg)
無事パラメータで関数名を指定できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940980/addab5e9-e71c-3896-14a1-873c0ff9e9f6.png)
エラーも解消できました。

## 注意事項
:::note alert
改修したテンプレートはアカウントを跨いで流用しないことを推奨します。
(関数のパッケージダウンロードの辺りでエラーが起こります)
:::
## まとめ(感想)
今回はNew RelicがAWS Serverless Application Repositoryで公開しているCloudFormationテンプレートを改修してみました。
「もしかしたら別の回避策とかあるかもしれないなぁ」と思いつつ今回は力業で解決してしまいましたが、違うアプローチを試した方がいればコメント等でご教示いただけますと幸いです。
本投稿が皆様のお役に立つ機会があれば幸いです。