API Gatewayは、GUIでぽちぽちやると、かなり入力項目が多く、
コード化しておいた方が何かと便利なため、表題の構成をterraformでコード化したときのメモです。
参考にしたのは、以下のサイト
Amazon API GatewayでAPIキー認証を設定する
Amazon API Gateway で AWS Service Proxy を使って DynamoDB にアクセスする
API Gatewayから、AWS Lambdaを使わずにDynamoDBにアクセスする
1.構成図
2.各種terraformファイル
2-1. AWS環境設定部分
#####################################
# Provider Settings
#####################################
provider "aws" {
access_key = "********************"
secret_key = "*********************************"
region = "ap-northeast-1"
}
2-2. DynamoDB設定部分
#####################################
# DynamoDB Settings
#####################################
resource "aws_dynamodb_table" "sample" {
name = "sample"
read_capacity = 5
write_capacity = 5
hash_key = "primary_key"
attribute {
name = "primary_key"
type = "S"
}
}
primary_keyというキーで、DynamoDBに文字列を格納するだけです。
2-3. API Gateway設定部分
#####################################
#API Gateway Settings
#####################################
resource "aws_api_gateway_rest_api" "sample" {
name = "sample"
description = "sample"
}
resource "aws_api_gateway_api_key" "sample" {
name = "sample"
}
2-4. API GatewayのPOSTリクエスト部分
#############################################
#POST method
#############################################
resource "aws_api_gateway_model" "post-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
name = "PostSample"
description = "post-sample"
content_type = "application/json"
schema = <<EOF
{
"type" : "object",
"properties" : {
"key": { "type": "string" }
}
}
EOF
}
resource "aws_api_gateway_method" "post-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "POST"
authorization = "NONE"
api_key_required = "true"
request_models = {
"application/json" = "${aws_api_gateway_model.post-sample.name}"
}
}
resource "aws_api_gateway_integration" "post-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.post-sample.http_method}"
type = "AWS"
uri = "arn:aws:apigateway:ap-northeast-1:dynamodb:action/PutItem"
integration_http_method = "POST"
credentials = "${aws_iam_role.post-sample.arn}"
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"application/json" = "${file("request_templates/post-sample.json")}"
}
}
resource "aws_api_gateway_integration_response" "post-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.post-sample.http_method}"
status_code = "${aws_api_gateway_method_response.post-sample.status_code}"
selection_pattern = "200"
response_templates = {
"application/json" = "{'message':'Success'}"
}
}
resource "aws_api_gateway_method_response" "post-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.post-sample.http_method}"
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
POSTするときに、keyというデータを送信するようにしています。
DynamoDBの中身によって、このschemaやresponse_templatesのマッピング方法を変更すればよいかと。
2-5. API GatewayのGETリクエスト部分
#############################################
#GET method
#############################################
resource "aws_api_gateway_model" "get-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
name = "GetSample"
description = "get-sample"
content_type = "application/json"
schema = <<EOF
{
"type": "array",
"items": {
"type": "object",
"properties": {
"primary_key": {
"type": "string"
}
}
}
}
EOF
}
resource "aws_api_gateway_method" "get-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "GET"
authorization = "NONE"
api_key_required = "true"
}
resource "aws_api_gateway_integration" "get-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.get-sample.http_method}"
type = "AWS"
uri = "arn:aws:apigateway:ap-northeast-1:dynamodb:action/Scan"
integration_http_method = "POST"
credentials = "${aws_iam_role.get-sample.arn}"
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"application/json" = "${file("request_templates/get-sample.json")}"
}
}
resource "aws_api_gateway_integration_response" "get-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.get-sample.http_method}"
status_code = "${aws_api_gateway_method_response.get-sample.status_code}"
selection_pattern = "200"
response_templates = {
"application/json" = "${file("request_templates/get-sample-response.json")}"
}
}
resource "aws_api_gateway_method_response" "get-sample" {
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
resource_id = "${aws_api_gateway_rest_api.sample.root_resource_id}"
http_method = "${aws_api_gateway_method.get-sample.http_method}"
status_code = "200"
response_models = {
"application/json" = "${aws_api_gateway_model.get-sample.name}"
}
}
array型を返してますが、ここもPOST側と同じく、用途に応じてマッピングを変更すればよいかと。
#というか、ほとんどがこのマッピング部分の設定次第ですw
2-6. API Gatewayのデプロイ部分
#############################################
#Deploy
#############################################
resource "aws_api_gateway_deployment" "sample" {
depends_on = [
"aws_api_gateway_method.get-sample",
"aws_api_gateway_method.post-sample",
]
rest_api_id = "${aws_api_gateway_rest_api.sample.id}"
stage_name = "sample"
}
内容に変更がなければ、terraformで初回applyのみデプロイされる模様
2-7. IAMの設定部分
###############################################
#IAM Settings
###############################################
resource "aws_iam_role" "get-sample" {
name = "get-sample"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "dynamodb.amazonaws.com"
},
"Effect": "Allow",
"Sid": "2"
}
]
}
EOF
}
resource "aws_iam_role_policy" "get-sample" {
name = "get-sample"
role = "${aws_iam_role.get-sample.id}"
policy = "${file("policy/get-sample.json")}"
}
resource "aws_iam_role" "post-sample" {
name = "post-sample"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "dynamodb.amazonaws.com"
},
"Effect": "Allow",
"Sid": "2"
}
]
}
EOF
}
resource "aws_iam_role_policy" "post-sample" {
name = "post-sample"
role = "${aws_iam_role.post-sample.id}"
policy = "${file("policy/post-sample.json")}"
}
POSTとGETの違いは、policy配下のjsonで設定しています。
2-8. policyの設定部分
POST用
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DynamoDBPutItem",
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:PutItem"
],
"Resource": [
"arn:aws:dynamodb:ap-northeast-1:**********:table/sample"
]
},
{
"Sid": "cloudwatchlog",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
GET用
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DynamoDBScanItem",
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": [
"arn:aws:dynamodb:ap-northeast-1:**********:table/sample"
]
},
{
"Sid": "cloudwatchlog",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
2-9. request_templeteの設定部分
POSTのリクエストテンプレート
#set($inputRoot = $input.path('$'))
{
"TableName": "sample",
"Item": {
"primary_key": {
"S": "${inputRoot.key}"
}
}
}
GETのリクエストテンプレート
{
"TableName": "sample"
}
GETのレスポンステンプレート
#set($items = $input.path('$.Items'))
[
#foreach($item in $items)
{
"key": "${item.primary_key.S}"
}#if($foreach.hasNext),#end
#end
]
ここの部分は、用途によって変更すればよいかと。
3.実行結果
3-1.POSTリクエスト
$ curl -XPOST --data '{"key":"test"}' -H 'Content-Type:application/json' -H 'x-api-key:*********************' https://**********.execute-api.ap-northeast-1.amazonaws.com/sample
{'message':'Success'}
3-2.GETリクエスト
$ curl -H'x-api-key:*********************' https://**********.execute-api.ap-northeast-1.amazonaws.com/sample
[
{
"key": "test"
}]
DynamoDBの値を取得できました^^
4.まとめ
少しコード量が多い気がしますが、terraformでもAPI Gatewayのコード化ができました。
今回はDynamoDBで設定しましたが、同じような感じで他のAWSのサービスと連携できるかなと。
なお、検証で使ったコードは、以下にあげてあります。
https://github.com/CkReal/apigw4dynamodb