こんにちは。自称ソフトウェアエンジニアの橋本 (@hassaku_63)です。
今回の記事は CDK 慣用句シリーズ(仮)です。
サクッと、よくありそうなやつを紹介します。
この記事で提示したいこと
主に次の2つです。
- Arn モジュールや from メソッドを使って、Stack 管理外のリソースでも CDK 上で扱えるようにする方法
- 特定のリソースにだけアタッチするリソースタグの書き方
前者は grantXxx 系メソッドに代表される、CDK の便利メソッドに乗っかることができる点で大変重宝します...と、この説明でピンと来る人はそこそこ CDK に慣れている方だと思うので、もうちょっと補足します。
例えば、手持ちの CDK プロジェクトで、そのプロジェクト以外の他所で作成されたリソース "X" を参照した実装がやりたいとします。今回の記事の例では ECR Public Repository がリソース "X" に相当しますが、別の CDK App や手作業で定義された任意のリソースに読み換えても構いません。
ここで、手元の CDK プロジェクトでは、そのプロジェクト内で定義する特定のリソース(例えば ECS Task や Lambda Function)から "X" に対する操作が可能な IAM ポリシーを実装したい、という状況を仮定します。
このとき、リソース "X" が単なる ARN 文字列としてではなく、CDK の L2 Construct のオブジェクトとして扱えると、grant 系のメソッドが使えるようになります。ARN 文字列をそのまま(ほぼ)生の IAM ポリシーに埋め込んで追加する、といった実装は不要になり、CDK らしく grant メソッド1行でスマートに所望の権限追加を実装できるようになります。
このような CDK ならではの便利機能を、手元の CDK プロジェクトの管理外にあるリソースに対して、管理外である状態はそのままに実現したいわけです。
題材紹介と注意事項
GuardDuty Runtime Monitoring を特定の ECS Fargate のクラスタに適用する場合の実装例です。
Fargate ではサイドカーの方式で当該サービスを使うことになります。Agent コンテナは ECR Public Repository にいるので、それを pull する権限が Task Execution Role に必要になります。
なお、Runtime Monitoring の有効無効に関しては私自身もまだ条件が整理しきれていない部分があるので、今回の実装例はおそらく完全に要件を満たすものではありません。その点はご了承ください。
導入手順は以下のリンクなどに書かれています。
以下、今回の記事でスコープ外としたものについて簡単に触れます。
本記事で考慮していないこと
ドキュメントには Service が稼働している VPC に Runtime Monitoring 用の VPC Endpoint を自動で追加する、と書かれており、このへんの挙動は私の手元では自動作成されたりされなかったりするケースがありました。自動作成の挙動は IaC とは相性が悪いので、作成条件の整理はまた別の記事でやれたらと思います。この VPC Endpoint は自分で作成してもいいので、IaC で管理している VPC であればおそらく開発者自身が明示的に定義するのがよいと思います。しかしながら、今回の記事では提示したい主題とはそれほど関係がなかったため、考慮外としています。
また、Fargate における Runtime Monitoring はコンテナホストではなくサイドカーから介入させる形を取るため、サイドカーが必要とする分のスペックを追加で考慮してあげる必要があります。つまり、Runtime Monotoring (for Fargate) の導入には Task Definition の変更が必要ということになります。そのあたりの目安は以下のドキュメントに記載があります。
このあたりも ON/OFF に応じて動的に吸収するような書き方が提示できると便利かも?という気はしましたが、今回はこちらも考慮外です*1。
やりたいこと
主に次の2つです。
- ECS Cluster に
GuardDutyManaged: trueのタグを付ける(App 管理下のすべてに対して適用) - Public Repository を参照して、Task Execution Role リソースに対する pull 権限を grant する
実装
こちら。
ちょっとだけ解説
Arn モジュールを使って CloudFormation 管理外のリソースを CDK 上で扱うための方法と、特定リソースにだけアタッチするリソースタグの書き方を持ち帰っていただければいいかなと思っています。
// https://docs.aws.amazon.com/guardduty/latest/ug/runtime-monitoring-ecr-repository-gdu-agent.html // > 533107202818.dkr.ecr.ap-northeast-1.amazonaws.com/aws-guardduty-agent-fargate const gaurddutyAgentRepoArn = cdk.Arn.format({ partition: 'aws', service: 'ecr', account: '533107202818', region: 'ap-northeast-1', resource: 'repository', resourceName: 'aws-guardduty-agent-fargate', }, this); const guarddutyAgentRepo = ecr.Repository.fromRepositoryArn(this, 'GuardDutyAgentRepo', gaurddutyAgentRepoArn);
既知の値を使ってリソースの ARN が取れるようになるので、それを利用して Runtime Monitoring エージェントのレポジトリを特定しています。
Task Execution Role で pull を許可するためには grant メソッドが便利ですが、それには操作許可/拒否の対象となるリソースを CDK 上のオブジェクトとして扱えている必要があります。一方で、Public Repository ではそのレポジトリ名しかわかっていません。
そこで、Public Repository を CDK 上のオブジェクト (IRepository) として扱えるようにするために from 系のメソッドの一種である fromRepositoryArn を用いています。このメソッドのパラメータには ARN が必要であることから、既知のレポジトリ URI の情報を使って Arn クラスで値を組み立てています。
最後に、この CDK App で管理しているすべての ECR Repository に対して Runtime Monitoring を有効化するためのタグを付与する Aspect (Tags) を適用します。このタグは Runtime Monitoring を利用する場合に AWS が参照するタグキーであり、Runtime Monotoring の仕様の一部です(とはいえ、必須なわけではありません)。
// Enable GuardDuty Runtime Monitoring for all ECS Cluster in this app cdk.Tags.of(app).add('GuardDutyManaged', 'true', { includeResourceTypes: ['AWS::ECS::Cluster'], });
このタグは ECS Cluster にだけ適用できれば OK なので、 add の第3引数で適用対象のリソースタイプを限定しています。これで、この Tags の適用対象である CDK App の中に存在する ECS Cluster すべてに対するタグ適用が可能になります。
なお、GuardDuty の仕様としてのこのタグの詳細は、関連ドキュメント(下記)を参照ください。
まとめ
Stack 管理外のリソースでも、割とサクッと扱えることがおわかりいただけるかと思います。
手で変換して Policy をベタ書きしていた方がいらっしゃいましたら、こうした書き方ができないか検討してみてはいかがでしょう。
*1:私自身が ECS のデプロイに関する仕事は CloudFormation の外で、それが得意なツール (例えば ecspress) にやらせたいという思想の持ち主なので、あまり実装のモチベがない...という事情もあります。とはいえ Task Definition の定義は CI の範疇だと思うので、一緒に CDK にやらせてしまっていいような気もします