【AWS AppConfig】機能フラグで定義した属性が GetLatestConfiguration で取得できなかった場合に確認すること

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

はじめに

テントの中から失礼します、IoT 事業部のてんとタカハシです!

AppConfig の機能フラグを触り始めて間もない頃に、定義した属性が GetLatestConfiguration で取得できない!なんで?という経験を3つほどしたので、その際に確認した点をご紹介しようと思います。

本記事では機能フラグをマネジメントコンソール上で作成する例と併せて AWS CLI で作成する際の例も記載します。AWS CLI で機能フラグを作成する方法は下記の記事で触れていますので、併せてご参照いただければと思います。

環境

今回、AWS CLI で機能フラグ作成・取得するにあたり使用した環境は下記の通りです。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.7
BuildVersion:	19H1824

$ aws --version
aws-cli/2.7.9 Python/3.9.11 Darwin/19.6.0 exe/x86_64 prompt/off

$ jq --version
jq-1.6

前提

  • AppConfig にアプリケーション「SampleApplication」が作成済みであること
  • 上記のアプリケーションに機能フラグ設定プロファイル「SampleProfile」が作成済みであること
  • 上記のアプリケーションに環境「dev」が作成済みであること
  • 機能フラグを環境にデプロイした直後に再デプロイが可能なデプロイ戦略が作成済みであること

最後の項目で触れているデプロイ戦略の作成方法については下記の記事をご参照ください。本記事では検証のためにデプロイを何度か繰り返しますので、デプロイ後の待ち時間が不要なデプロイ戦略を活用します。

取得用のスクリプト

機能フラグを GetLatestConfiguration で取得するためのスクリプトを用意します。

#!/bin/bash

TOKEN=$(aws appconfigdata start-configuration-session \
  --application-identifier SampleApplication \
  --configuration-profile-identifier SampleProfile \
  --environment-identifier dev \
  --query InitialConfigurationToken \
  --output text \
)

aws appconfigdata get-latest-configuration \
    --configuration-token "${TOKEN}" \
    --query NextPollConfigurationToken \
    --output text \
    result.txt \
    > /dev/null

cat result.txt | jq

上記のスクリプトを実行すると、下記のような実行結果が出力されます。

{
  "sampleFlag": {
    "enabled": true,
    "sampleBool": true,
    "sampleNumber": 5555,
    "sampleString": "fuga"
  }
}

確認事項

作成した機能フラグのバージョンが環境にデプロイされているか

まずは初歩的なところから。機能フラグで属性を定義してバージョンを作成した後、環境の方に反映するためにはデプロイを行う必要があります。うっかりバージョンだけ作成して満足しちゃったなんてことにならないように、ちゃんとデプロイまで行う必要があることを忘れないようにしましょう。

環境の詳細画面で、どのバージョンが環境に反映されているのかを確認することができます。下記の例ではバージョン「3」が環境に反映されている状態です。

機能フラグが有効になっているか

機能フラグは有効/無効を切り替えることができます。デフォルトでは無効になっているため注意が必要です。

機能フラグが無効になっている場合は、GetLatestConfiguration で属性が取得できません。下記に記載の通り、"enabled": false のみが返ってくるだけになります。

{
  "sampleFlag": {
    "enabled": false
  }
}

マネジメントコンソール上では、少し分かりづらい位置に有効/無効のスイッチが配置されていますので、見逃さないようにしましょう。ちなみに有効/無効を切り替えるだけでバージョンが上がりますので、デプロイが必要なことも忘れずに。

下記は AWS CLI で機能フラグを作成する場合の例です。「values」の中にある機能フラグに対して enabled: true と記載することで有効化できます。enabled を false にしたり、Key 自体を記載しない場合は、デフォルトで無効になります。

{
  "version": "1",
  "flags": {
    "sampleFlag": {
      "name": "SampleFlag",
      "attributes": {
        "sampleString": {
          "constraints": {
            "type": "string"
          }
        }
      }
    }
  },
  "values": {
    "sampleFlag": {
      "enabled": true, // *** 有効化
      "sampleString": "hoge"
    }
  }
}

属性の名前や型の定義だけ行い、値が未登録ではないか

機能フラグの属性は、名前や型だけを定義して、値は登録しない(空文字ではない)ということが可能です。値が登録されていない属性については GetLatestConfiguration で取得できないため注意が必要です。

AWS CLI での作成例を基に挙動を再現する

下記の記事にも記載していますが、AWS CLI で機能フラグを作成する際の JSON スキーマでは、属性の定義(名前や型)を「flags」に、値の登録は「values」に記載します。このうち、値の登録は必須ではないため、何も記載しなければ値が未登録の状態になります。

例として下記の JSON を作成してみました。属性として「sampleString」と「sampleNumber」を定義していますが、値の方は「sampleString」に空文字を入れているだけで、「sampleNumber」の方は Key を記載せず未登録の状態にしています。

{
  "version": "1",
  "flags": {
    "sampleFlag": {
      "name": "SampleFlag",
      "attributes": {
        "sampleString": {
          "constraints": {
            "type": "string"
          }
        },
        "sampleNumber": {
          "constraints": {
            "type": "number"
          }
        }
      }
    }
  },
  "values": {
    "sampleFlag": {
      "enabled": true,
      "sampleString": "" // *** 空文字
      // *** sampleNumber の値は未登録
    }
  }
}

上記の JSON で機能フラグを作成すると、マネジメントコンソール上では下記の通りに表示されます。値の欄を確認すると「sampleString」は空文字なので空欄、「sampleNumber」は値が未登録ということで-が表示されています。マネジメントコンソール上では空文字と未登録の区別をつけれるように表示を変えてくれているようです。

このバージョンを環境にデプロイした後、GetLatestConfiguration で属性を取得すると下記の通りになります。「sampleString」の値だけが取得できて、「sampleNumber」についてはまるで属性が存在しないかのような結果になります。

{
  "sampleFlag": {
    "enabled": true,
    "sampleString": ""
  }
}

なぜこのような挙動になるのか?については、下記のドキュメントに記載がある GetLatestConfiguration で取得される JSON のスキーマが参考になりそうです。

GetLatestConfiguration の JSON スキーマ(クリックすると確認できます)
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "patternProperties": {
    "^[a-z][a-zA-Z\\d-_]{0,63}$": {
      "$ref": "#/definitions/attributeValuesMap"
    }
  },
  "maxProperties": 100,
  "additionalProperties": false,
  "definitions": {
    "attributeValuesMap": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        }
      },
      "required": ["enabled"],
      "patternProperties": {
        "^[a-z][a-zA-Z\\d-_]{0,63}$": {
          "$ref": "#/definitions/attributeValue"
        }
      },
      "maxProperties": 25,
      "additionalProperties": false
    },
    "attributeValue": {
      "oneOf": [
        { "type": "string","maxLength": 1024 },
        { "type": "number" },
        { "type": "boolean" },
        {
          "type": "array",
          "oneOf": [
            {
              "items": {
                "oneOf": [
                  {
                    "type": "string",
                    "maxLength": 1024
                  }
                ]
              }
            },
            {
              "items": {
                "oneOf": [
                  {
                    "type": "number"
                  }
                ]
              }
            }
          ]
        }
      ],
      "additionalProperties": false
    }
  }
}

これを解読すると、機能フラグを作成する際に指定した JSON の「values」内を返しているように見えます。となると、値が未登録の属性が存在しないかのような結果になることに合点がいきます。

ということで、特別な理由が無い限りは values 内で値を登録してあげた方が安全な気がします。属性を定義する際は、こうした挙動になり得るということを理解しておくと良いでしょう。

マネジメントコンソール上で作成した場合も同じ挙動を再現可能

ここまで AWS CLI での作成例を見てきましたが、同じ挙動をマネジメントコンソール上でも再現することが可能です。ただし、あくまでも本記事を投稿した時点(2022/06/26)では可能ということで、今後 UI に変更があった場合などに挙動が変わる可能性があるのでご注意ください。

再現は簡単で、新規の属性を作成する際に値を入力しない(入力してから削除は不可)で「フラグを作成」をクリックするだけです。

すると、先ほど説明した通り、値の欄が-で表示されます(これは値が未登録であることを示しています)。

ここで、逆に空文字はどうやって登録するのか?という疑問が湧くのですが、これも一応対応可能です。新規の属性を作成する際に、一旦テキトーな値を入力してから、

入力した値を全て削除します。その後「フラグを作成」をクリックします。

すると、値の欄が空白になります(これは値が空文字であることを示しています)。

この状態で、機能フラグのバージョンを作成する際に送信される HTTP リクエストの内容を確認してみましょう。ここでは Chrome のデベロッパーツールで確認を行います。「新しいバージョンを保存」をクリックした後、HTTP リクエストのペイロードに含まれる「contentString」を見てみます。

中身を展開すると下記の通りでした。期待通り「sampleString」は空文字で「sampleNumber」の値は未登録となっていました。再現作業は以上になります。

{
  "version": "1",
  "flags": {
    "sampleFlag": {
      "name": "SampleFlag",
      "description": "",
      "attributes": {
        "sampleNumber": {
          "constraints": {
            "type": "number"
          }
        },
        "sampleString": {
          "constraints": {
            "type": "string"
          }
        }
      }
    }
  },
  "values": {
    "sampleFlag": {
      "enabled": true,
      "sampleString": ""
    }
  }
}

おわりに

機能フラグで定義した属性が GetLatestConfiguration で取得できなかった場合、取得側の実装で属性が存在することを前提としてしまっていると、エラーを引き起こす可能性があります。AppConfig の機能フラグでは属性を定義しても取得することができないケースがあるということで、実装側は属性が取得できなくても正常に動作するように考慮しておくと安全だと考えます。

今回は以上になります。最後まで読んで頂きありがとうございました!