labunix's blog

labunixのラボUnix

aws lambdaでHello Worldを実行して、awscliで確認してみる。

■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