CloudFormationのパラメータでマルチバイト文字を入力する

2016.10.05

ウィスキー、シガー、パイプをこよなく愛する大栗です。

CloudFormationで環境を構築することが多いのですが、EC2にミドルウェアを設定する時に環境固有の情報をパラメータから入力します。しかし日本語のようなマルチバイト文字を入力すると全て『?』と表示されてしまうので、環境を構築した後に個別に設定しており面倒でした。
最近CFnで日本語を入力する方法がわかったので紹介します。

ASCII文字に変換して入力する

結論として、マルチバイト文字を入力する方法はありませんでした。EC2のUserDataやMetadataへ流し込む時にマルチバイトが文字化けしてしまいます。

その代わり一旦ASCII文字に変換すれば、ASCII文字を入力することになるので使用できることがわかりました。CloudFormationで使用できる組み込み関数の中でASCII文字に変換できるのはFn::Base64です。Fn::Base64を使用してBase64エンコーディングしてASCII文字として入力します。

具体的な記述方法

ダメな例

以下のようなCloudFormationテンプレートでEC2を1台起動して、入力したパラメータをファイルに出力しています。
CloudFormationテンプレートはYAML形式で記述しています。

AWSTemplateFormatVersion : 2010-09-09
Description : Linux Stack
Parameters :
  TempVar1 :
    Type : String
    Default : PARAMETER
  TempVar2 :
    Type : String
    Default : PARAMETER
  KeyPairName :
    Type : String
    Default : KeyPairName
Resources :
  Server01 :
    Type : AWS::EC2::Instance
    Properties :
      ImageId : ami-374db956
      InstanceType : t2.small
      KeyName : !Ref KeyPairName
      UserData :
        Fn::Base64 : !Sub |
            #!/bin/bash
            /opt/aws/bin/cfn-init -s ${AWS::StackId} -r Server01 -c sample --region ${AWS::Region}
    Metadata :
      AWS::CloudFormation::Init :
        configSets :
          sample :
            - execute
        execute :
          files :
            /tmp/cfn-output.sh :
              content : !Sub |
                VAR1=${TempVar1}
                VAR2=${TempVar2}
                echo $VAR1 >  /tmp/var1.txt
                echo $VAR2 >> /tmp/var1.txt
              mode : "000755"
          commands :
            a-write-output-file :
              command : /tmp/cfn-output.sh

以下のように入力します。

項目名 入力値
KeyPairName (各環境のキーペア)
TempVar1 クラスメソッド株式会社
TempVar2 大栗

起動したインスタンスの中身を確認します。
作成したスクリプトは以下のように文字化けしています。

$ cat /tmp/cfn-output.sh
VAR1=???????????
VAR2=??
echo $VAR1 >  /tmp/var1.txt
echo $VAR2 >> /tmp/var1.txt

実行結果も当然文字化けしています。

$ cat /tmp/var1.txt
???????????
??

修正した例

スクリプト部分を以下のように書き換えます。
!Subの第一引数にスクリプトの内容を記述します。ここで新しい変数である${MyVar1}${MyVar2}を使用しています。base64 -dでBase64デコードした結果をVAR1VAR2に格納しています。
!Subの第二引数では、第一引数で使用した引数を定義します。MyVar1TempVar1をBase64エンコードした内容、MyVar2TempVar1をBase64エンコードした内容として定義します。

/tmp/cfn-output.sh :
  content : !Sub 
    - |
      VAR1=$(echo ${MyVar1} | base64 -d)
      VAR2=$(echo ${MyVar2} | base64 -d)
      echo $VAR1 >  /tmp/var1.txt
      echo $VAR2 >> /tmp/var1.txt
    - MyVar1: {"Fn::Base64": !Ref TempVar1}
      MyVar2: {"Fn::Base64": !Ref TempVar2}

このテンプレートを実行すると、スクリプトは以下になります。

$ cat /tmp/cfn-output.sh
VAR1=$(echo 44Kv44Op44K544Oh44K944OD44OJ5qCq5byP5Lya56S+ | base64 -d)
VAR2=$(echo 5aSn5qCX | base64 -d)
echo $VAR1 >  /tmp/var1.txt
echo $VAR2 >> /tmp/var1.txt

出力結果では、日本語が表示できています。

$ cat /tmp/var1.txt
クラスメソッド株式会社
大栗

さいごに

いかがでしょうか。Fn::Subを使ってスクリプト部分とCloudFormation側の変数定義が分離できるので、可読性が高いスッキリした記述となります。
CloudFormationのYAML対応を機にCloudFormationでインフラ環境のコード化を使用している方もいるようなので、参考になれば幸いです。