はじめに
AWS CloudFormationを利用して、CloudFormationマクロを構築するテンプレートのサンプルです。
テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。
コードはGitHubにもあります。
今回は、akane というシステムの all 環境を想定しています。
同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。
ディレクトリ構成
akane (システム)
└─ macro (スタック)
├─ macro.yml (CFnテンプレート)
└─ all-parameters.json (all 環境のパラメータ)
マクロとは
- マクロを使用すると、検索して置換操作のような単純なアクションからテンプレート全体の広範な変換まで、テンプレートに対してカスタム処理を実行できるようになります。
AWS リソース構築内容
- macroスタック
- Lambda (マクロ)
- Lambdaのアクセス許可 (CloudFormationが利用するため)
- IAMロール (Lambda用)
実行環境の準備
AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。
AWS リソース構築手順
-
下記を実行してスタックを作成
./create_stacks.sh
-
下記を実行してスタックを削除
./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
マクロの利用方法
-
CloudFormationのテンプレート内で、組み込み関数
Transform
を利用して参照するテンプレート.ymlFn::Transform: - Name: convertString Parameters: InputString: 文字列 Operation: Upper (パラメータ)
-
利用できるパタメータ
- Upper
- Lower
- Capitalize
- Title
- SwapCase