☁️

CloudFormationテンプレート内でのマルチバイト文字についての検証をしてみました。

2022/10/17に公開

はじめに

最初にお伝えしなければいけないのが、いつもなら最初に確認してからスタートしているはずの「検証の前提」がそもそもな事に全文を書き終えてから気付いてしまいました。

(触っている方であれば次の章を読んだ瞬間に「あれ?」です。)

即刻ボツにしようかと思いましたが、参考にした記事など素晴らしい情報も多かったので工程の中で何か拾える部分があればと思い公開にしています。

取り組んでいる内容自体は無意味な努力ですが、間々を摘んで読んでいただけたらと思います。

※ここからは失敗に気付く前の文章になります。

概要

少し前になりますが、「Cloudformationテンプレートはマルチバイト文字は未対応。使えるといいのにね」というような話題を目にしました。

やりとりを眺めていた私も最近テンプレートの作成に着手し始めまして、

”Cfnテンプレート内では何が出来て何が出来ないか?”が関心事です。

今回は「"マルチバイト文字(≒日本語)"を用意した入力欄で受け取って、インスタンス起動時に「半角英数字以外」の名前(Tag)をつける」に挑戦したいと思います!(←ココ)

今回の構成について

構成は出来るだけシンプルにしましたが、作業者のグローバルIP(/32)からのHTTPのみ開けています。

(セッションマネージャーを使って接続も出来るようにしています。)

・シングルAZ
・パブリックサブネット1つのみ
・EC2は一台
・セッションマネージャーで接続出来るように
 - EC2にはパブリックIPを付与
 - EC2のIAMロールにはSSMManagedInstanceCoreを追加
・EC2起動時実行されるコマンドの権限に必要な為EC2FullAccessもIAMロールに追加(テストなのでフルでつけています)
・スタック作成時に入力を求められるMyIP(作成者のグローバルIP)をセキュリティグループに利用

とします。

尚、

パラメーターについては
https://zenn.dev/tmasuyama1114/articles/aws-cloudformation-basics-parameters

テンプレート最上部にあるMetadata:の意図についてはこちら
https://zenn.dev/ano/articles/c5eedcc31b30e2

セッションマネージャーについては良い記事が沢山存在していますので「パブリックサブネット セッションマネージャー」で検索していただければ幸いです。


参考にさせていただいた元の記事がとても分かりやすいのでまずこちらにお目通しお願いします。
https://dev.classmethod.jp/articles/cfn-multibyte-character-input/

ということで、実際に作業に入りたいと思います。

CloudFormationテンプレート(Yaml)

既に内容やサービスを理解している方にはお目汚しですが、
「●」で始まる行はAWSサービス、CloudFormationに馴染みの無い方の理解になればと思いかなりふわっとしたコメントを添えてあります。

以下をコピーしてご自身の端末上でファイルを作成ください。

※linuxコマンドも勉強中ですので理解の間違い等是非ご指摘お願いします。

multibyte_test.yml

# ●※この日付は作成日ではなく次に新しいバージョンが出るまでは毎回これで記載するもの。
AWSTemplateFormatVersion : 2010-09-09
Description : Linux Stack
# ============== メタデータ ==============
# ●ここは作成者がテンプレートをアップした次の画面で入力してもらうパラメータ項目の表示順番を指定しています。
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - 
        Label:
          default: Parameters
        Parameters:
          - MyIP
          - TempVar1
# ============== パラメータ ==============
# ●作成者にキーボードで入力・或いは選択して欲しいという項目を作成しています。(ここでは2個)
Parameters: 
# ●「あなたのグローバルIPを書いてね」という質問を用意。(セキュリティグループの”マイIP”機能を使うと便利です。)
  MyIP:
    Description:  Enter your global IP(xxx.xxx.xxx.xxx/32).This is used for SecurityGroupIngress of EC2.
    Type: String
# ●「好きなマルチバイト文字を入力してね」という欄を用意。
  TempVar1 :
    Description: Enter multibyte characters for instance name tag value.
    Type : String
# ============== リソース ==============
Resources:
# -------------- IAM --------------
# ●インスタンスに許されたアクションを定義します。
# ロール
  MyTestRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'ec2.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Path: '/'
      RoleName: MyTestRole
      ManagedPolicyArns:
# ●セッションマネージャーを利用しての接続に必要な権限
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
# ●後で登場するuserdataの一部コマンドを実行する為の権限(テストなのでフルでつけています。)
        - arn:aws:iam::aws:policy/AmazonEC2FullAccess
# -------------- インスタンスプロファイル --------------
# ●上のIAMロールとインスタンスの接着点のようなもの。
  MyInstanceProfile:
    DependsOn: MyTestRole
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: '/'
      Roles:
        - !Ref MyTestRole
# -------------- VPC --------------
# ●一番大きな箱。
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: MyVPC
#-------------- インターネットゲートウェイ --------------
# ●VPC内のリソースがインターネットとアクセス出来るようにVPCに出入り口を作ります。
#  IGW
  MyIGW:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags:
        - Key: Name
          Value: MyIGW
# ●それをどのVPCにつけるかを指定。
#  アタッチメント
  MyIGWAttach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref MyIGW
      VpcId: !Ref MyVPC
# -------------- サブネット --------------
# ●VPCの中にもう一つ小さな箱を用意。この中にEC2インスタンスを入れる。今回はインターネットに向けて開かれている設定。
# パブリック
  MyPubSN1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref MyVPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: MyPubSN1
# -------------- ルートテーブル --------------
# ●「どこに向かってどう走ればいいの?」を指示したテーブル。
#  ルートテーブル
  MyPubRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: MyPubRT
# ●その内容。
#  ルート
  MyPubRoute:
    Type: AWS::EC2::Route
    DependsOn: MyIGW
    Properties:
      RouteTableId: !Ref MyPubRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyIGW
# ●どのサブネットに紐づけるか
#  アソシエーション
  MyPubSN1Assoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref MyPubSN1
      RouteTableId: !Ref MyPubRT
# -------------- セキュリティグループ --------------
# ●「どのIPに対してどのポートを開けるの?」を指定。
  MyEC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref MyVPC
      GroupDescription: Allow HTTP access only MyIP
      SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            CidrIp: !Ref MyIP
      Tags:
        - Key: Name
          Value: MyEC2SG
# -------------- EC2インスタンス --------------
# ●今回の主役。これをサブネットの中に配置。
  MyEC2:
    Type : AWS::EC2::Instance
    Properties :
# ●ここ から
      ImageId : ami-078296f82eb463377
      InstanceType : t2.micro
      IamInstanceProfile: !Ref MyInstanceProfile
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          SubnetId: !Ref MyPubSN1
          GroupSet:
            - !Ref MyEC2SG
# ●ここ までは「どんな設計図、どんなサイズ、どのルール、どこに配置、どの役割、どの通信ポート」などを指定。

# ●この部分は起動時にインスタンスで実行されるコマンド
# ●下のMetadataで作成したcommandを実行して、
# ●wgetで取得したEC2の物理IDを指定して「入力したマルチバイトの文字を使ってこのEC2に名前タグ作成してよ」と命令している意図です。
      UserData :
        Fn::Base64 : !Sub |
            #!/bin/bash
            sudo /opt/aws/bin/cfn-init -s ${AWS::StackId} -r MyEC2 -c sample --region ${AWS::Region}
            cat /tmp/cfn-output.sh
            /usr/bin/aws ec2 create-tags --resources `wget -q -O - http://169.254.169.254/latest/meta-data/instance-id` --tags Key=Name,Value=`cat /tmp/var1.txt` --region ${AWS::Region}
# ●この部分はパラメーターで受け取ったマルチバイト文字(=TempVar1という変数に格納)をASCIIに変換→「日本語に戻したものをvar1.txtというファイルに書き出してね」という命令をしています(私の理解)
    Metadata :
      AWS::CloudFormation::Init :
        configSets :
          sample :
            - execute
        execute :
          files :
            /tmp/cfn-output.sh :
              content : !Sub 
                - |
                  VAR1=$(echo ${MyVar1} | base64 -d)
                  echo $VAR1 >  /tmp/var1.txt
                - MyVar1: {"Fn::Base64": !Ref TempVar1}
              mode : "000755"
          commands :
            a-write-output-file :
              command : /tmp/cfn-output.sh

ここからはマネージドコンソールでの作業

ファイルの作成が済んだらマネージドコンソールにログイン→[サービス]→”CloudFormation”を選択。

①”スタックの作成”ボタンを押下。

②作成したテンプレートを選択して”次へ”を押下します。

②好きなスタック名(上段/英数字ならなんでもいいです。)を入力。
MyIP(下段ひとつめ/ご自身のグローバルIPを入力してください。)と
TempVar1(下段ふたつめ/マルチバイト文字を含んだ文字列を入力してください。)

私は漢字ひらがなカタカナAlphabetを入れてみました。
入力が完了したら"次へ"を押下します。

その先も、「何も変更・入力せず」最下部にスクロール→"次へ"。→もう一度最下部までスクロールして、最後のこのチェックボックスにチェックを入れて"スタックの作成"をクリックします。

このような画面になります。

[リソース]タブ→暫く待っていると"MyEC2"の作成が始まるので、右隣の"i"から始まる物理IDクリック。

EC2を管理する画面に遷移します。
立ち上がりまで少し時間がかかりますが、暫く眺めていると・・・

入力した日本語名で作成されました!

ちなみに。。(ここで間違いに気づきます。)

# -------------- EC2インスタンス --------------
  MyEC2:
    Type : AWS::EC2::Instance
    Properties :
      Tags:
        - Key: Name
          Value: 名前を日本語で直接入力

と、直接日本語をTagsのValueに持ってきたらどうなるかというと、
こうなります。

■━⊂( ・∀・) 彡 ガッ☆`Д´)ノ

終わりに。

大変失礼いたしました。

「Cfn自体のDescription項目が日本語対応していない」みたいなのはテンプレートがというよりはその項目自体の決まりなので関係ないとして
(※例えばセキュリティグループの”GroupDescription”に日本語を入力してみると、「Valid characters for VPC security groups only include a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*.」と怒られるように)

今わかっている時点では「EC2のUserDataやMetadataへ流し込む時」にマルチバイトが文字化けという事でした。

「本来だと日本語がいけるのにいけない」という限定した困り事を話題にしていたという理解が必要でしたね。

マルチバイト文字については再度さまざま試して理解を深めたいと思いますが次はこのような事のなきように!というのが一番の学びでした。。

お読みいただきありがとうございました!

Discussion