0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CloudFormationでCloudFormationテンプレートのマクロを構築しよう

0
Last updated at Posted at 2020-11-09

AWS CloudFormationでCloudFormationテンプレートのマクロを構築しようのアイキャッチ

2026年9月に、サポート終了済みのPython 3.7からPython 3.13へLambdaランタイムを更新しました。利用できるランタイムと廃止予定日はAWS Lambda runtimesで確認してください。

この記事でわかること

  • AWS CloudFormationでCloudFormationテンプレートのマクロを構築しよう で扱う作業の全体像
  • 手順を進める前に確認したい前提
  • 今の環境で試すときに気をつけたい点

はじめに

AWS CloudFormationを使って、CloudFormationマクロを構築するテンプレートのサンプルです。

テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。

コードはGitHubにもあります。

特定ブランチだけを取得する場合は、次のコマンドを実行します。

# macro-01 ブランチだけを取得する
git clone -b macro-01 --single-branch \
  https://github.com/s-horikoshi-public/CloudFormation-Template.git

今回は、akane というシステムの all 環境を想定しています。
同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。

ディレクトリ構成
akane (システム)
  └─ macro (スタック)
      ├─ macro.yml (CFnテンプレート)
      └─ all-parameters.json (all 環境のパラメータ)

マクロとは

  • マクロを使用すると、検索して置換操作のような単純なアクションからテンプレート全体の広範な変換まで、テンプレートに対してカスタム処理を実行できるようになります。

AWS リソース構築内容

  1. macroスタック
    • Lambda (マクロ)
    • Lambdaのアクセス許可 (CloudFormationが利用するため)
    • IAMロール (Lambda用)

実行環境の準備

AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。

AWS リソース構築手順

  1. 次を実行してスタックを作成

    ./create_stacks.sh
    
  2. 次を実行してスタックを削除

    ./delete_stacks.sh
    

構築テンプレート

1. macroスタック

macro.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Macro For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.

# Mappings

# Conditions

# Transform

Resources:
  # ロール作成
  akaneRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Description: !Sub
        - ${SystemName}-${EnvType}-role-macro
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Path: /
      RoleName: !Sub
        - ${SystemName}-${EnvType}-role-macro
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:*
                Resource: arn:aws:logs:*:*:*
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-role-macro
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # Lambda作成
  akaneLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          import traceback
          def handler(event, context):
              response = {
                  "requestId": event["requestId"],
                  "status": "success"
              }
              try:
                  operation = event["params"]["Operation"]
                  input = event["params"]["InputString"]
                  no_param_string_funcs = ["Upper", "Lower", "Capitalize", "Title", "SwapCase"]
                  if operation in no_param_string_funcs:
                      response["fragment"] = getattr(input, operation.lower())()
                  elif operation == "Strip":
                      chars = None
                      if "Chars" in event["params"]:
                          chars = event["params"]["Chars"]
                      response["fragment"] = input.strip(chars)
                  elif operation == "Replace":
                      old = event["params"]["Old"]
                      new = event["params"]["New"]
                      response["fragment"] = input.replace(old, new)
                  elif operation == "MaxLength":
                      length = int(event["params"]["Length"])
                      if len(input) <= length:
                          response["fragment"] = input
                      elif "StripFrom" in event["params"]:
                          if event["params"]["StripFrom"] == "Left":
                              response["fragment"] = input[len(input)-length:]
                          elif event["params"]["StripFrom"] != "Right":
                              response["status"] = "failure"
                      else:
                          response["fragment"] = input[:length]
                  else:
                      response["status"] = "failure"
              except Exception as e:
                  traceback.print_exc()
                  response["status"] = "failure"
                  response["errorMessage"] = str(e)
              return response
      FunctionName: !Sub
        - ${SystemName}-${EnvType}-macro-lambda
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Handler: index.handler
      Runtime: python3.13
      Role: !GetAtt akaneRole.Arn
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-macro-lambda
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # LambdaPermission作成
  akaneLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref akaneLambdaFunction
      Principal: cloudformation.amazonaws.com
  # Macro作成
  akaneCloudFormationMacro:
    Type: AWS::CloudFormation::Macro
    Properties:
      Name: convertString
      Description: Provides various string processing functions
      FunctionName: !Ref akaneLambdaFunction

# Outputs
all-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "all"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

2. 実行ファイル

create_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane
ENV_TYPE=all

create_stack () {
    STACK_NAME=$1
    aws cloudformation create-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} \
    --template-body file://./${SYSTEM_NAME}/${STACK_NAME}/${STACK_NAME}.yml \
    --cli-input-json file://./${SYSTEM_NAME}/${STACK_NAME}/${ENV_TYPE}-parameters.json

    aws cloudformation wait stack-create-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

create_stack macro
delete_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane
ENV_TYPE=all

delete_stack () {
    STACK_NAME=$1
    aws cloudformation delete-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}

    aws cloudformation wait stack-delete-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

delete_stack macro

マクロの利用方法

  1. CloudFormationのテンプレート内で、組み込み関数Transformを使って参照する

    テンプレート.yml
    Fn::Transform:
      - Name: convertString
        Parameters:
          InputString: 文字列
          Operation: Upper (パラメータ)
    
  2. 利用できるパタメータ

  • Upper
  • Lower
  • Capitalize
  • Title
  • SwapCase

実務で使うときの確認ポイント

  • AWSアカウント、リージョン、IAM権限、料金影響を確認してから実行する。
  • CloudFormationやTerraformを使う場合は、変更差分を確認してから反映する。
  • 検証環境で動作確認し、不要なリソースは削除する。
  • サービス仕様、上限、推奨設定は変わるため、公式ドキュメントで現在の内容を確認する。

おわりに

ここまで読んでいただきありがとうございました。
Wealthy Designでは、AWS・Webシステム開発・AI活用など、現場で使える技術を大切にしています。

会社の取り組みは、会社サイトにまとめています。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?