この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
いわさです。
先日CloudFormationで別のリソースから取得した文字列値を使ってVPCエンドポイントを作成しようとしたところ、エラーになりました。
The Vpc Endpoint Service 'com.amazonaws.ap-northeast-1.managedblockchain.n-clxlsessenfatn2ty2tiehnf6m' does not exist
どうやら大文字と小文字の違いでサービスが見つからないことで失敗しているようです。
CloudFormationで大文字を小文字に変換出来ないものかと探したところTransformを使えば出来るようなので本日は試してみました。
Fn::Transform
Fn::Transformについては以下の記事で文字列のUPPER/LOWER含めて少し触れられています。
Transformで利用するにあたって関数の作成が必要なのですが、awslabsのサンプルリポジトリで紹介されておりそのまま利用出来ます。
いつも思うのですが、なんでもありますね、このリポジトリ群。
以下のStackを自分のAWSアカウントで用意しておくと好きな時に今後使えるので便利ですね。
AWSTemplateFormatVersion: 2010-09-09
Resources:
TransformExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ['logs:*']
Resource: 'arn:aws:logs:*:*:*'
TransformFunction:
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
Handler: index.handler
Runtime: python3.6
Role: !GetAtt TransformExecutionRole.Arn
TransformFunctionPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt TransformFunction.Arn
Principal: 'cloudformation.amazonaws.com'
Transform:
Type: AWS::CloudFormation::Macro
Properties:
Name: 'String'
Description: Provides various string processing functions
FunctionName: !GetAtt TransformFunction.Arn
利用側は以下のようになります。
...
Outputs:
NetworkId:
Value: !GetAtt NetworkAndInitialMember.NetworkId
NetworkIdLower:
Value:
Fn::Transform:
- Name: String
Parameters:
InputString: n-CLXLSES5ENFATN2TY2TIEHNF6M
...
注意
注意点というかTransformの仕様なのですが、リソースの動的なプロパティを渡すことは出来ません。
Failed to digest functions within transform parameters, intrinsic functions in transform block must only contain parameter values or stack metadata.
...
Outputs:
NetworkId:
Value: !GetAtt NetworkAndInitialMember.NetworkId
NetworkIdLower:
Value:
Fn::Transform:
- Name: String
Parameters:
InputString: !GetAtt NetworkAndInitialMember.NetworkId
...
なので、本来やりたかった上記のような使い方は出来ません。
結局カスタムリソースで対応することにしました。
ただし、スタックパラメータであれば利用できるので、ネストさせれば利用できそうです。
さいごに
本日はTransformを使って、文字列の大文字/小文字を変換してみました。
小文字変換くらいであれば組み込みで出来るだろうと思っていたらLambda関数を用意しなければならないなど思ったより準備が必要でした。
しかし、AWSのレポジトリで変換関数サンプルが提供されているのはありがたいですね。
今回の仕組みは他にも色々と応用が効きそうなので、カスタムリソースと併せて何でも出来そうですね。複雑にならないようには努めたいものですが。