概要
やりたいこと
Slack Events API の Challenge 認証する API Gateway + Lambda を Terraform で作成したい。
API Gateway
API Gateway に関しては下記が大変わかりやすい。
下記記事から API Gateway 作成パターンを引用。
機能 | 選択肢 |
---|---|
認証・認可 | IAM | Lambdaオーソライザー | Cognitoオーソライザー |
エンドポイントタイプ | エッジ | リージョン | プライベート |
統合タイプ | Lambda | HTTP | Mock | AWSサービス | VPCリンク |
プロキシ統合 | TRUE | FALSE |
以下の Lambda プロキシ統合の API Gateway を作成する(ヘッダーを受け取りたいため)。
- 認証・認可: なし
- エンドポイントタイプ : エッジ
- 統合タイプ : Lambda
- プロキシ統合 : True
Terraform
Terraform 準備
vi main.tf
provider "aws" {
profile = "terraform"
region = "ap-northeast-1"
}
provider "archive" {
version = "1.3.0"
}
terraform {
required_version = ">= 0.12.0"
}
初期化
terraform init
Lambda 作成
ローカルで作成した Lambda 用プログラムを Terraform でデプロイする。
mkdir app vi app/lambda.tf
import os
import json
import logging
import urllib.request
# ログ設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handle_slack_event(request: dict, context) -> str:
logging.info(json.dumps(request))
header = request['headers']
slack_event = json.loads(request['body'])
logging.info(json.dumps(header))
logging.info(json.dumps(slack_event))
if "challenge" in slack_event:
#return slack_event.get("challenge")
return {
'statusCode': 200,
'body': slack_event.get("challenge")
}
vi lambda.tf
locals {
function_name = "slackbot_function"
}
# ====================
#
# Archive
#
# ====================
data "archive_file" "function_source" {
type = "zip"
source_dir = "app"
output_path = "archive/slackbot.zip"
}
# ====================
#
# Lambda
#
# ====================
resource "aws_lambda_function" "alert_function" {
function_name = local.function_name
handler = "lambda.handle_slack_event"
role = aws_iam_role.lambda_role.arn
runtime = "python3.8"
filename = data.archive_file.function_source.output_path
source_code_hash = data.archive_file.function_source.output_base64sha256
depends_on = [aws_cloudwatch_log_group.lambda_log_group]
tags = {
Name = "${terraform.workspace}-slackbot"
}
}
# ====================
#
# IAM Role
#
# ====================
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "lambda_role" {
name = "SlackbotLambdaRole"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
# ====================
#
# CloudWatch
#
# ====================
resource "aws_cloudwatch_log_group" "lambda_log_group" {
name = "/aws/lambda/${local.function_name}"
}
API Gateway 作成
vi api_gateway.tf
locals {
api_gateway_name = "slackbot_api"
}
# ====================
#
# API Gateway
#
# ====================
resource "aws_api_gateway_rest_api" "slackbot_api" {
name = local.api_gateway_name
endpoint_configuration {
types = ["EDGE"]
}
}
resource "aws_api_gateway_resource" "slackbot_api" {
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
parent_id = aws_api_gateway_rest_api.slackbot_api.root_resource_id
path_part = "slackbot_api"
}
resource "aws_api_gateway_method" "slackbot_api" {
resource_id = aws_api_gateway_resource.slackbot_api.id
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "slackbot_api" {
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
resource_id = aws_api_gateway_resource.slackbot_api.id
http_method = aws_api_gateway_method.slackbot_api.http_method
integration_http_method = "POST"
type = "AWS_PROXY" #Lambda proxy integration
uri = aws_lambda_function.alert_function.invoke_arn
}
resource "aws_api_gateway_deployment" "slackbot_api" {
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
triggers = {
# NOTE: The configuration below will satisfy ordering considerations,
# but not pick up all future REST API changes. More advanced patterns
# are possible, such as using the filesha1() function against the
# Terraform configuration file(s) or removing the .id references to
# calculate a hash against whole resources. Be aware that using whole
# resources will show a difference after the initial implementation.
# It will stabilize to only change when resources change afterwards.
redeployment = sha1(jsonencode([
aws_api_gateway_resource.slackbot_api.id,
aws_api_gateway_method.slackbot_api.id,
aws_api_gateway_integration.slackbot_api.id,
]))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "slackbot_api" {
deployment_id = aws_api_gateway_deployment.slackbot_api.id
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
stage_name = "v1"
}
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
resource_id = aws_api_gateway_resource.slackbot_api.id
http_method = aws_api_gateway_method.slackbot_api.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
depends_on = [aws_api_gateway_method.slackbot_api]
}
resource "aws_api_gateway_integration_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.slackbot_api.id
resource_id = aws_api_gateway_resource.slackbot_api.id
http_method = aws_api_gateway_method.slackbot_api.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
response_templates = {
"application/json" = ""
}
depends_on = [aws_api_gateway_integration.slackbot_api]
}
# ====================
#
# Lambda Permission
#
# ====================
resource "aws_lambda_permission" "apigw_lambda" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.alert_function.function_name
principal = "apigateway.amazonaws.com"
# More: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-control-access-using-iam-policies-to-invoke-api.html
source_arn = "arn:aws:execute-api:ap-northeast-1:657885203613:${aws_api_gateway_rest_api.slackbot_api.id}/*/${aws_api_gateway_method.slackbot_api.http_method}${aws_api_gateway_resource.slackbot_api.path}"
}
# ====================
#
# Log
#
# ====================
resource "aws_cloudwatch_log_group" "slackbot_api" {
name = "/API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.slackbot_api.id}/${aws_api_gateway_stage.slackbot_api.stage_name}"
retention_in_days = 7
# ... potentially other configuration ...
}
デプロイ。
terraform apply
URL をメモっておく。
Slack App 作成
Slack APP を新規で作成。
Event Subscriptions より、Enable Events 後に API Gateway の URL を入力し、Verified となれば OK。
参考
terraformでAPI Gatewayを構築してAPI Key認証を設定してみた
コメント