はじめに
- 任意のAWS Lambda関数に対するAPI Gatewayを一発の実行で作成できるPythonスクリプトの紹介です。
- スクリプト自体はAWS Lambdaに作成しました。
- クライアントからのリクエスト、Lambdaからのレスポンスのボディを無変換で対向に渡します。
- API Gateway作成と同時にAPIキー認証、スロットリング、クォータの設定をしています。
#スクリプトの作成
ランタイムにPythonを指定したAWS Lambda関数(今回は関数名agdep)を作成します。
以下のコードを記載したlambda_function.pyを作成します。
import boto3
import uuid
agw = boto3.client('apigateway')
lam = boto3.client('lambda')
sts = boto3.client('sts')
def lambda_handler(event, context):
#Lambda関数存在チェック
try:
#Lambda関数ARN取得
response=lam.get_function(FunctionName=event['functionName'])
lambdaArn=response['Configuration']['FunctionArn']
except:
return('存在しないLambda関数が指定されています。')
#初期化
apiName = event['functionName'] + '_api'
regionName = 'ap-northeast-1'
accountId = (sts.get_caller_identity())['Account']
stageName = 'dev'
#API作成
agw.create_rest_api(name=apiName)
#API ID取得
apis = {}
for item in (agw.get_rest_apis())['items']:
apis[item['name']]=item
apiId = apis[apiName]['id']
#リソース作成
agw.create_resource(
restApiId=apiId,
parentId=(agw.get_resources(restApiId=apiId))['items'][0]['id'],
pathPart=apiName+'_rsc'
)
#リソースID取得
resourcePath = '/' + apiName + '_rsc'
resources = {}
for item in (agw.get_resources(restApiId=apiId))['items']:
resources[item['path']]=item
resourceId = resources[resourcePath]['id']
#メソッドリクエスト作成
agw.put_method(
restApiId=apiId,
resourceId=resourceId,
httpMethod='POST',
authorizationType='None',
apiKeyRequired=True
)
#Lambda側での関数実行許可
lam.add_permission(
FunctionName=event['functionName'],
StatementId=str(uuid.uuid4()),
Action='lambda:InvokeFunction',
Principal='apigateway.amazonaws.com',
SourceArn='arn:aws:execute-api:' + regionName + ':' + accountId + ':' + apiId + '/*/POST/' + apiName + '_rsc'
)
#統合リクエスト作成
agw.put_integration(
restApiId=apiId,
resourceId=resourceId,
httpMethod='POST',
type='AWS',
integrationHttpMethod='POST',
uri='arn:aws:apigateway:' + regionName + ':lambda:path/2015-03-31/functions/' + lambdaArn + '/invocations'
)
#メソッドレスポンス作成
agw.put_method_response(
restApiId=apiId,
resourceId=resourceId,
httpMethod='POST',
statusCode='200'
)
#統合レスポンス作成
agw.put_integration_response(
restApiId=apiId,
resourceId=resourceId,
httpMethod='POST',
statusCode='200',
responseTemplates={
'application/json': ''
}
)
#APIデプロイ
agw.create_deployment(
restApiId=apiId,
stageName=stageName
)
#APIキー作成、キー値取得
api_key_value = (agw.create_api_key(
name=apiName+'_key',
enabled=True,
stageKeys=[
{
'restApiId': apiId,
'stageName': stageName
},
]
)
)['value']
#Usageプラン作成
agw.create_usage_plan(
name=apiName+'_plan',
apiStages=[
{
'apiId': apiId,
'stage': stageName
},
],
throttle={
'burstLimit': 10,
'rateLimit': 5
},
quota={
'limit': 200,
'offset': 0,
'period': 'MONTH'
}
)
#UsageプランID取得
plans = {}
for item in (agw.get_usage_plans())['items']:
plans[item['name']]=item
usegePlanId = plans[apiName+'_plan']['id']
#APIキーID取得
keys = {}
for item in (agw.get_api_keys())['items']:
keys[item['name']]=item
keyId = keys[apiName+'_key']['id']
#Usageプランキー作成
agw.create_usage_plan_key(
usagePlanId=usegePlanId,
keyId=keyId,
keyType='API_KEY'
)
return('https://' + apiId + '.execute-api.' + regionName + '.amazonaws.com/' + stageName + resourcePath,api_key_value)
実行ロールは以下のポリシーと同等以上の権限が付与されたロールを使用してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"lambda:GetFunction",
"lambda:AddPermission",
"apigateway:PUT",
"apigateway:POST",
"apigateway:GET"
],
"Resource": [
"arn:aws:lambda:<RegionName>:<AccountID>:function:*",
"arn:aws:apigateway:<RegionName>::*"
],
"Effect": "Allow"
}
]
}
#動作確認
APIGatewayを作成したいAWS Lambda関数(今回は関数名test-func)を指定して、先程作成したスクリプトをAWSCLIより実行します。
実行結果には作成されたAPIGatewayのエンドポイントURIとAPIキーが出力されるので控えておきます。
$ function_name=test-func
$ aws lambda invoke --function-name agdep --payload {\"functionName\":\"${function_name}\"} outfile;cat outfile | jq .;rm outfile
{
"ExecutedVersion": "$LATEST",
"StatusCode": 200
}
[
"https://5wd******.execute-api.ap-northeast-1.amazonaws.com/dev/test-func_api_rsc",
"aW7OjaD8****************************"
]
先程控えたエンドポイントURIとAPIキーを指定してPOSTリクエストを実行し、想定通りのレスポンスが返ってくればAPIGatewayは利用可能になっています。
お好きなシステムと連携させてお使いください。
$ endpoint_uri=https://5wd*****.execute-api.ap-northeast-1.amazonaws.com/dev/test-func_api_rsc
$ api_key=aW7OjaD8****************************
$ data='{"key1":"value1","key2":"value2","key3":"value3"}'
$ curl -X POST -H "x-api-key:$api_key" -d $data $endpoint_uri
### test-func のレスポンス ###
#おわりに
このスクリプトで作成されるAPIGatewayは、APIキー認証により、クライアントから受けたPOSTリクエストのペイロードをそのままLambda関数へ渡し、Lambda関数からのreturnをペイロードとしたレスポンスをそのままクライアントへ返す、最低限の機能のAPIエンドポイント/HTTPプロキシとなります。
わたしはAWS Lambdaを使ったシステムを構築するときはLambda関数とセットでAPIGatewayを作成して外部連携のためにAPI化することがほとんどなのですが、作成するAPIGatewayはネーミング以外は共通の設定で十分なことが多いため、我ながらとても便利に使っているスクリプトとなります。
ただ、作成されるAPIGatewayはID、キー値、実行するLambda関数以外は共通の設定のため、APIGatewayが実行するLambda関数をリクエスト時に動的に指定できるようになると、作成するAPIGatewayの数をうんと減らせるのですが、いまの仕様だと出来なさそうです。
余裕があればPowerShell版とシェルスクリプト版もそのうち投稿したいと思います。