LoginSignup
11
9

More than 5 years have passed since last update.

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

Posted at

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

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9