前提条件
API Gatewayへの権限
API Gatewayに対してフル権限があること。
Lambdaへの権限
Lambdaに対してフル権限があること。
AWS CLI
以下のバージョンで動作確認済
- AWS CLI 1.10.58
aws --version
aws-cli/1.10.58 Python/2.7.11 Darwin/15.6.0 botocore/1.4.48
0. 準備
0.1. リージョンの決定
export AWS_DEFAULT_REGION='ap-northeast-1'
0.2. 変数の確認
プロファイルが想定のものになっていることを確認します。
aws configure list
Name Value Type Location
---- ----- ---- --------
profile lambdaFull-prjz-mbp13 env AWS_DEFAULT_PROFILE
access_key ****************XXXX shared-credentials-file
secret_key ****************XXXX shared-credentials-file
region ap-northeast-1 env AWS_DEFAULT_REGION
1. 事前作業
1.1. API名の指定
APIGW_API_NAME="slack-echo-command-$( date '+%Y%m%d' )" \
&& echo ${APIGW_API_NAME}
同名のREST APIが存在しないことを確認します。
aws apigateway get-rest-apis \
--query "items[?name == \`${APIGW_API_NAME}\`].name"
[]
1.2. AWS IDの取得
AWS_ID=$( \
aws sts get-caller-identity \
--query 'Account' \
--output text \
) \
&& echo ${AWS_ID}
XXXXXXXXXXXX
2. APIの作成
2.1. APIの作成
APIを作成するときは、説明も必ず入れるようにしましょう。
APIGW_API_DESC='API for slack-echo-command'
cat << ETX
APIGW_API_NAME: ${APIGW_API_NAME}
APIGW_API_DESC: "${APIGW_API_DESC}"
ETX
aws apigateway create-rest-api \
--name ${APIGW_API_NAME} \
--description "${APIGW_API_DESC}"
{
"id": "xxxxxxxxxx",
"name": "slack-echo-command-20160829",
"description": "API for slack-echo-command",
"createdDate": 1448002904
}
2.2. APIの確認
aws apigateway get-rest-apis \
--query "items[?name == \`${APIGW_API_NAME}\`].name"
[
"slack-echo-command-20160829"
]
2.3. API IDの取得
APIGW_API_ID=$( \
aws apigateway get-rest-apis \
--query "items[?name == \`${APIGW_API_NAME}\`].id" \
--output text \
) \
&& echo ${APIGW_API_ID}
xxxxxxxxxx
2.4. APIの確認
aws apigateway get-rest-api \
--rest-api-id ${APIGW_API_ID}
{
"id": "xxxxxxxxxx",
"name": "slack-echo-command-20160829",
"description": "API for slack-echo-command",
"createdDate": 1445947998
}
2.5. モデルの確認
aws apigateway get-models \
--rest-api-id ${APIGW_API_ID}
{
"items": [
{
"description": "This is a default empty schema model",
"schema": "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"title\" : \"Empty Schema\",n \"type\" : \"object\"\n}",
"contentType": "application/json",
"id": "xxxxxx",
"name": "Empty"
},
{
"description": "This is a default error schema model",
"schema": "{\n \"$schema\" : \"http://json-schema.org/draft-04/schema#\",\n \"title\" : \"Error Schema\",\n \"type\" : \"object\",\n \"properties\" : {\n \"message\" : { \"type\" : \"string\" }\n }\n}",
"contentType": "application/json",
"id": "xxxxxx",
"name": "Error"
}
]
}
3. リソースの作成
3.1. 現在のリソースの確認
aws apigateway get-resources \
--rest-api-id ${APIGW_API_ID}
{
"items": [
{
"path": "/",
"id": "xxxxxxxxxx"
}
]
}
3.2. 現在のリソースのリソースID取得
APIGW_RESOURCE_PATH="/"
APIGW_RESOURCE_ID=$( \
aws apigateway get-resources \
--rest-api-id ${APIGW_API_ID} \
--query "items[?path == \`${APIGW_RESOURCE_PATH}\`].id" \
--output text \
) \
&& echo ${APIGW_RESOURCE_ID}
xxxxxxxxxx
aws apigateway get-resource \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID}
{
"path": "/",
"id": "xxxxxxxxxx"
}
3.3. 新しいリソースの作成
APIGW_PARENT_ID="${APIGW_RESOURCE_ID}"
APIGW_PATH_PART='sayhello'
cat << ETX
APIGW_API_ID: ${APIGW_API_ID}
APIGW_PARENT_ID: ${APIGW_PARENT_ID}
APIGW_PATH_PART: ${APIGW_PATH_PART}
ETX
aws apigateway create-resource \
--rest-api-id ${APIGW_API_ID} \
--parent-id ${APIGW_PARENT_ID} \
--path-part ${APIGW_PATH_PART}
{
"path": "/sayhello",
"pathPart": "sayhello",
"id": "xxxxxx",
"parentId": "xxxxxxxxxx"
}
3.4. 新しいリソースのリソースID取得
APIGW_RESOURCE_PATH="${APIGW_RESOURCE_PATH}${APIGW_PATH_PART}" \
&& echo ${APIGW_RESOURCE_PATH}
APIGW_RESOURCE_ID=$( \
aws apigateway get-resources \
--rest-api-id ${APIGW_API_ID} \
--query "items[?path == \`${APIGW_RESOURCE_PATH}\`].id" \
--output text \
) \
&& echo ${APIGW_RESOURCE_ID}
xxxxxxxxxx
3.5. 新しいリソースの内容確認
aws apigateway get-resource \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID}
{
"path": "/sayhello",
"pathPart": "sayhello",
"id": "xxxxxx",
"parentId": "gca9ho3sa4"
}
4. メソッドの作成
4.1. Lambda関数の指定
LAMBDA_FUNC_NAME='slack-echo-command-20160829'
LAMBDA_FUNC_ARN=$( \
aws lambda get-function \
--function-name ${LAMBDA_FUNC_NAME} \
--query 'Configuration.FunctionArn' \
--output text \
) \
&& echo ${LAMBDA_FUNC_ARN}
arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829
4.2. API Gateway Integration URLの指定
APIGW_INTEG_URI="arn:aws:apigateway:${AWS_DEFAULT_REGION}:lambda:path/2015-03-31/functions/${LAMBDA_FUNC_ARN}/invocations" \
&& echo ${APIGW_INTEG_URI}
arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829
4.3. HTTPメソッドの作成
HTTP_METHOD='POST'
AUTH_TYPE='NONE'
cat << ETX
APIGW_API_ID: ${APIGW_API_ID}
APIGW_RESOURCE_ID: ${APIGW_RESOURCE_ID}
HTTP_METHOD: ${HTTP_METHOD}
AUTH_TYPE: ${AUTH_TYPE}
ETX
aws apigateway put-method \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--authorization-type ${AUTH_TYPE}
{
"apiKeyRequired": false,
"httpMethod": "POST",
"authorizationType": "NONE"
}
aws apigateway get-method \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD}
{
"apiKeyRequired": false,
"httpMethod": "POST",
"authorizationType": "NONE"
}
aws apigateway get-resource \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID}
{
"resourceMethods": {
"POST": {}
},
"pathPart": "sayhello",
"parentId": "xxxxxxxxxx",
"path": "/sayhello",
"id": "xxxxxx"
}
4.4. integrationの作成
aws apigateway get-integration \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD}
A client error (NotFoundException) occurred when calling the GetIntegration operation: No integration defined for method
Integration Typeの指定
APIGW_INTEG_TYPE='AWS'
IntegrationのHTTPメソッド指定
APIGW_INTEG_METHOD='POST'
Integration RequestのBody Mapping Templates作成
FILE_INPUT="${APIGW_RESOURCE_ID}-BODY_MAPPING_TEMPLATE.json" \
&& echo ${FILE_INPUT}
cat << \EOF > ${FILE_INPUT}
{
"application/x-www-form-urlencoded": "{ \"body\": $input.json(\"$\") }"
}
EOF
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
jsonlint -q ${FILE_INPUT}
エラーが出力されなければOKです。
APIGW_PASSTHROUGH_BEHAVIOR='WHEN_NO_TEMPLATES'
cat << ETX
APIGW_API_ID: ${APIGW_API_ID}
APIGW_RESOURCE_ID: ${APIGW_RESOURCE_ID}
HTTP_METHOD: ${HTTP_METHOD}
APIGW_INTEG_TYPE: ${APIGW_INTEG_TYPE}
APIGW_INTEG_METHOD: ${APIGW_INTEG_METHOD}
APIGW_INTEG_URI: ${APIGW_INTEG_URI}
FILE_INPUT: ${FILE_INPUT}
APIGW_PASSTHROUGH_BEHAVIOR: ${APIGW_PASSTHROUGH_BEHAVIOR}
ETX
aws apigateway put-integration \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--type ${APIGW_INTEG_TYPE} \
--integration-http-method ${APIGW_INTEG_METHOD} \
--uri ${APIGW_INTEG_URI} \
--request-templates file://${FILE_INPUT} \
--passthrough-behavior ${APIGW_PASSTHROUGH_BEHAVIOR}
{
"passthroughBehavior": "WHEN_NO_TEMPLATES",
"cacheKeyParameters": [],
"uri": "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829/invocations",
"httpMethod": "POST",
"requestTemplates": {
"application/x-www-form-urlencoded": "{ \"body\": $input.json(\"$\") }"
},
"cacheNamespace": "xxxxxx",
"type": "AWS"
}
aws apigateway get-integration \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD}
{
"passthroughBehavior": "WHEN_NO_TEMPLATES",
"cacheKeyParameters": [],
"uri": "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829/invocations",
"httpMethod": "POST",
"requestTemplates": {
"application/x-www-form-urlencoded": "{ \"body\": $input.json(\"$\") }"
},
"cacheNamespace": "xxxxxx",
"type": "AWS"
}
4.5. Integration Responseの作成
POSTメソッドのリザルトコード200でのIntegration Responseが存在作成しな
いことを確認します。
HTTP_STATUS_CODE='200'
aws apigateway get-integration-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE}
A client error (NotFoundException) occurred when calling the GetMethodResponse operation: Invalid Response status code specified
POSTメソッドのリザルトコード200でのIntegration Responseを作成します。
cat << ETX
APIGW_API_ID: ${APIGW_API_ID}
APIGW_RESOURCE_ID: ${APIGW_RESOURCE_ID}
HTTP_METHOD: ${HTTP_METHOD}
HTTP_STATUS_CODE: ${HTTP_STATUS_CODE}
ETX
aws apigateway put-integration-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE} \
--selection-pattern ".*"
{
"selectionPattern": ".*",
"statusCode": "200"
}
POSTメソッドのリザルトコード200でのIntegration Responseを確認します。
aws apigateway get-integration-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE}
{
"selectionPattern": ".*",
"statusCode": "200"
}
aws apigateway get-integration \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD}
{
"integrationResponses": {
"200": {
"selectionPattern": ".*",
"statusCode": "200"
}
},
"passthroughBehavior": "WHEN_NO_TEMPLATES",
"cacheKeyParameters": [],
"uri": "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829/invocations",
"httpMethod": "POST",
"requestTemplates": {
"application/x-www-form-urlencoded": "{ "body": $input.json("$") }"
},
"cacheNamespace": "hrs5il",
"type": "AWS"
}
4.6. Lambda関数の実行権限付与
POSTメソッドのリザルトコード200でのIntegration Responseに、Lambda関数
を実行する権限を付与します。
Statement IDを乱数32桁で生成します。
LAMBDA_STAT_ID=$(od -vAn -N16 -tx < /dev/urandom | sed 's/ //g')
RESOURCE_PATH="${APIGW_RESOURCE_PATH}"
cat << ETX
AWS_ID: ${AWS_ID}
APIGW_API_ID: ${APIGW_API_ID}
HTTP_METHOD: ${HTTP_METHOD}
RESOURCE_PATH: ${RESOURCE_PATH}
ETX
SOURCE_ARN="arn:aws:execute-api:${AWS_DEFAULT_REGION}:${AWS_ID}:${APIGW_API_ID}/*/${HTTP_METHOD}${RESOURCE_PATH}" \
&& echo ${SOURCE_ARN}
LAMBDA_PERMIT_ACTION='lambda:InvokeFunction'
LAMBDA_PERMIT_PRINCIPAL='apigateway.amazonaws.com'
RESOURCE_PATH="${APIGW_RESOURCE_PATH}"
cat << ETX
LAMBDA_FUNC_NAME: ${LAMBDA_FUNC_NAME}
LAMBDA_STAT_ID: ${LAMBDA_STAT_ID}
LAMBDA_PERMIT_ACTION: ${LAMBDA_PERMIT_ACTION}
LAMBDA_PERMIT_PRINCIPAL: ${LAMBDA_PERMIT_PRINCIPAL}
SOURCE_ARN: ${SOURCE_ARN}
ETX
aws lambda add-permission \
--function-name ${LAMBDA_FUNC_NAME} \
--statement-id ${LAMBDA_STAT_ID} \
--action ${LAMBDA_PERMIT_ACTION} \
--principal ${LAMBDA_PERMIT_PRINCIPAL} \
--source-arn ${SOURCE_ARN}
{
"Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:ap-northeast-1:XXXXXXXXXXXX:xxxxxxxxxx/*/POST/sayhello\"}},\"Action\":[\"lambda:InvokeFunction\"],\"Resource\":\"arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160829\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Sid\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}"
}
aws lambda get-policy \
--function-name ${LAMBDA_FUNC_NAME} \
| sed 's/\\//g' | sed 's/\"{/{/' | sed 's/\"$//' \
| jp.py "Policy.Statement[?Sid == \`${LAMBDA_STAT_ID}\`]"
[
{
"Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160828",
"Effect": "Allow", "Sid": "54dee6c20e66aa1d1af183b2b8a2796a",
"Action": "lambda:InvokeFunction",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:ap-northeast-1:XXXXXXXXXXXX:npvefdajeb/*/POST/sayhello"
} },
"Principal": {
"Service": "apigateway.amazonaws.com"
}
}
]
4.7. Method Responseの作成
aws apigateway get-method-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE}
A client error (NotFoundException) occurred when calling the GetIntegrationResponse operation: Invalid Response status code specified
aws apigateway put-method-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE} \
--response-models '{}'
{
"responseModels": {},
"statusCode": "200"
}
aws apigateway get-method-response \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD} \
--status-code ${HTTP_STATUS_CODE}
{
"responseModels": {},
"statusCode": "200"
}
aws apigateway get-method \
--rest-api-id ${APIGW_API_ID} \
--resource-id ${APIGW_RESOURCE_ID} \
--http-method ${HTTP_METHOD}
{
"apiKeyRequired": false,
"httpMethod": "POST",
"methodIntegration": {
"integrationResponses": {
"200": {
"selectionPattern": ".*",
"statusCode": "200"
}
},
"passthroughBehavior": "WHEN_NO_TEMPLATES",
"cacheKeyParameters": [],
"uri": "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:slack-echo-command-20160828/invocations",
"httpMethod": "POST",
"requestTemplates": {
"application/x-www-form-urlencoded": "{ "body": $input.json("$") }"
},
"cacheNamespace": "0z3hfw",
"type": "AWS"
},
"methodResponses": {
"200": {
"responseModels": {},
"statusCode": "200"
}
},
"authorizationType": "NONE"
}
5. APIのデプロイ
5.1. APIのデプロイ
aws apigateway get-deployments \
--rest-api-id ${APIGW_API_ID}
{
"items": []
}
APIGW_STAGE_NAME='prod'
APIGW_STAGE_DESC='This is a prod.'
APIGW_DEPLOY_DESC='Calling Lambda functions blueprint.'
cat << ETX
APIGW_API_ID: ${APIGW_API_ID}
APIGW_STAGE_NAME: ${APIGW_STAGE_NAME}
APIGW_STAGE_DESC: "${APIGW_STAGE_DESC}"
APIGW_DEPLOY_DESC: "${APIGW_DEPLOY_DESC}"
ETX
aws apigateway create-deployment \
--rest-api-id ${APIGW_API_ID} \
--stage-name ${APIGW_STAGE_NAME} \
--stage-description "${APIGW_STAGE_DESC}" \
--description "${APIGW_DEPLOY_DESC}"
{
"description": "Calling Lambda functions blueprint.",
"id": "xxxxxx",
"createdDate": 1448115971
}
5.2. APIのステージ名の取得
aws apigateway get-stages \
--rest-api-id ${APIGW_API_ID}
{
"item": [
{
"description": "This is a prod.",
"stageName": "test",
"cacheClusterEnabled": false,
"cacheClusterStatus": "NOT_AVAILABLE",
"deploymentId": "xxxxxx",
"lastUpdatedDate": 1448115971,
"createdDate": 1448115971,
"methodSettings": {}
}
]
}
APIGW_STAGE_NAME=$( \
aws apigateway get-stages \
--rest-api-id ${APIGW_API_ID} \
--query "item[?description == \`${APIGW_STAGE_DESC}\`].stageName" \
--output text \
) \
&& echo ${APIGW_STAGE_NAME}
prod
aws apigateway get-stage \
--rest-api-id ${APIGW_API_ID} \
--stage-name ${APIGW_STAGE_NAME}
{
"stageName": "prod",
"description": "This is a prod.",
"cacheClusterEnabled": false,
"cacheClusterStatus": "NOT_AVAILABLE",
"deploymentId": "xxxxxx",
"lastUpdatedDate": 1448115971,
"createdDate": 1448115971,
"methodSettings": {}
}
6. APIのテスト
APIGW_URI="https://${APIGW_API_ID}.execute-api.${AWS_DEFAULT_REGION}.amazonaws.com/${APIGW_STAGE_NAME}${APIGW_RESOURCE_PATH}" \
&& echo ${APIGW_URI}
7. Slack (API Endpoint追加)
-
https://${SLACK_TEAM_NAME}.slack.com/apps にアクセスします。(App Directory 画 面)
- 検索窓に 'Slash Commands' と入力します。
- 'Slash Commands'が表示されるので、クリックします。
-
Browse Apps > Slash Commands (画面)
- (ボタン)をクリックします。
-
Edit configuration (画面)
- Integration Settings
- URL: API Gatewayのエンドポイント
- Integration Settings
8. slackコマンドの実行
slackから、/sayhelloコマンドに続いて英数字半角のテキストを入力してください。
/sayhello ec2