TerraformによるFirebase Remote Configの管理
モバイルアプリに掲載されるバナー広告の画像やリンク先がFirebase Remote Configで管理されているのですが、それらをエンジニア以外のスタッフ(マーケティングチーム等)が編集したいという話がありました。
バナー広告 | Remote Configパラメータ |
---|---|
![]() |
![]() |
開発環境のFirebaseプロジェクトであればスタッフに適切な権限を付与して、コンソールから変更してもらうこともできますが、本番環境のFirebaseプロジェクトの場合はきちんとレビューを通したいところです。
そこでTerraform+AtlantisでRemote Configを管理することを思いつきました。
Atlantisは以前紹介しましたが、Pull Request上でTerraformのplan・applyを行うツールで、レビュー後にatlantis apply
とコメントすることで変更内容を適用することができます。Pull Request上のやり取りで作業が完了するので特別な権限やツールは不要です。
また、GitHubではブラウザからファイルを編集できるので、手元にgit cloneできなくてもPull Requestを作成することができます。
問題はFirebase Remote ConfigのTerraform Providerが存在しないことです。FirebaseのProviderはあるのですが、Remote Configは管理対象に含まれていません。
Firebase Remote Config APIは公開されておりGoのライブラリもあったので、Providerを自作してみることにしました。
Firebase Remote ConfigのAPI自体は単純で数も少なく実装の手間はそれほどかかりません。しかしgoogle-api-go-clientに含まれるfirebaseremoteconfig-api.jsonは8年前から更新されておらずParameterValueTypeが未実装の状態。
Issueによると https://www.googleapis.com/discovery/v1/apis にfirebaseremoteconfig
が含まれない状態が続いているとのこと。
The Go firebaseremoteconfig has not been generated from a new discovery doc in many years it seems. We only regenerate libraries for things that show up in the public listing of APIs, which this one no longer does it seems: https://www.googleapis.com/discovery/v1/apis.
Firebase Admin Go SDKにも機能追加の要望が上がっていましたが、こちらは2021年から更新がありません。
Firebase Admin Go SDKにAPI追加のPull Requestを投げるのが正攻法だと思います。しかしIssueの放置具合からマージに時間がかかりそうな気配を感じました。また、google-api-go-clientに至っては今後、Remote Configまわりが更新される可能性は非常に低いでしょう。
公式ライブラリをforkするのはあまり好ましくないのですが、今回は時間がなかったのでgoogle-api-go-clientをforkして、自前でParameterValueTypeを追加しました。
ということで作ったのがterraform-provider-firebaseremoteconfigです。
Remote Config全体を管理したいというより一部だけ編集したい要望だったので1パラメータをリソースとしました。
resource "firebaseremoteconfig_parameter" "foo" {
key = "foo"
value_type = "JSON"
default_value = {
value = jsonencode({
foo = "bar"
zoo = 100
})
}
}
変更が必要なパラメータが出てきたら都度都度インポートするようにしています。
Remote ConfigのterraformingによりPull Request上で変更のレビューが行えるようになり、安全にリリースできるようになったと思います。
余談
パラメータのJSON値にはテキストではなくTerraformのjsonencode関数を使ってhclで定義しているのですが、この関数には<
、>
、&
がUnicodeエスケープシーケンスに変換される仕様があります。
> jsonencode({ url = "https://example.com/?foo=bar&zoo=baz" })
"{\"url\":\"https://example.com/?foo=bar\\u0026zoo=baz\"}"
# ^^^^^^^
replace関数を使って置換すればいいのですが、冗長な記述になってしまうため専用のユーザー定義関数を作成しました。
> provider::multireplace::jsonunescape(jsonencode({ url = "https://example.com/?foo=bar&zoo=baz" }))
"{\"url\":\"https://example.com/?foo=bar&zoo=baz\"}"
# ^
余談2
jsonunescape関数は複数文字列置換用のmultireplace関数の特殊系として作ったのですが、multireplaceと同様のユーザー定義関数を提供するProviderはすでにありました。
こちらは文字列に関する便利関数をいくつも提供しており、なかなか便利そうです。
Discussion