API Gatewayを一発で作成するLambda関数を作成してみた(Python版)
API Gatewayを一発で作成するLambda関数を作成してみた(Python版):
AWS Lambdaを使ったシステムを構築するときはLambda関数とセットでAPIGatewayを作成して外部連携のためにAPI化することがほとんどなのですが、作成するAPIGatewayはネーミング以外は共通の設定で十分なことが多いです。そこで、指定のLambda関数に対するリクエスト送信/レスポンス取得を行うAPIGatewayを一発で作成できるスクリプトを作りました。
このスクリプトで作成されるAPIGatewayは、APIキー認証により、クライアントから受けたPOSTリクエストのペイロードをそのままLambda関数へ渡し、Lambda関数からのreturnをペイロードとしたレスポンスをそのままクライアントへ返す、最低限の機能のAPIエンドポイント/HTTPプロキシとなりますが、大抵の場合はそのまま利用できると思います。
ランタイムにPython(バージョン不問)を指定したLambda関数を作成します。
Lambda関数の関数コードでは以下のコードを記載したlambda_function.pyを作成します。
Lambda関数の実行ロールでは以下のポリシーと同等以上の権限が付与されたロールを使用します。
先程作成したスクリプト(Lambda関数)をAWSCLIより実行します。
作成されたAPIGateswayのエンドポイントURIとAPIキーが表示されるので控えます。
先程控えたエンドポイントURIとAPIキーを指定してPOSTリクエストを実行し、想定通りのレスポンスが返ってくればAPIGatewayは利用可能になっています。
クローズドな利用のLambda関数を量産しているため、我ながらとても便利に使っているスクリプトとなります。
作成されるAPIGatewayはID、キー値、実行するLambda関数以外は共通の設定のため、APIGatewayが実行するLambda関数をリクエスト時に動的に指定できるようになると、作成するAPIGatewayの数をうんと減らせるのですが、いまの仕様だと出来なさそうです。
はじめに
AWS Lambdaを使ったシステムを構築するときはLambda関数とセットでAPIGatewayを作成して外部連携のためにAPI化することがほとんどなのですが、作成するAPIGatewayはネーミング以外は共通の設定で十分なことが多いです。そこで、指定のLambda関数に対するリクエスト送信/レスポンス取得を行うAPIGatewayを一発で作成できるスクリプトを作りました。このスクリプトで作成されるAPIGatewayは、APIキー認証により、クライアントから受けたPOSTリクエストのペイロードをそのままLambda関数へ渡し、Lambda関数からのreturnをペイロードとしたレスポンスをそのままクライアントへ返す、最低限の機能のAPIエンドポイント/HTTPプロキシとなりますが、大抵の場合はそのまま利用できると思います。
スクリプト(Lambda関数)の作成
ランタイムにPython(バージョン不問)を指定したLambda関数を作成します。Lambda関数の関数コードでは以下のコードを記載したlambda_function.pyを作成します。
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" } ] }
スクリプト(Lambda関数)を実行してAPIGatewayを作成
先程作成したスクリプト(Lambda関数)をAWSCLIより実行します。作成されたAPIGateswayのエンドポイントURIとAPIキーが表示されるので控えます。
$ function_name=test-func #作成するAPIGatewayを使用するLambda関数名を指定 $ aws lambda invoke --function-name <スクリプトのLambda関数名> --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****************************" ]
作成したAPIGatewayを利用
先程控えたエンドポイント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 のレスポンス ###
おわりに
クローズドな利用のLambda関数を量産しているため、我ながらとても便利に使っているスクリプトとなります。作成されるAPIGatewayはID、キー値、実行するLambda関数以外は共通の設定のため、APIGatewayが実行するLambda関数をリクエスト時に動的に指定できるようになると、作成するAPIGatewayの数をうんと減らせるのですが、いまの仕様だと出来なさそうです。
コメント
コメントを投稿