Lambda関数 describe-api
概要
AWS Lambda関数「describe-api」を作成してみたので、紹介します。
一言で言うと、URLパラメータの組み合わせでAWSのAPIを呼び出して、結果をWebブラウザに返すLambda関数です。
AWSのマネージメントコンソール(マネコン)に入らなくても、WebブラウザーでAWSの情報取得をできちゃいたいこと、ありませんか?
それをサーバーレスで実現するための関数です。
変更系APIと参照系APIの2種類のAPIに大別したときの 参照系APIが対象です。
フロー図
- Webクライアント(Webブラウザ)でLambda関数のエンドポイントURLへアクセス
- URLのパラメータによりLambda関数がAWS API(boto3)を実行
例:api=ec2:describe_subnets
→ EC2 の DescribeSubnets APIを呼び出す
S3利用時は、キャッシュが新しい場合はキャッシュを返す(図の点線) - Webクライアントは結果のJsonを受け取る
利用ケース
エンドユーザーがAWS上の情報(EC2の一覧表示など)を取得したいが、AWSのマネコンへログイン権限がない(またはログイン権限を付与したくない)、という場合があります。
API実行用のWebサーバーを立てて中継することでも実現できますが、その場合にサーバー管理の手間が発生します。
S3バケットとLambda関数だけで実現させてサーバー管理の手間も無くしたい、というケースで利用できます。
また、マネコンでは提供されていない切り口でWebブラウザに表示させたいというケースでも利用できます。
実装デモ
以下のWebページにアクセスして、デモを触ってみてください。
デモの中の各リンクをクリックすると、describe-api 関数を呼び出して結果を表示するようにしています。
GitHub
Lambda関数コードはGitHubに置いています。
GitHub - aws-lambda-describe-api
-
S3バケット版
- Lambda関数: describe_api_with_s3.py
- CloudFormationテンプレート: cfn-lambda-describe-api-with-s3.yaml
-
S3バケット無し版
- Lambda関数: describe_api.py
- CloudFormationテンプレート: cfn-lambda-describe-api.yaml
S3バケットをキャッシュとして利用しない場合は、コードを簡易にしています。
特徴
Lambda関数名は describe-api
としていますが、対象のAPIは、describeから始まるAPI 以外に getから始まるAPI, listから始まるAPI(参照系API)が対象です。
- HTTP GETメソッドを利用します:
POSTメソッドじゃないの?と思うかもしれません。一般ユーザーでもURLの組み立てが扱いやすいように、HTTPヘッダーではなくURLパラメーターの指定だけでAPIを実行できます。 - ページネーションによる分割を意識しなくてよいです:
AWS APIの多くは、ページネーションの機能があります。それにより1回のAPIではすべての結果を得られず、複数回実行して分割した結果を結合する必要があります。この関数では、1回のURLアクセスだけで分割された結果を関数内部で結合してから取得します。 - キャッシュ機能があります(S3利用時):
S3バケットにAPIの実行結果をキャッシュして、次回以降の高速な応答を実現します。キャッシュの有効期間を個別に指定できます。 - 複雑なJSONデータの整形・加工します:
- select=xxx:APIの結果から必要項目だけに絞ります
-
simpletag:複雑なタグ構造をシンプル化します
詳しくは、URLパラメータの章に記載します。
- アクセス元IPアドレスによる許可の制御をします:
環境変数source_cidr_list
により、アクセス元のIPをCIDR形式で許可します。 - API固有の変換機能があります:
- ec2 の describe_instances 専用:flatten を付与すると配列をフラット化します
- cloudwatch の get_metric_widget_image API専用:扱いやすいBase64で返します
- メタデータを返しません:
APIの実行結果で必要なのは、そのAPIの結果データの部分です。メタデータ等は不要ですので、除去してから返します
URLパラメータ
URLは、Lambda関数のエンドポイントの形式のドメイン(xxxxxxx.lambda-url.リージョン名.on.aws
)式とします。
https://xxxxxxx.lambda-url.your-region-name.on.aws/?
api=service:describe_xxxxxxxx
[&arg=【APIの引数用文字列】]
[®ion=【リージョン名】]
[&select=【キー名1:キー名2:・・・】]
[&cache=【キャッシュ有効秒数】]
[&flatten]
[&simpletag]
URLパラメータの例
具体的な例はこちらにあります。
api=service:describe_xxxxxx
- 必須パラメータ
-
service
は、IAMポリシー ReadOnlyAccess で許可されているサービス が指定可能
例:ec2, rds, elb, s3, iam, cloudwatch (その他にも200以上あります) -
describe_xxxxxx
は、各サービスの参照系のAPI
snake_case (単語を「_」でつなぐ) で指定 -
describe_xxxx
の箇所はlist_xxxx
,get_xxxx
も対象
arg=【APIの引数用文字列】
- APIによっては必須
- 値はJsonオブジェクトの { "Key" : "Value" } の形式
- URLパラメータでは最低でも
{
→%7B
、}
→%7D
の2つのURLエンコードが必須 -
"Key"
は CamelCase (先頭が大文字、単語の間に文字無し)で指定します - 例:
api=elb:describe_instance_health
の場合は、arg=%7B"LoadBalancerName":"MY-LOAD-BALANCER"%7D
のようにします -
Filters
を指定する場合はやや複雑になります
例:api=rds:describe_db_instances
でSQLServer Standard Editionでフィルターするには、arg=%7B"Filters":[%7B"Name":"engine","Values":["sqlserver-se"]%7D]%7D
region=【リージョン名】
- 各リージョンを指定可能
例: us-east1, ap-northeast-1, eu-central-1 等 - デフォルト値は Lambdaの環境変数 default_region の値
select=【キー名1:キー名2:・・・】
- APIの結果から、必要な項目だけをフィルターします
- API自体はフィルター無しで実行され、API実行結果からLambda関数内でフィルターされます
- 複数指定する場合はコロンで区切ります
例:api=ec2:describe_subnets
の場合、select=AvailabilityZone:CidrBlock
とすると、 各配列要素は{"AvailabilityZone":"xxx-xxx", "CidrBlock":"x.x.x.x/nn"}
の項目だけ含んだJsonを返します。 - キー名にドットを指定すると、深いキー名を指定可能です。aaa.bbb.ccc のように深い階層も可能です
- キー名にドットを連続して指定すると、配列の各要素のキーでフィルターできます。
例:[{"IpAddresses":[{"Ip":"1.1.1.1"}, {"Ip":"2.2.2.2"}]]
のデータに対してselect=IpAddresses.Ip
を指定すると値は取れません。select=IpAddresses..Ip
とすると[{ "IpAddresses..IP":["1.1.1.1", "2.2.2.2"]}]
を返します
cache=秒
- S3バケットを利用する場合、APIはクライアントにデータを返しつつ、S3にキャッシュする仕様です
- 指定した秒数と比較してキャッシュが期限切れ前なら、S3に保存していたキャッシュを返します
- 値が10文字以上あると、先頭9文字より後は切り捨てられます。デフォルト値は 60 です
- S3バケットを利用するかどうかに関わらず、
cache=秒
の数値を HTTP Cache-Control ヘッダーのmax-age=秒
で返します。(max-age の最大値は 604800) - 必ず最新データが欲しい場合は、
cache=0
とします - S3バケットを利用する場合で、今回はキャッシュさせたくないという場合は、
cache=never
とします
flatten
-
ec2:describe_instances
専用のオプションです
ec2:describe_subnets
など他のAPIにflatten
を指定しても無視されます
flatten
が無い場合:
{
"Reservations": [
{
"Groups": [],
"Instances": [
{"EC2_A の属性": "EC2_A の属性の値", },
{"EC2_B の属性": "EC2_B の属性の値", },
]
},
{
"Groups": [],
"Instances": [
{"EC2_C の属性": "EC2_C の属性の値", },
]
}
]
}
flatten
がある場合(フラットにします):
[
{"EC2_A の属性": "EC2_A の属性の値", },
{"EC2_B の属性": "EC2_B の属性の値", },
{"EC2_C の属性": "EC2_C の属性の値", }
]
simpletag
- タグ情報を含む一部のAPI 専用のオプションです。APIの結果に
Tags
またはTagList
がある場合に適用されます
simpletag
がない場合:
[
{
"Key": "Name",
"Value": "Server01"
},
{
"Key": "Environment",
"Value": "Production"
}
]
simpletag
がある場合(シンプルにします):
{
"Name": "Server01",
"Environment": "Production"
}
CloudWatchメトリクスの画像取得
このLambda関数では、CloudWatchメトリクスのグラフをPNG画像で取得するAPI get_metric_widget_image
に対応しています。
元のAPIではバイナリデータを受け取りますが、この関数ではBase64形式にして返されます。
呼び出す場合のURLは、以下のように組み立てます。
- CloudWatchメトリクスで目的のグラフを描画させます
- 発信元タブ → イメージAPI → コピー の順にクリックします
- 上述したように、
{
→%7B
、}
→%7D
のように置換します - さらに、
"
→%5C"
のように、バックスラッシュをエンコードした文字を"
の前に付与します
上記画像をエンコードした例:
%7B%5C"view%5C":%5C"timeSeries%5C", ~中略~ %5C"end%5C":%5C"P0D%5C"%7D
- 出来上がった文字列を
https://xxxx.lambda-url.your-region-name.on.aws/api=cloudwatch:get_metric_widget_image&arg=%7B%22MetricWidget%22:%22
と%22%7D
の間に挟んで、結合した文字列がURLになります
出来上がったURLにアクセスすると、{"MetricWidgetImage": "iVBOR~~~~~~~~" } のJsonが返ってきます。
iVBOR~~~~~~~~
はPNG画像のBase64形式です。
これを <img src="data:image/png;base64,iVBOR~~~~~~~~" />
のようなimgタグをJavascriptで動的に作成して、画像をブラウザに表示させることができます。
Lambda関数の環境変数
各環境変数が定義できます。
-
s3_bucket:
S3バケット名を指定すると、一度取得したJsonをS3に保管してキャッシュとして使用するようになります -
default_region:
region= のURLパラメータを付けない場合の、デフォルトのリージョンです。この環境変数も指定せずURLパラメータも付けない場合は、us-east-1
となります -
source_cidr_list:
アクセス元のIPアドレスをCIDR形式で許可します。デフォルトは無制限です。0.0.0.0/0, ::/0
とすると無制限になります。空白区切りで複数指定できます -
default_cache:
S3のキャッシュ利用時、URLパラメータに cache=数字 を付けない場合、この環境変数の数字がキャッシュ有効秒となります -
remove_key_list:
APIを実行した後に取得された結果には、メタデータなどを含んでいます。この関数では、この環境変数で指定したものを除去します。値はResponseMetadata Marker NextToken nextToken IsTruncated MaxResults
としておけばよいでしょう
エラーに関すること
- URLパラメータに
{
と}
をエンコードせずにそのままの文字で呼び出すと、Lambda関数まで到達できません。 - api の不足、存在しないAPI名の指定、変更系APIの指定、arg のミス等、必須パラメータの不足があると { "Error_Message": "xxxxxxx" } の Jsonが返ります。
例:{ "Error_Message": "Content size limit exceeded." }
- Lambda関数エンドポイント経由でのアクセスの制約で 処理に59秒以上かかると失敗します
- Lambda関数の制約の関係で、この関数では結果が 5.75MiB を超えていると失敗します。結果のデータが巨大になりそうな場合は、
select
を活用することでエラーを防げます
あとがき
関数自体は2019年に初版を作成し、徐々に機能拡張しながら、AWSインフラ運用管理に役立ててきました。
2022年には今の機能を実装済みでしたが、生成AIでコードをブラッシュアップしましたので、記事として公開に踏み切りました。
この関数をベースにクライアントサイド(Javascript)で、幅広いツールの作成に活用できます。
ぜひ試してみてください。
正直、この関数だけだと便利なサイトを作成したとは言えませんね。このLambda関数を活用した便利なWebサービス例は、別記事で紹介したいと思います。