/ #Aspec 

CDK/Pythonでオレオレガードレールを実装する

もくじ

こんにちはししぃです。

わたしはCDKを使ってインフラを作るのが好きです。

インフラを作る上でオレオレルール的(もっというとコンプライアンス的なもの)なのありますよね。 例えばS3でバケット作ったら暗号化は必須にしようとか、dynamoでTable作ったときは削除policyは、、、とかとか。

でも実装時は色々考えて作ってますから、そのルールがすっかり頭から抜けて実装漏れるとか、ありますよね。

今回はそんなオレオレルール(もといガードレール)をCDKの中に組み込んで、実装漏れをふせぐ方法を書いていきます。

前知識

オレオレガードレールを作るにあたって Aspects を使います。

これはなにかというと、公式ではこんな説明されてます。

Aspects are a way to apply an operation to all constructs in a given scope. The aspect could modify the constructs, such as by adding tags, or it could verify something about the state of the constructs, such as ensuring that all buckets are encrypted. (DeepLの和訳)アスペクトは、与えられたスコープ内のすべての構成要素に操作を適用する方法です。アスペクトは、タグを追加するなどして構成要素を変更したり、すべてのバケットが暗号化されているかなど、構成要素の状態を確認したりすることができます。

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/aspects.html

というものです。

噛み砕くとテンプレート吐き出す前に construct を検証したら、勝手に変更したりできるよ!ってことだと思います。

このへん実際に動き見たほうが早いと思いますので次行きます。

今回作ろうとしてるもの

CDK/Pythonを使って、Lambda Functionをただ1つデプロイするだけのStackを作る。

ただしオレオレルールで、アーキテクチャはARM64が必須。 かつメモリーサイズは指定してないと激おこされるようにします。

では 作っていきましょう👻

まずベースとなるFunction1つだけデプロイするStackを作ります

import aws_cdk as cdk
from aws_cdk import aws_lambda

app = cdk.App()
stack = cdk.Stack(app, "test")

lambda_ = aws_lambda.Function(
    stack,
    "function",
    code=aws_lambda.Code.from_asset("runtime"),
    runtime=aws_lambda.Runtime.PYTHON_3_9,
    handler="index.handler",
)

app.synth()

ここの説明は不要だと思うので次行きます。

ここにオレオレルールを追加すると、、こんなかんじ↓

import aws_cdk as cdk
import jsii
from aws_cdk import aws_lambda
from constructs import IConstruct


# --------------------------------------
# ガードレール的なモノを定義する
@jsii.implements(cdk.IAspect)
class HogeGuardrail:
    def visit(self, node: IConstruct):
        if isinstance(node, aws_lambda.Function):
            cfn_node: aws_lambda.CfnFunction = node.node.default_child
            # アーキテクチャーの判断
            if not cfn_node.architectures == ["arm64"]:
                cdk.Annotations.of(node).add_error("ARM64じゃないよ!!")
            # メモリーの判断
            if not cfn_node.memory_size:
                cdk.Annotations.of(node).add_error("メモリーサイズ設定してないよ!!デフォルトだと最小だからパフォーマンス悪いよ!!")


# --------------------------------------
# CDKでアプリを作る
app = cdk.App()
stack = cdk.Stack(app, "test")

lambda_ = aws_lambda.Function(
    stack,
    "function",
    code=aws_lambda.Code.from_asset("runtime"),
    runtime=aws_lambda.Runtime.PYTHON_3_9,
    handler="index.handler",
)

cdk.Aspects.of(stack).add(HogeGuardrail())

app.synth()

じゃん。

変わったところはHogeGuardrailのクラスが追加されたのと、下の方でそのクラスを利用するcdk.Aspects.of(stack).add(HogeGuardrail())の行が増えたことです。

コードの説明の前に動作確認しておきます。

$ cdk -a "python3 app.py" synth
>> [Error at /test/function] ARM64じゃないよ!!
>> [Error at /test/function] メモリーサイズ設定してないよ!!デフォルトだと最小だからパフォーマンス悪いよ!!
>>
>> Found errors
>> make: *** [synth] Error 1

こんな↑感じで怒られます。

ではHogeGuardrailクラスのコード見てきましょう

@jsii.implements(cdk.IAspect)
class HogeGuardrail:
    def visit(self, node: IConstruct):
        if isinstance(node, aws_lambda.Function):
            cfn_node: aws_lambda.CfnFunction = node.node.default_child
            # アーキテクチャーの判断
            if not cfn_node.architectures == ["arm64"]:
                cdk.Annotations.of(node).add_error("ARM64じゃないよ!!")
            # メモリーの判断
            if not cfn_node.memory_size:
                cdk.Annotations.of(node).add_error("メモリーサイズ設定してないよ!!デフォルトだと最小だからパフォーマンス悪いよ!!")

真っ先にこれ↓なんぞやってとこですね

@jsii.implements(cdk.IAspect)

AWSが開発したjsiiという色々な言語でtypescriptを動かす魔法のようなパッケージがあります。jsii経由でtypescriptを動かすためのインターフェイスを、実装するためのデコレーターが@jsii.implements(cdk.IAspect)です。(このへん私自身もあまり詳しくないので間違ってたらツッコミいただければと、、)

そしてcdk.IAspectの中をちらっと覗くとvisit関数がただ一人でででーん!と存在するので、それを実装してあげます。

ちなみに、CDK/Pythonの公式ドキュメントを見ると

..
class MyAspect(cdk.IAspect):
    def visit(self, node):
..

https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk/Aspects.html

こんな感じで実装できるぜ!!って書いてありますが、これたぶん間違いで、jsii経由で実装しないと動かないと思います。

ここまでで、jsii経由でvisit関数実装すればいいんだなー、というところまでわかると、後はその中でルールを記述するだけです。

visitは指定した construct に入ってるすべての node が来るので、まず node の型をみて Function だったらアーキテクチャとメモリーサイズを検証してます。 検証の注意点は Construct 次第では L1 Construct (CfnXXX系)にしてからでないと設定されているパラメーターを確認できないときがあるということです(今回そのパターン)

次に今回出したERRORに対応してみます。

Lambdaを作ってるところだけもってきました↓が、こんな感じ

..
lambda_ = aws_lambda.Function(
    stack,
    "function",
    code=aws_lambda.Code.from_asset("runtime"),
    runtime=aws_lambda.Runtime.PYTHON_3_9,
    handler="index.handler",
    architecture=aws_lambda.Architecture.ARM_64,  # 追加
    memory_size=256,  # 追加
)
..

コメントがついているところを追加しただけです。

この状態で

$ cdk -a "python3 app.py" synth

すると無事ERRORが表示されなくなり、無事テンプレートが出力されます!!

めでたしめでたしですね。

ではでは今回は以上です。

最後まで読んでいただきありがとうございました!

今回のリポジトリはこちら

https://github.com/sisi100/demo-build-guardrail-with-cdk

Author

Sisii

AWSが好きなフリーランスのエンジニア。主にAWSを使ったバックエンドの開発をしています😊最近Frontも始めました