はじめに
業務でモバイルアプリケーションのバックエンド側のAWS環境を構築するにあたって、スマートフォンに送られるプッシュ通知を実装するために、Amazon Pinpointを使用することにしました。Amazon Pinpointを使えば、モバイルアプリケーションへのあらゆる通知機能を一元管理できるのに加えて、開封率などの効果測定もできるため、分析やマーケティングにも役立つということで、ちょっと使ってみようかという話になりました。
私の会社では、AWSリソースをIaCとして構成管理するためにTerraformを使用しているのですが、Amazon Pinpointのリソースとプッシュ通知に用いる
- Apple Push Notification Service (APNs)
- Firebase Cloud Messaging (FCM)
の設定を実装する必要があるのですが、そこで色々と沼にハマり、関連するドキュメントもAWS公式のもの以外に見当たらなかったので、備忘録的に、最終的にどういう実装にしたのかを記録しておきます。
Amazon Pinpointのリソースの実装
これは教科書通りで、以下のように実装すれば問題ありません。
# ===============================================================================
# Pinpoint Configure
# ===============================================================================
resource "aws_pinpoint_app" "pinpoint" {
name = "test-pinpoint"
limits {
maximum_duration = 10800
messages_per_second = 20000
}
}
Apple Push Notification Service (APNs)向けのチャネルの実装
最初にはまったポイントがここ。Terraformの公式ドキュメントには、APNsチャネルの実装方式として2つの方式が示されているのですが、サンプルに書かれている“Certificate credentials”(Appleから発行される証明書と秘密鍵を用いる)方式では、何をどうしても何故か、実行時にAppleから発行された正式な証明書ではないというエラーが出てチャネルを作成することができません。秘密鍵の代わりに、パスワードをSSMのパラメータストアに定義してあげて値を渡してもダメ、ならばとパスワードを定数(variables)として定義してあげて値を渡してもやはりダメです。
なので、もうひとつの方式として挙げられている、“Key credentials”(Appleから発行されるバンドルID、チームID、トークンとして用いられる”.p8″ファイル、トークンのキーを用いる)方式で実装し直しました。このうち、バンドルID、チームID、トークンのキーはSSMパラメータストアに以下のように格納します。
# ===============================================================================
# SSM Parameters for iOS (Pinpoint)
# ===============================================================================
resource "aws_ssm_parameter" "apns_bundle_id" {
name = "/test/prod/apns-bundle-id"
description = "The parameter for apns team id with Pinpoint"
key_id = aws_kms_key.application.key_id
type = "SecureString"
value = "${bundle_id}"
lifecycle {
ignore_changes = [
value,
]
}
}
resource "aws_ssm_parameter" "apns_team_id" {
name = "/test/prod/apns-team-id"
description = "The parameter for apns team id with Pinpoint"
key_id = aws_kms_key.application.key_id
type = "SecureString"
value = "${team_id}"
lifecycle {
ignore_changes = [
value,
]
}
}
resource "aws_ssm_parameter" "apns_token_key_id" {
name = "/test/prod/apns-token-key-id"
description = "The parameter for apns token key id with Pinpoint"
key_id = aws_kms_key.application.key_id
type = "SecureString"
value = "${token_key_id}"
lifecycle {
ignore_changes = [
value,
]
}
}
その上で、Amazon PinpointのAPNsチャネル設定を以下のように実装します。
# ===============================================================================
# Pinpoint Settings for iOS
# ===============================================================================
resource "aws_pinpoint_apns_channel" "apns" {
application_id = aws_pinpoint_app.main.application_id
bundle_id = aws_ssm_parameter.apns_bundle_id.value
team_id = aws_ssm_parameter.apns_team_id.value
token_key = file("./files/key/token_file.p8")
token_key_id = aws_ssm_parameter.apns_token_key_id.value
}
この実装方法で、APNsチャネルの登録は無事に成功しました。
Firebase Cloud Messaging (FCM)向けのチャネルの実装
こちらはAPNsチャネルの実装よりも敷居が低いです。Firebaseプロジェクトを新規に作成したときに生成される、FCN用の”サーバーキー”をAPIキーとしてチャネルに組み込んであげるだけでOK、なのですが、ここでもトラップが。
APIキーは当然SSMパラメータストア内に格納したいのですが、格納して実行したところ、”401 Unauthorized”エラーが。なのでものは試しにAPIキーをそのまま引数として代入すると、なんの問題もなくFCMチャネルが作成されます。というわけで、仕方がないので以下のようにAPIキーを実装。
# ===============================================================================
# Firebase Cloud Messaging (FCM)
# ===============================================================================
variable "gcm_api_key" {
default = "(FCM Api Key)"
}
その上で、Amazon PinpointのFCMチャネル設定を以下のように実装します。
# ===============================================================================
# Pinpoint Settings for Android
# ===============================================================================
resource "aws_pinpoint_gcm_channel" "gcm" {
application_id = aws_pinpoint_app.main.application_id
api_key = var.gcm_api_key
}
この実装方法で無事にFCMチャネルも作成できるようになりました。
謎というか宿題というか
一連の試行錯誤を通じて感じたのは、なぜSSMパラメータストアでは値がチャネル設定側に正しく渡らないのに、variablesにすると値が正しく渡るのかというところです。文字コードの問題なのか、何かしらエンコーディングしてあげないといけないのか、謎は深まるばかり。
ただ、APIキーのような正しく管理しなくてはならない値に関してはできるだけSSMパラメータストアにSecureStringとして管理したいので、そこのところをどうするのか、自分への宿題ということでもう少し試行錯誤してみたいと思います。
そんなわけで、この投稿はもう少し続く、かもしれません。
追記
FCMの実装でSSMパラメータからうまく取得できない理由がわかりました。思い切り、“ignore_changes”を指定していたため、後からAPIキーを追加しても反映されないわけで。
というわけで、以下の実装で無事にFCM向けの設定も無事に通るようになりました。
# ===============================================================================
# SSM Parameters for Android (Pinpoint)
# ===============================================================================
resource "aws_ssm_parameter" "gcm_api_key" {
name = "/test/prod/gcm-api-key"
description = "The parameter for gcm api key with Pinpoint"
key_id = aws_kms_key.application.key_id
type = "SecureString"
value = "${api_key}"
lifecycle {
ignore_changes = [
value,
]
}
}