AWS
DynamoDB
Terraform
APIGateway

DynamoDBの操作を行うAPI Gatewayをterraformで準備するときのメモ

More than 1 year has passed since last update.

API Gatewayは、GUIでぽちぽちやると、かなり入力項目が多く、

コード化しておいた方が何かと便利なため、表題の構成をterraformでコード化したときのメモです。

参考にしたのは、以下のサイト

Amazon API GatewayでAPIキー認証を設定する

Amazon API Gateway で AWS Service Proxy を使って DynamoDB にアクセスする

API Gatewayから、AWS Lambdaを使わずにDynamoDBにアクセスする


1.構成図

図解するほどでもないですが、各種構成は以下の通り.

apigw4dynamodb.PNG


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'}

dynamodb.PNG

データ投入後はこんな感じです。


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