LoginSignup
9
1

More than 1 year has passed since last update.

AWS CDK(Python)でアカウントIDを含むS3バケットを作る

Last updated at Posted at 2021-11-30

この記事は ハンズラボ AdventCalendar2021 1日目の記事です。

プログラム言語でAWSインフラを定義できるという AWS CDK を試してみました。言語はPython3を利用します。

インストール

Node、Python3、virtualenvが必須のため事前にセットアップ。
私の環境ではpyenvも導入しています。

$ node -v
v12.12.0
$ python -V
Python 3.8.6
$ virtualenv --version
virtualenv 20.10.0 from /Users/xxxx/.pyenv/versions/3.8.6/lib/python3.8/site-packages/virtualenv/__init__.py

早速CDKをインストールします。

$ npm install -g aws-cdk
# インストール確認
$ cdk --version
1.134.0 (build dd5e12d)

初期設定

今回の検証用ディレクトリを作成し、そこで初期設定を行います。

$ mkdir cdk_practice
$ cd cdk_practice

$ cdk init app --language python

source .venv/bin/activatepip install -r requirements.txt を実行しなさい、とメッセージが表示されますが私は pipenv を使いたいので小細工をします。
特にこだわりのない方はメッセージに従ってコマンドを実行することをおすすめします。

# デフォルトで作成された仮想環境を削除
$ rm -rf .venv

# pipenvで再セットアップ
$ export PIPENV_VENV_IN_PROJECT=true
$ pipenv --python 3

# ライブラリインストール
$ pipenv install
$ pipenv install --dev -r ./requirements-dev.txt

# もう使わないので削除
$ rm -rf requirements*.txt

# 仮想環境有効化
$ pipenv shell

コーディング

ドキュメントによるとCDKでは構築したいサービスに対応するコアモジュールをインストールする必要があるようです。命名規則は aws-cdk.SERVICE-NAME とのことです。今回はS3バケットを作成したいので、以下のコマンドを実行しS3用モジュールをインストールします。

$ pipenv install aws-cdk.aws-s3

次はコードを見ていきます。主に修正を行うのは app.pycdk_practice/cdk_practice_stack.py のようです。過去のバージョンでは setup.py も存在していたようですが、現在のバージョンでは配置されないようです。

app.py

初期化直後の app.py はこんな感じです。

#!/usr/bin/env python3
import os

from aws_cdk import core as cdk

# For consistency with TypeScript code, `cdk` is the preferred import name for
# the CDK's core module.  The following line also imports it as `core` for use
# with examples from the CDK Developer's Guide, which are in the process of
# being updated to use `cdk`.  You may delete this import if you don't need it.
from aws_cdk import core

from cdk_practice.cdk_practice_stack import CdkPracticeStack


app = core.App()
CdkPracticeStack(app, "CdkPracticeStack",
    # If you don't specify 'env', this stack will be environment-agnostic.
    # Account/Region-dependent features and context lookups will not work,
    # but a single synthesized template can be deployed anywhere.

    # Uncomment the next line to specialize this stack for the AWS Account
    # and Region that are implied by the current CLI configuration.

    #env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),

    # Uncomment the next line if you know exactly what Account and Region you
    # want to deploy the stack to. */

    #env=core.Environment(account='123456789012', region='us-east-1'),

    # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
    )

app.synth()

これに以下の通り修正を加えました。

  • from aws_cdk import core as cdk でimportするのが推奨というコメントに従い from aws_cdk import core を削除
  • CdkPracticeStackにenv追加
#!/usr/bin/env python3
import os
from aws_cdk import core as cdk
from cdk_practice.cdk_practice_stack import CdkPracticeStack

app = cdk.App()
CdkPracticeStack(app, "CdkPracticeStack",env=cdk.Environment(region='ap-northeast-1.'))

app.synth()

複数のスタックを管理したい場合はCdkPracticeStackと同様の定義を追加していけば良さそうですね。

cdk_practice_stack.py

次は初期化直後の cdk_practice/cdk_practice_stack.py です。

from aws_cdk import (
    core as cdk
    # aws_sqs as sqs,
)

# For consistency with other languages, `cdk` is the preferred import name for
# the CDK's core module.  The following line also imports it as `core` for use
# with examples from the CDK Developer's Guide, which are in the process of
# being updated to use `cdk`.  You may delete this import if you don't need it.
from aws_cdk import core


class CdkPracticeStack(cdk.Stack):

    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # The code that defines your stack goes here

        # example resource
        # queue = sqs.Queue(
        #     self, "CdkPracticeQueue",
        #     visibility_timeout=cdk.Duration.seconds(300),
        # )

今回構築したいS3バケットの仕様は以下の通りです。

  • バケット名重複防止のためバケット名にAWSアカウントIDを含ませる
  • デフォルトキーを利用してサーバーサイドで暗号化する
  • ファイルの有効期限を1年とする
  • スタックが削除されたら合わせて削除する

コアモジュールの仕様書と睨めっこしながら書き上げたコードがこちらです。

from aws_cdk import (
    core as cdk,
    aws_s3 as s3,
)


class CdkPracticeStack(cdk.Stack):

    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # アカウントIDの取得
        accountId = cdk.Aws.ACCOUNT_ID

        # バケットの作成
        bucket = s3.Bucket(self,
            'cdkpracticebucket',
            # サーバーサイド暗号化
            encryption=s3.BucketEncryption.S3_MANAGED,
            # バケット名にアカウントIDを付与
            # f'hogehoge-{accountId}'だとNG
            bucket_name= 'cdkpracticebucket-' + accountId
        )

        # 1年経過で削除するライフサイクルルールを追加
        bucket.add_lifecycle_rule(
            enabled=True,
            expiration=cdk.Duration.days(365)
        )

        # スタック削除時に一緒に削除
        bucket.apply_removal_policy(cdk.RemovalPolicy.DESTROY)

テンプレート生成

以下のコマンドを実行すると記述した定義をもとにCloudFormationテンプレート(yaml形式)が生成され、標準出力に表示されます。定義に誤りがある場合はエラーになります。

$ cdk synth

今回はこのようなテンプレートになりました。また/cdk.outにも同様にテンプレートが出力されるようです。

Resources:
  cdkpracticebucket59CD39AD:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName:
        Fn::Join:
          - ""
          - - cdkpracticebucket-
            - Ref: AWS::AccountId
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 365
            Status: Enabled
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: CdkPracticeStack/cdkpracticebucket/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Modules: aws-cdk=1.134.0

デプロイ

テンプレートを見た感じ問題なさそうですのでデプロイしていきます。ここからはAWSアカウントに対するcredentialが必要ですので、別途設定してください。

$ cdk deploy

うまくいきました。
スクリーンショット 2021-11-28 12.09.58.png

S3バケットが作成されています。
スクリーンショット 2021-11-28 13.21.35.png

暗号化やライフサイクルルールも大丈夫そうです。
スクリーンショット 2021-11-28 13.21.45.png

スクリーンショット 2021-11-28 13.22.03.png

後片付け

一通り検証できたので後片付けをします。削除コマンドは cdk destroy です。

$ cdk destroy
Are you sure you want to delete: CdkPracticeStack (y/n)? y
CdkPracticeStack: destroying...



 ✅  CdkPracticeStack: destroyed

削除できました。
スクリーンショット 2021-11-28 13.24.11.png

apply_removal_policy を指定していたのでバケットも消えています。指定しなかった場合、デフォルト設定がRETAINのため削除がスキップされます。

ハマりポイント

バケット名にアカウント名を含める際、CloudFormationでは擬似パラメーターを活用してこのように書くことが多いです。

!Sub hogehoge-${AWS::AccountId}

CDKでアカウントIDを取得したい場合は aws_cdk.core.Aws を見れば良いようです。

そこでPythonでも同じようにbucket_nameを指定してみたところエラー。

bucket_name= f'cdkpracticebucket-{accountId}'

色々と調査した結果、以下の記述だとテンプレートに変換することができました。

bucket_name= 'cdkpracticebucket-' + accountId

生成されたテンプレートでは、Fn::Joinを利用した形式に自動変換されていました。加算演算子を使った場合のみ、CDK内部で何らかの変換処理が行われているものと思われます。

      BucketName:
        Fn::Join:
          - ""
          - - cdkpracticebucket-
            - Ref: AWS::AccountId

バケット名のような文字列のみ受け付けるパラメータに擬似パラメータを渡したい場合は工夫が必要のようです。

感想

上記のハマりなどもあり最初は苦労しました。しかし、一般のプログラム言語を利用していることからIDEの豊富な支援をそのまま受けることができ、補完機能なども多数扱えるためノウハウが溜まればCloudFormationより高速に構築できるようになる可能性を感じました。テストの実施もできるようですので、もう少し深掘りして検証を行ってみても良いかもしれません。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1