■aws lambdaでHello Worldを実行して、awscliで確認してみる。 「Hello, World!」をサーバーレスで実行する https://aws.amazon.com/jp/getting-started/hands-on/run-serverless-code/ ■上記のハンズオンの通りに実施して、関数の削除前にawscliで確認してみる。 まずは許可するポリシーの確認。 https://raw.githubusercontent.com/labunix/aws/main/aws-policy-search $ ./aws-policy-search lambda.*full | sort -uV AWSLambdaFullAccess AWSLambda_FullAccess ■さっそく脇道にそれるけど、差分から考えると「_」のつくおかしなポリシー名の修正だと思われる。 $ aws iam list-policies --scope AWS | \ jq -r '.Policies[] | select (.PolicyName | test ("^AWSLambda.*Full"))' { "PolicyName": "AWSLambdaFullAccess", "PolicyId": "ANPAI6E2CYYMI4XI7AA5K", "Arn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess", "Path": "/", "DefaultVersionId": "v8", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2015-02-06T18:40:45Z", "UpdateDate": "2017-11-27T23:22:38Z" } { "PolicyName": "AWSLambda_FullAccess", "PolicyId": "ANPAZKAPJZG4OXQPYWZ5D", "Arn": "arn:aws:iam::aws:policy/AWSLambda_FullAccess", "Path": "/", "DefaultVersionId": "v1", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2020-11-17T21:14:08Z", "UpdateDate": "2020-11-17T21:14:08Z" } $ sdiff -l AWSLambda_FullAccess AWSLambdaFullAccess { ( "PolicyName": "AWSLambda_FullAccess", | "PolicyName": "AWSLambdaFullAccess", "PolicyId": "ANPAZKAPJZG4OXQPYWZ5D", | "PolicyId": "ANPAI6E2CYYMI4XI7AA5K", "Arn": "arn:aws:iam::aws:policy/AWSLambda_FullAccess", | "Arn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess", "Path": "/", ( "DefaultVersionId": "v1", | "DefaultVersionId": "v8", "AttachmentCount": 0, ( "PermissionsBoundaryUsageCount": 0, ( "IsAttachable": true, ( "CreateDate": "2020-11-17T21:14:08Z", | "CreateDate": "2015-02-06T18:40:45Z", "UpdateDate": "2020-11-17T21:14:08Z" | "UpdateDate": "2017-11-27T23:22:38Z" } ( ( ■脇道ついでにjq検索ではなくlsecで検索してみる。 lsecにはIGNORECASEが無いので大文字小文字が区別される。 https://raw.githubusercontent.com/labunix/lsec/master/lsec $ aws iam list-policies --scope AWS | jq -r '.Policies[]' | ./myscripts/lsec -sep '^{' Lambda.*Full { "PolicyName": "AWSLambdaFullAccess", "PolicyId": "ANPAI6E2CYYMI4XI7AA5K", "Arn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess", "Path": "/", "DefaultVersionId": "v8", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2015-02-06T18:40:45Z", "UpdateDate": "2017-11-27T23:22:38Z" } { "PolicyName": "AWSLambda_FullAccess", "PolicyId": "ANPAZKAPJZG4OXQPYWZ5D", "Arn": "arn:aws:iam::aws:policy/AWSLambda_FullAccess", "Path": "/", "DefaultVersionId": "v1", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2020-11-17T21:14:08Z", "UpdateDate": "2020-11-17T21:14:08Z" } ■awscliで使えるget-*およびlist-*のオプションは以下。 $ aws lambda help | awk '/list|get/{print $NF}' get-account-settings get-alias get-event-source-mapping get-function get-function-configuration get-layer-version get-layer-version-policy get-policy list-aliases list-event-source-mappings list-functions list-layer-versions list-layers list-tags list-versions-by-function ■list-*オプションがそのまま使えるかをリターンでチェック。 $ aws lambda help | awk '/list/{print "echo -n "$NF";aws lambda "$NF" >/dev/null;echo \042,$?\042"}' | sh 2>/dev/null list-aliases,2 list-event-source-mappings,0 list-functions,0 list-layer-versions,2 list-layers,0 list-tags,2 list-versions-by-function,2 ■今回、必要な情報を返すのはlist-functionsだけなので、list-functionsにフォーカスする。 lambdaはリージョンサービスなので、デフォルトと異なるリージョンの関数はリージョン指定が必要。 $ awk '/region/{print $NF}' .aws/config us-east-2 $ aws lambda list-functions | jq -r '.Functions[].FunctionName' $ aws lambda list-functions --region us-east-1 | jq -r '.Functions[].FunctionName' cloudshell-nortification hello-world-python trans-func ■全リージョンから検索するとしたら、公式のAZ一覧のドキュメントを参考に。 $ w3m -cols 240 -dump "https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html" | \ awk 'BEGIN{a=0} \ {if($1 ~ /コード/&& $2 ~ /名前/){a=1}else{if($1 ~ /詳細/){a=0}}} \ {if(a==1&& !/^$|lax-/){gsub("不要|必要|必須|利用不可|オプトインステータス|ローカルゾーン","",$0); \ printf "%-18s%s%s%s\n",$1,$2,$3,$4}}' > aws-region.txt $ cat aws-region.txt コード 名前 us-east-2 米国東部(オハイオ) us-east-1 米国東部(バージニア北部) us-west-1 米国西部(北カリフォルニア) us-west-2 米国西部(オレゴン) af-south-1 アフリカ(ケープタウン) ap-east-1 アジアパシフィック(香港) ap-south-1 アジアパシフィック(ムンバイ) ap-northeast-3 アジアパシフィック(大阪:ローカル) ap-northeast-2 アジアパシフィック(ソウル) ap-southeast-1 アジアパシフィック(シンガポール) ap-southeast-2 アジアパシフィック(シドニー) ap-northeast-1 アジアパシフィック(東京) ca-central-1 カナダ(中部) eu-central-1 欧州(フランクフルト) eu-west-1 欧州(アイルランド) eu-west-2 欧州(ロンドン) eu-south-1 ヨーロッパ(ミラノ) eu-west-3 欧州(パリ) eu-north-1 欧州(ストックホルム) me-south-1 中東(バーレーン) sa-east-1 南米(サンパウロ) ■全部見ればいいじゃんということで。 以下のエラーが出たら無効なリージョンとして扱う。 An error occurred (UnrecognizedClientException) when calling the ListFunctions operation: The security token included in the request is invalid. $ awk '!/コード/{print "echo \042["$1"\042];aws lambda list-functions --region "$1" | jq -r \047.Functions[].FunctionName\047"}' \ aws-region.txt | sh 2>&1 | awk '{if(/^An error occurred/){print "無効なリージョン"}else{if($0 !~ /^$/){print $0}}}' [us-east-2] [us-east-1] cloudshell-nortification hello-world-python trans-func [us-west-1] [us-west-2] [af-south-1] 無効なリージョン [ap-east-1] 無効なリージョン [ap-south-1] [ap-northeast-3] 無効なリージョン [ap-northeast-2] [ap-southeast-1] [ap-southeast-2] [ap-northeast-1] [ca-central-1] [eu-central-1] [eu-west-1] [eu-west-2] [eu-south-1] 無効なリージョン [eu-west-3] [eu-north-1] [me-south-1] 無効なリージョン [sa-east-1] ■jqの検索でもlsecの検索でも結果は同じ。 $ aws lambda list-functions --region us-east-1 | jq -r '.Functions[] | select (.FunctionName | test ("^hello"))' | \ awk '!/Location/{gsub("[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]","XXXXXXXXXXXX");print $0}' $ aws lambda list-functions --region us-east-1 | jq -r '.Functions[]' | ./myscripts/lsec -sep '^{' hello | \ awk '!/Location/{gsub("[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]","XXXXXXXXXXXX");print $0}' { "FunctionName": "hello-world-python", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:hello-world-python", "Runtime": "python3.7", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/service-role/lambda_basic_execution", "Handler": "lambda_function.lambda_handler", "CodeSize": 343, "Description": "A starter AWS Lambda function.", "Timeout": 3, "MemorySize": 128, "LastModified": "2021-01-05T16:07:20.098+0000", "CodeSha256": "lz0z220we8A5YsX4ECRyNT5tF7dbcB2a0u5m3skRHlc=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "ccf32a9e-c2cb-44ee-92ff-3c1c89aa335c" } ■get-*オプションが関数名だけで、そのまま使えるかリターンでチェック。 $ aws lambda help | \ awk '/get/{print "echo -n "$NF";aws lambda "$NF" --function-name hello-world-python --region us-east-1 >/dev/null;echo \042,$?\042"}' | \ sh 2>/dev/null get-account-settings,255 get-alias,2 get-event-source-mapping,2 get-function,0 get-function-configuration,0 get-layer-version,2 get-layer-version-policy,2 get-policy,255 ■get-function-configurationは、get-functionの中のConfigurationセクション。 $ aws lambda get-function-configuration --function-name hello-world-python --region us-east-1 | \ awk '!/Location/{gsub("[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]","XXXXXXXXXXXX");print $0}' { "FunctionName": "hello-world-python", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:hello-world-python", "Runtime": "python3.7", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/service-role/lambda_basic_execution", "Handler": "lambda_function.lambda_handler", "CodeSize": 343, "Description": "A starter AWS Lambda function.", "Timeout": 3, "MemorySize": 128, "LastModified": "2021-01-05T16:07:20.098+0000", "CodeSha256": "lz0z220we8A5YsX4ECRyNT5tF7dbcB2a0u5m3skRHlc=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "ccf32a9e-c2cb-44ee-92ff-3c1c89aa335c" } ■get-functionの中で、LocationやFunctionArnにはリージョンコードが含まれている。 $ aws lambda get-function --function-name hello-world-python --region us-east-1 | \ awk '{if(!/Location/){gsub("[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]","XXXXXXXXXXXX");print $0}else{print $1"XXX"}}' { "Configuration": { "FunctionName": "hello-world-python", "FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:hello-world-python", "Runtime": "python3.7", "Role": "arn:aws:iam::XXXXXXXXXXXX:role/service-role/lambda_basic_execution", "Handler": "lambda_function.lambda_handler", "CodeSize": 343, "Description": "A starter AWS Lambda function.", "Timeout": 3, "MemorySize": 128, "LastModified": "2021-01-05T16:07:20.098+0000", "CodeSha256": "lz0z220we8A5YsX4ECRyNT5tF7dbcB2a0u5m3skRHlc=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "ccf32a9e-c2cb-44ee-92ff-3c1c89aa335c" }, "Code": { "RepositoryType": "S3", "Location":XXX }, "Tags": { "lambda-console:blueprint": "hello-world-python" } } ■get-functionオプションの結果からjqでConfigurationを抜き出すと残念なことになるからだろうか。 空白の違いを無視すると、list-functionsと同じ結果なので要らない気もする。 $ aws lambda get-function --function-name hello-world-python --region us-east-1 | \ jq '.Configuration[]' | awk '{gsub("[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]","XXXXXXXXXXXX");print $0}' "hello-world-python" "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:hello-world-python" "python3.7" "arn:aws:iam::XXXXXXXXXXXX:role/service-role/lambda_basic_execution" "lambda_function.lambda_handler" 343 "A starter AWS Lambda function." 3 128 "2021-01-05T16:07:20.098+0000" "lz0z220we8A5YsX4ECRyNT5tF7dbcB2a0u5m3skRHlc=" "$LATEST" { "Mode": "PassThrough" } "ccf32a9e-c2cb-44ee-92ff-3c1c89aa335c" $ aws lambda list-functions --region us-east-1 | jq -r '.Functions[]' | ./myscripts/lsec -sep '^{' hello > list-functions $ aws lambda get-function-configuration --function-name hello-world-python --region us-east-1 > get-function-configuration $ sdiff -b -l list-functions get-function-configuration { ( "FunctionName": "hello-world-python", ( "FunctionArn": "arn:aws:lambda:us-east-1:770940425907:funct ( "Runtime": "python3.7", ( "Role": "arn:aws:iam::770940425907:role/service-role/lambda ( "Handler": "lambda_function.lambda_handler", ( "CodeSize": 343, ( "Description": "A starter AWS Lambda function.", ( "Timeout": 3, ( "MemorySize": 128, ( "LastModified": "2021-01-05T16:07:20.098+0000", ( "CodeSha256": "lz0z220we8A5YsX4ECRyNT5tF7dbcB2a0u5m3skRHlc= ( "Version": "$LATEST", ( "TracingConfig": { ( "Mode": "PassThrough" ( }, ( "RevisionId": "ccf32a9e-c2cb-44ee-92ff-3c1c89aa335c" ( } ( ■cloudtrailでAPIを叩いたログの構成を見てみる。 2回目のjqは1行にまとめられたjsonログを元のJSON形式に戻している。 $ aws cloudtrail lookup-events > aws_cloudtrail_lookup-events_90days.txt $ cat aws_cloudtrail_lookup-events_90days.txt | jq -r '.Events[] | select (.EventSource | test ("ambda")) | .CloudTrailEvent' | \ jq -r 'select(.eventTime | test("2021-01-05T17:59:16Z"))' | awk -F: '{print $1}' { "eventVersion" "userIdentity" "type" "principalId" "arn" "accountId" "accessKeyId" "userName" }, "eventTime" "eventSource" "eventName" "awsRegion" "sourceIPAddress" "userAgent" "errorCode" "errorMessage" "requestParameters" "functionName" }, "responseElements" "requestID" "eventID" "readOnly" "eventType" "managementEvent" "eventCategory" "recipientAccountId" } ■eventNameなど、わざわざjqしなくて良いこともある。 $ cat aws_cloudtrail_lookup-events_90days.txt | jq -r '.Events[] | select (.EventSource | test ("ambda")) | .CloudTrailEvent' | \ jq -r '' | grep eventName | sort -uV "eventName": "GetAccountSettings20160819", "eventName": "GetFunction20150331v2", "eventName": "GetFunctionConfiguration20150331v2", "eventName": "GetPolicy20150331v2", "eventName": "ListEventSourceMappings20150331", "eventName": "ListFunctions20150331", ■ListFunctions20150331を叩いたのは誰かを抽出するとかはlsecかjqした方が良い。 $ cat aws_cloudtrail_lookup-events_90days.txt | jq -r '.Events[] | select (.EventSource | test ("ambda")) | .CloudTrailEvent' | \ jq -r 'select(.eventName | test("ListFunctions")) | .userIdentity.arn' | sort -uV | \ sed -e 's/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/XXXXXXXXXXXX/g' arn:aws:iam::XXXXXXXXXXXX:user/labunix-admin arn:aws:iam::XXXXXXXXXXXX:user/labunix-cloudshell