0
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-09

はじめに

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

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

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

今回は、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.7
      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
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