CloudFormationのドリフトを自動検出するシステムを構築したら、ServerlessFrameworkの自動タグ付け機能に苦しめられた

# 目次
- [はじめに](#はじめに)
- [定義と実際のリソースとの差分を検出する方法](#定義と実際のリソースとの差分を検出する方法)
- [作成したシステム](#作成したシステム)
- [サーバーレスフレームワークではうまく機能しなかった話](#サーバーレスフレームワークではうまく機能しなかった話)
- [最後に](#最後に)

# はじめに
インフラの管理をより安全に再現性高く行うために、CloudFormationを利用したIaCを構築している方は多いと思います。ただ、完全にコードによる管理を徹底できているかというと、できていない方も多いのではないでしょうか。

例えば、障害時の緊急対応では、コードの変更と反映を待っていると対応が遅くなってしまうので、コンソールから修正を加えることもあると思います。このコンソールでの変更がそのまま放置されると、コードでの定義と実際のリソースとの間に差分が生じ、後日別件でコードに変更を加えてリソースの更新を行った時に、障害時に行った修正が消え去り、予期せぬ事故が生じる可能性、などのリスクを抱えることになると思います。

そのため、安全なサービス運営のためには、コードでの定義と実際のリソースとの間に差分が起きていないかを監視し、もし差分が生じている場合には、検知できる状態にしておくことは大切だと思います。

この記事では、コードでの定義とリソースの実態との間の差分を検知する機能を試すためにプライベートで軽く作成したシステムの紹介と、そのシステムが自動タグ付け機能というServerlessFrameworkの機能により、当該フレームワークで構築されたシステムに対してはうまく動作しなかった、という課題の共有をします。

# 定義と実際のリソースとの差分を検出する方法
作成したシステムについて触れる前に、どうすれば、コードでの定義と実際のリソースとの差分を検出することができるかを紹介します。

以下のドキュメントが示すように、CloudFormationにはドリフト検出という機能があり、この機能を使うことで、コード上の定義と実際のリソースとの間に生じている差分を検出することができます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html

コンソール上からこの機能を実行する場合は、以下の手順で実行します。

1. ドリフトを検出したいスタックを開き、スタックアクションから「ドリフトの検出」を選択することで、検出を開始することができます。
ドリフトの検出が終わったら、画像赤枠の部分に検出結果が表示されます。CloudFormationの定義と実際のリソースとの間に差分が生じている場合、画像のように「DRIFTED」と表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/05634f52-2e36-7bbb-7a10-2f2d46c2b18a.png)
1. 上の画像の「ドリフト結果を表示」というボタンを押すと、検出結果の詳細画面を見ることができます。
画像赤枠の部分のように「MODIFIED」と表示されているリソースが、差分が発生しているリソースになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/8d0cccd6-2a33-69ff-5794-ff0aea0f3992.png)
1. 差分が発生しているリソースを選択し「ドリフトの詳細を表示」というボタンを押すと、CloudFormationの定義と、実際のリソースの定義との比較を見ることができます。
ここで差分が生じている部分が、修正すべき差分、ということになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/bfb7bfd2-2d00-4553-adba-a49e2bf5f5f9.png)

# 作成したシステム
この機能が気になっていたので、上記のドリフト検出を自動で実行し、ドリフトを検出した場合は、slackに通知を行うシステムをプライベートで軽く実装してみました！
ソースコードは以下にあります。

https://github.com/Kaito-Ozaki/cfn-drift-police

このコードをAWS上にデプロイしておけば、週次でスタックに対するドリフト検出を実行し、ドリフトしているスタックがあれば以下のようなメッセージをslackに投稿してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/15a4bb3f-404c-c647-1495-a875abf37011.png)
このシステムの稼働をさせることで、CloudFormationの定義と実際のリソースとの差分によるリスクに怯えることなく、安心してインフラを管理できるようになるはずです！！
<br>
<br>
<br>
<br>
<br>
はずだったのです、、

# サーバーレスフレームワークではうまく機能しなかった話
上記のシステムはシンプルなCloudFormationによる実装には機能するものの、ServerlessFrameworkを経由して実装されたリソースではうまく機能しないことがわかりました。

うまく動作しなかった原因は、ServerlessFrameworkの自動タグ付け機能です。
以下のコードでは、タグに関する定義は行っていませんが、この定義をServerlessFrameworkのresources配下に置き、デプロイした場合、ServerlessFrameworkの機能により、自動的にタグ付けが行われます。
（画像の赤枠部分が、自動で追加された定義です）
```yml
xxx:
  Type: AWS::CodeStarNotifications::NotificationRule
  Properties:
    Name: ${self:provider.stage}-xxx
    DetailType: FULL
    Resource: arn:aws:codepipeline:${self:provider.region}:${self:custom.accountId.${self:provider.stage}}:${self:provider.stage}-xxx
    EventTypeIds:
      - codepipeline-pipeline-pipeline-execution-succeeded
      - codepipeline-pipeline-pipeline-execution-failed
      - codepipeline-pipeline-pipeline-execution-canceled
      - codepipeline-pipeline-manual-approval-needed
      - codepipeline-pipeline-manual-approval-failed
      - codepipeline-pipeline-manual-approval-succeeded
    Targets:
      - TargetType: AWSChatbotSlack
        TargetAddress: arn:aws:chatbot::${self:custom.accountId.${self:provider.stage}}:xxx
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/bfb7bfd2-2d00-4553-adba-a49e2bf5f5f9.png)
このタグ付け機能が上手くいけば、何ら問題なく差分の検出を行えるのですが、例えば以下のcodestar notification ruleのように、タグをつけることのできないリソースについては、タグの定義が追加されているにも関わらず、リソースにはタグがつけられないので、定義とリソースとの差分が生じてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646249/0e46f238-8d00-f566-903d-f8a6d962a255.png)
そのため、ServerlessFrameworkによって構築されたスタックでかつ、タグを設定できないリソースを含むスタックについては常にドリフトが検出されることとなり、運用上の問題はなくともドリフトが発生しているものとしてアラートが出され続けてしまう、という事象が発生しました。

もちろん、ServerlessFrameworkが自動でタグをつけないようにする方法も探しましたが、見つけることができず、今回作成したシステムは、一部のスタックには適用できない、ということになりました、、

# 最後に
少し気持ちの悪い終わり方になりますが、以上、私が作成したシステムと、そのシステムがぶつかった課題です。

まさかServerlessFrameworkが、このフレームワークを利用して作成された全てのリソースに対して問答無用でタグを追加しているとは思いもよりませんでした、、

この課題は未だ解決できていないので、もし自動タグ付けの回避方法などを知っている方がいれば、ぜひ教えていただきたいです...！
また、この記事が少しでも同じようなことをしようとしている方の参考になれば、幸いです...！（一応、ServerlessFramework以外の手段で作成されたシステムには適用できるので、もし同じようなことをしたい方がいれば、ぜひこのシステムを参考にしていただければ、と思います）