CloudFormationでSecrets ManagerのプレーンテキストをJSON形式で設定するのにハマった話

プレーンテキストをJSONで設定する際に公式ドキュメントを見てもよくわからなかったので備忘録です。
2023.01.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!AWS事業本部のおつまみです。

先日こちらのブログでTransfer Familyの接続ユーザーをSecretsManagerで管理する方法をお伝えしました。

このブログでは「3. SecretsManagerでシークレットを作成」を手動で行っていました。
ただ、IaC化した方がリソースの管理がしやすいです。
いざやろうと息込んだはいいものの、以下の点にハマってしまいました。

  • JSON内で他のスタックからImportValueした文字列をSub関数で結合する必要がある。
  • シークレットの値を[キー/値]ではなく、プレーンテキストにJSON形式で設定する必要がある。

そのため今回は私が1時間かけて作成したテンプレートを3分でご紹介します。

いきなり結論

このようなプレーンテキストをSecretManagerで保存します。

{
  "Password": "xxxxxxx",
  "PublicKey": "ssh-rsa xxxxxxx",
  "Role": "arn:aws:iam::xxxxxxxxxxx:role/test-transferfamily-role",
  "HomeDirectory": "/fs-xxxxxxxxxxxxx",
  "SFTPAcceptedIPNetwork": "0.0.0.0",
  "PosixProfile": {
    "Uid": "0",
    "Gid": "0"
  }
}

この場合、CloudFormationテンプレートは下記のようになります。
(ハイライトをかけてる箇所がハマったところです。)

SecretsManager.yml

AWSTemplateFormatVersion: '2010-09-09'
Description: create SecretsManager
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:

  Password:
    Type: String
    Default: Password

  PublicKey:
    Type: String
    Default: ssh-rsa xxxxxxx

  SFTPAcceptedIPNetwork:
    Type: String
    Default: 0.0.0.0

Resources:
  SecretManager:
    Type: "AWS::SecretsManager::Secret"
    Properties:
      Name: !Sub
        - ${TransferServerId}/test-transferfamily-user
        - TransferServerId: {'Fn::ImportValue': TransferServerId}
      Description: SFTP Connection for kisyo-server
      SecretString:
        !Sub
        - |-
          {
            "Password": "${Password}",
            "PublicKey": "${PublicKey}",
            "Role": "${Role}",
            "HomeDirectory": "/${FileSystemResource}",
            "SFTPAcceptedIPNetwork": "${SFTPAcceptedIPNetwork}",
            "PosixProfile": {
              "Uid": "0",
              "Gid": "0"
            }
          }
        - {Role: !ImportValue Role}
	        {FileSystemResource: !ImportValue FileSystemResource}     
      Tags:
        - Key: "Name"
          Value: !Sub ${PJPrefix}-kisyo-server

TransferServerId,Role,HomeDirectoryは別スタックでOutputしています。

ハマったポイント

このテンプレートを作る際、ハマったポイントをお伝えします。

他のスタックからImportValueした文字列をSub関数で結合する方法

SecretManagerのプレーンテキストは公式ドキュメントの「AWS::SecretsManager::Secret」を見ると、SecretStringにStringで記述する必要があります。

元のプレーンテキストはこのように記載する必要があり、この中でRoleHomeDirectoryは他のスタックでOutputしています。

{
  "Password": "xxxxxxx",
  "PublicKey": "ssh-rsa xxxxxxx",
  "Role": "arn:aws:iam::xxxxxxxxxxx:role/test-transferfamily-role",
  "HomeDirectory": "/fs-xxxxxxxxxxxxx",
  "SFTPAcceptedIPNetwork": "0.0.0.0",
  "PosixProfile": {
    "Uid": "0",
    "Gid": "0"
  }
}

そのため、ImportValueした文字列をSub関数で結合する必要があります。

公式ドキュメントの「組み込み関数リファレンス » Fn::Sub」を見ると、実行時に取得する値(以下の Var1Value、Var2Value )を Sub 関数のパラメータに含むことができると書かれています。

短縮形の構文:

!Sub
  - String
  - { Var1Name: Var1Value, Var2Name: Var2Value }

ここでStringは、"${VarName}"のように実行時に取得する値と置き換えることができます。
よって、ImportValueした文字列をSub関数で結合する場合、下記のように記述する必要があります。

!Sub
  - "${Role}",
  - {Role: !ImportValue Role}

YAML記述のCloudFormation内でJSONを埋め込む方法

次にSecretManagerのプレーンテキストはJSON形式で記述しなければいけません。
そのため、YAML記述のCloudFormation内にどう置換すればいいのか悩みました。

ググったところ、こちらのブログに記述方法が載ってました。(ありがたや)

よってSecretManagerのプレーンテキストはこのように記述することができます。

      SecretString:
        !Sub
        - |-
          {
            "Password": "${Password}",
            "PublicKey": "${PublicKey}",
            "Role": "${Role}",
            "HomeDirectory": "/${EFSID}",
            "SFTPAcceptedIPNetwork": "${SFTPAcceptedIPNetwork}",
            "PosixProfile": {
              "Uid": "0",
              "Gid": "0"
            }
          }
        - {Role: !ImportValue Role}
	        {EFSID: !ImportValue FileSystemResource}

こちらCloudFormationで実行してみます。
想定通りJSON形式で保存できました!

最後に

今回はCloudFormationでSecrets ManagerのプレーンテキストをJSON形式で設定するのにハマった話をお伝えしました。

CloudFormationの組み込み関数は奥深いなと思いました。
だんだん慣れていけるようになりたいです。

最後までお読みいただきありがとうございました!
どなたかのお役に立てれば幸いです。

以上、おつまみ(@AWS11077)でした!