はじめに
以前の記事で、TerraformでAPI Gatewayを構築することを書いてみたが、基本的な部分過ぎて実用的ではなかったので今回はゲートウェイのレスポンスと、ステージの詳細設定をするためのIaCをまとめる。
例によって、Terraformの公式ドキュメントは書き味が薄くて困るので、内容の薄い部分について、AWSの開発者ガイドやCLIのリファレンス等を組み合わせながらまとめる。
文中の画面キャプチャや画面文言は2020年7月25日時点のものなので、今後文言が変わったり、項目の増減があるかもしれないことをご了承いただきたい。
前提条件
- 以前のTerraform+Amazon API Gatewayの記事の中身がなんとなく理解できる
- Terraformについてはある程度書き方を理解していて、自力でリファレンスを見ながら書くことができる程度の知識量を期待する
ゲートウェイのレスポンスを設定する
以下の画面のことを指す。
Terraformのリソースとしては、api_gateway_gateway_responseとなる。
これを見ると、
response_type
- (Required) The response type of the associated GatewayResponse.
と書かれていて、いきなり心が折れることになる。
一体画面の項目が何のタイプにマッピングされるのよ……
ということで、TerraformはバックエンドではCLIを動かしていると思われるので、該当のCLIのリファレンスを見てみる。
The response type of the associated GatewayResponse . Valid values are
- ACCESS_DENIED
(以下略)
とある。よしよし、これならいけそうだ。
というわけで、画面の日本語とマッピングしていってみる。
マネコン画面の日本語 | CLIのパラメータ(Terraformでも使える) |
---|---|
アクセスが拒否されました | ACCESS_DENIED |
API 設定エラー | API_CONFIGURATION_ERROR |
オーソライザー設定エラー | AUTHORIZER_CONFIGURATION_ERROR |
オーソライザーエラー | AUTHORIZER_FAILURE |
リクエスト本文が不正です | BAD_REQUEST_BODY |
リクエストパラメータが不正です | BAD_REQUEST_PARAMETERS |
DEFAULT 4XX | DEFAULT_4XX |
DEFAULT 5XX | DEFAULT_5XX |
期限切れのトークン | EXPIRED_TOKEN |
統合の失敗 | INTEGRATION_FAILURE |
統合のタイムアウト | INTEGRATION_TIMEOUT |
無効な API キー | INVALID_API_KEY |
無効な署名 | INVALID_SIGNATURE |
認証トークンが見つかりません | MISSING_AUTHENTICATION_TOKEN |
クォータを超過しました | QUOTA_EXCEEDED |
リクエストが大きすぎます | REQUEST_TOO_LARGE |
リソースは存在しません | RESOURCE_NOT_FOUND |
スロットル済み | THROTTLED |
権限がありません | UNAUTHORIZED(たぶん) |
サポートされていないメディアタイプ | UNSUPPORTED_MEDIA_TYPE |
WAF フィルター | WAF_FILTERED |
ちなみに、CLIのリファレンスやmanにはWAFフィルター
該当するCLI項目がない。
開発者ガイドを見ると、どうやらWAF_FILTERED
らしい。いや、これ日本語おかしいだろ。WAFにフィルターされた
とかにしないと分かりにくい……。
※ちなみに、リファレンスやmanには載っていないが、WAF_FILTERED
で設定変更できたので、パラメータとしては有効なようだ。
それでは、よくcurlで間違ったURIとかメソッドを狙って返される
{"message":"Missing Authentication Token"}
を変更してみよう。response_type
はMISSING_AUTHENTICATION_TOKEN
で、デフォルトのHTTPレスポンスコードは403なので、レスポンスコードも変えてみよう(存在しないパスが403 Forbidden
になるのはある意味正しいので、本来はコードまでは変更する必要がないかもしれないが、今回はおためしということで)。
resource "aws_api_gateway_gateway_response" "missing_authentication_token" {
rest_api_id = aws_api_gateway_rest_api.my_api.id
response_type = "MISSING_AUTHENTICATION_TOKEN"
status_code = "404"
response_templates = {
"application/json" = "{'message':'Resource Not Found'}"
}
response_parameters = {
"gatewayresponse.header.test-header" : "'test'"
}
}
response_parameters
では、統合レスポンスを使用している場合に、その値をAPIのレスポンスにマッピングすることができる。詳細はAmazon API Gateway API リクエストおよびレスポンスデータマッピングのリファレンスを見ると良い。
今回のケースでは、このレスポンスのパターンになった場合、test-header: test
を返す。
では、これをterraform apply
した後にアクセスしてみよう。
curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/user?id=11111\&name=Taro
HTTP/2 404
date: Sat, 25 Jul 2020 08:57:45 GMT
content-type: application/json
content-length: 32
x-amzn-requestid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
test-header: test
x-amzn-errortype: MissingAuthenticationTokenException
x-amz-apigw-id: xxxxxxxxxxxxxxxx
{'message':'Resource Not Found'}
といった具合で、設定した通りのレスポンスが返却されるようになった。
ステージの詳細設定
次に以下の画面について。
主なTerraformのリソースとしてはaws_api_gateway_stageだ。
ここも、マネコン画面に表示されている日本語とTerraformの引数の内容をマッピングしてみる。
設定タブ
設定タブについては、aws_api_gateway_stageの他にapi_gateway_method_settingsが重要になる。
なぜapi_gateway_method_settings?と思うかもしれないが、このリソースは、method_path
のプロパティを */*
にすることで、stage_name
で指定したステージ全体のデフォルトの設定をすることができる。
ステージは以下のリソースのツリーを選択すると、以下のようにラジオボタンが表示される。
ここで、「このメソッドの上書き」を選択すると、
のように設定項目が展開され、リソースとメソッド固有の設定で上書きすることができる。
Terraformでも同様に、stage_name
に「リソースパス/メソッド」を設定することで */*
で設定した内容を上書きすることができる。
キャッシュ設定
マネコン画面の日本語 | Terraformの引数 |
---|---|
キャッシュ設定 | cache_cluster_enabled ※true/falseのboolean |
なお、キャッシュ設定は、マネコン画面上ではチェックボックスをONにすると、
な画面が表示されるが、これを詳細設定するリソースは、aws_api_gateway_method_settingsになる。
aws_api_gateway_stage
で設定できるのは、「キャッシュキャパシティー(cache_cluster_size)」までだ。aws_api_gateway_method_settings
では、以下のようにマッピングされる。
マネコン画面の日本語 | Terraformの引数 |
---|---|
キャッシュデータを暗号化する | cache_data_encrypted ※true/falseのboolean |
キャッシュ有効期限 (TTL) | cache_ttl_in_seconds |
認可が必要 | require_authorization_for_cache_control ※true/falseのboolean |
未認可リクエストの処理 | unauthorized_cache_control_header_strategy |
デフォルトのメソッドスロットリング
これも、aws_api_gateway_stage
ではなくてaws_api_gateway_method_settings
で定義する。
マネコン画面の日本語 | Terraformの引数 |
---|---|
デフォルトのメソッドスロットリング | スロットリングの制御内容は開発者ガイドを参照。Terraformでは、以下の throttling_rate_limit か throttling_burst_limit のいずれかが-1だとチェックOFFになる |
レート | throttling_rate_limit |
バースト | throttling_burst_limit |
ウェブアプリケーションファイアウォール (WAF)
これはイマイチよく分からなかった……。
aws_wafregional_web_acl_associationのリソースに
API Gateway Association Example
という項があるので、おそらくこのことだろうか。
クライアント証明書
実験できていないので、多分この引数で合っていると思うが、詳細は不明。
マネコン画面の日本語 | Terraformの引数 |
---|---|
証明書 | client_certificate_id |
ログ/トレースタブ
CloudWatch 設定
この項目は、aws_api_gateway_stage
ではなくてaws_api_gateway_method_settings
で定義する。
マネコン画面の日本語 | Terraformの引数 |
---|---|
CloudWatch ログを有効化 | logging_level(OFFで未チェック、ERRORまたはINFOでチェック済み) |
詳細 CloudWatch メトリクスを有効化 | metrics_enabled |
なお、CloudWatch ログを有効化をチェックすると、画面上では以下の項目が表示される。
マネコン画面の日本語 | Terraformの引数 |
---|---|
ログレベル | logging_level |
リクエスト/レスポンスをすべてログ | data_trace_enabled ※true/falseのboolean |
詳細CloudWatch メトリクスを有効化 | metrics_enabled ※true/falseのboolean |
また、CloudWatch ログを有効化するためには、以下の「設定」で、CloudWatchLogsの書き込み権を持ったIAMロールを設定する必要がある。
このため、今回は以下のようにIAMロールを定義する。CloudWatchLogsの書き込みポリシについては、AmazonAPIGatewayPushToCloudWatchLogs
を使えとAWSのブログに書いてあった。
################################################################################
# IAM Role for API Gateway Put CloudWatch Logs #
################################################################################
resource "aws_iam_role" "apigateway_putlog" {
name = local.rest_api_putlog_role_name
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "apigateway_putlog" {
role = "${aws_iam_role.apigateway_putlog.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}
これを、以下のようにaws_api_gateway_account
で設定すれば良い。
resource "aws_api_gateway_account" "my_api" {
cloudwatch_role_arn = aws_iam_role.apigateway_putlog.arn
}
で、ここで油断してはならないのは、aws_api_gateway_account
はそのままだと aws_api_gateway_method_settings
との依存関係がないため、順序性が保証されず、ログ設定がエラーになってしまう。そのため、以下のように依存関係を定義しておく。
resource "aws_api_gateway_method_settings" "all" {
depends_on = [
aws_api_gateway_account.my_api,
]
rest_api_id = "${aws_api_gateway_rest_api.my_api.id}"
stage_name = "${aws_api_gateway_stage.prod.stage_name}"
method_path = "*/*"
settings {
logging_level = "ERROR"
}
}
カスタムアクセスのログ設定
この項目はなんか日本語が変な気が…。「の」は要らないような。
マネコン画面の日本語 | Terraformの引数 |
---|---|
アクセスログの有効化 | access_log_settings ※ブロックに以下の項目を記載する |
なお、アクセスログの有効化をチェックすると、画面上では以下の項目が表示される。
これは、aws_api_gateway_stage
のリソースのaccess_log_settings
のブロックで定義する。
マネコン画面の日本語 | Terraformの引数 |
---|---|
Access Log Destination ARN | destination_arn |
ログの形式 | format |
ログの形式の出力例は、画面のボタンをポチっと押すか、AWSの開発者ガイドを参照。
この項目を有効にするためには、まずはCloudWatch Logsのリソースを作らなければいけないので、以下のように作成する。
resource "aws_cloudwatch_log_group" "apigateway_accesslog" {
name = local.rest_api_accesslog_loggroup_name
}
次に、aws_api_gateway_stage
に以下のように定義したアクセスログを設定すれば良い。
resource "aws_api_gateway_stage" "prod" {
stage_name = "prod"
rest_api_id = aws_api_gateway_rest_api.my_api.id
deployment_id = aws_api_gateway_deployment.dev.id
access_log_settings {
destination_arn = aws_cloudwatch_log_group.apigateway_accesslog.arn
format = "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] \"$context.httpMethod $context.resourcePath $context.protocol\" $context.status $context.responseLength $context.requestId"
}
}
X-Ray トレース
これはシンプルに設定可能。
マネコン画面の日本語 | Terraformの引数 |
---|---|
X-Ray トレースの有効化 | xray_tracing_enabled |
ステージ変数タブ
これはvariables
の引数で渡すことができる。
Terraformのドキュメントでは、
(Optional) A map that defines the stage variables
と書かれているので、渡し方に注意が必要。
SDKの生成/エクスポート/デプロイ履歴/ドキュメント履歴タブ
この辺は設定項目ではなく参照系であるため、特にTerraformに紐づく項目は無い。
Canaryタブ
うーん、これはTerraform未実装な気がする。
CLIのドキュメントには--canary-settings
オプションが存在するが、Terraformでは難しいということだろうか(いずれにしろ、CodeDeployのような便利なCanaryリリースではないので、あまり使えないのだが)。
デプロイについて
API Gatewayのリソースについては、以下のようにデプロイを定義する。
triggersは、「このリソースが変更されたら再デプロイする」という機能らしい。
詳細は、公式のNotesを参照。
lifecycleは、スキマを作らないようにするための施策だ。
resource "aws_api_gateway_deployment" "dev" {
rest_api_id = aws_api_gateway_rest_api.my_api.id
stage_name = "dev"
triggers = {
redeployment = sha1(join(",", list(
jsonencode(module.get_user),
jsonencode(module.put_user),
jsonencode(aws_api_gateway_gateway_response.missing_authentication_token),
)))
}
lifecycle {
create_before_destroy = true
}
}
ただ、これだけだと、devにデプロイした機能がprodまで伝播してくれない。
うーむ、prod側にも設定を入れるべきなのだろうか。それとも、そこは別契機でデプロイするということか。