Python 2.7/3.6/3.7 で実行される AWS Lambda 関数の AWS CloudFormation の cfn-response モジュールを更新するにはどうすればよいですか?
最終更新日: 2021 年 2 月 16 日
Python 2.7/3.6/3.7 で実行される AWS Lambda 関数の AWS CloudFormation の cfn-response モジュールを更新したいと考えています。
解決方法
注意: 次の手順は、Python 2.7/3.6/3.7 で実行されている Lambda 関数にのみ適用されます。以下のコマンドは、Linux および macOS 環境に適用できます。構文は Windows PowerShell によって異なる場合があります。
注意: AWS コマンドラインインターフェイス (AWS CLI) コマンドの実行中にエラーが発生した場合は、AWS CLI の最新バージョンを使用していることを確認してください。
1. カスタムリソースを含むスタックを検索するには、次のコマンドを実行します。
aws cloudformation list-stacks --region us-east-1 | grep -oE 'arn:[^"]+' | while read arn; do aws cloudformation list-stack-resources --stack-name $arn --region us-east-1 | grep -E '(Custom::)|(::CustomResource)' | awk '{print $2}' | while read resource; do if [[ -n $resource ]]; then echo $arn; echo $resource; fi; done; done
次の出力例のような出力が表示されます。
arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667
"ResourceType": "AWS::CloudFormation::CustomResource",
2. カスタムリソースに関連付けられている Lambda 関数を検索するには、次のコマンドを実行して、スタックのテンプレートからカスタムリソースの ServiceToken プロパティを確認します。
aws cloudformation get-template --stack-name TestStack | jq -r .TemplateBody
注意: ステップ 2 のコマンドは、(jq ウェブサイトの) jq オプションを使用してスタックのテンプレートをプレビューし、応答をフォーマットします。
次の出力例のような出力が表示されます。
Resources:
MyCustomResource:
Type: AWS::CloudFormation::CustomResource
Properties:
ServiceToken: !GetAtt MyFunction.Arn
Name: "John"
MyFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt MyRole.Arn
Runtime: python3.7
Code:
ZipFile: |
import cfnresponse
def handler(event, context):
responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
MyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Outputs:
Result:
Value: !GetAtt MyCustomResource.Message
注: ステップ 2 の出力から取得したテンプレートは、Lambda-backed カスタムリソースの最小テンプレートの例です。プロパティ ServiceToken: !GetAtt MyFunction.Arn は、MyCustomResource セクションにあります。ServiceToken プロパティの GetAtt MyFunction.Arn によって解決される値は、Amazon Simple Notification Service (Amazon SNS) トピックの Amazon リソースネーム (ARN) または Lambda 関数のいずれかです。
3. ステップ 2 のテンプレートで、Lambda 関数が定義されている場所を特定します。
Lambda 関数がカスタムリソースと同じスタックにある場合は、ステップ 4 に進みます。例えば、ステップ 2 の Fn::GetAtt 関数は、Lambda 関数がカスタムリソースと同じテンプレートで定義されていることを示しています。
ServiceToken プロパティがハードコードされた ARN をポイントしている場合、Lambda 関数は別のスタックにある可能性があります。ServiceToken プロパティが Fn::Import によって解決される場合は、AWS CloudFormation で list-exports API を使用して値を調べます。例:
aws cloudformation list-exports --region us-east-1
{
"Exports": [
{
"ExportingStackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/SomeOtherStack/481dc040-b283-11e9-b1bd-12d607a4fd1c",
"Value": "arn:aws:lambda:us-east-1:123456789012:function:SomeOtherStack-MyFunction-5ZE2CQO8RAA9",
"Name": "MyExport"
}
]
}
その後、list-tags を使用して AWS CloudFormation スタック ARN を特定することにより、別のスタックにある関数タグを確認します。例:
aws lambda list-tags --resource arn:aws:lambda:us-east-1:123456789012:function:TestStack-MyFunction-5ZE2CQO8RAA9 | grep stack-id
次のような出力が表示されます。
"aws:cloudformation:stack-id": "arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667"
注: AWS Lambda コンソールでも関数タグを見つけることができます。
4. AWS CloudFormation が Lambda 関数で最新の cfn-response モジュールをロードできるようにするには、Lambda 関数のインラインソースコードを更新します。例:
Code:
ZipFile: |
import cfnresponse
def handler(event, context):
responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
注意: インラインソースコードを備えた Lambda 関数を持つテンプレートの例については、ステップ 2 を参照してください。
これで、次の cfn-response モジュールコードサンプルが AWS CloudFormation によって Lambda 関数にロードされるようになりました。例:
from botocore.vendored import requests
import json
SUCCESS = "SUCCESS"
FAILED = "FAILED"
def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
responseUrl = event['ResponseURL']
print(responseUrl)
responseBody = {}
responseBody['Status'] = responseStatus
responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
responseBody['StackId'] = event['StackId']
responseBody['RequestId'] = event['RequestId']
responseBody['LogicalResourceId'] = event['LogicalResourceId']
responseBody['NoEcho'] = noEcho
responseBody['Data'] = responseData
注意: 詳細については、cfn-response モジュールの「モジュールソースコード」セクションのサンプルコードを参照してください。
cfn-response モジュールのサンプルコードでは、Lambda 関数のデプロイパッケージで botocore.requests を使用しています。
cfn-response モジュールを urllib3 を使用する最新バージョンに更新するには、AWS CloudFormation テンプレート内の関数のインラインコードを更新します。これを行うには、インラインの Lambda 関数のコードにコメントを追加します。例:
ZipFile: |
import cfnresponse
def handler(event, context):
+ # This comment was added to force an update on this function's code
responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
MyRole:
5. Lambda 関数を含むテンプレートへの変更を保存します。
6. スタックを更新します。
cfn-response モジュールは、スタックの更新が終了した後に変更されます。
注: 関数のコードが Amazon Simple Storage Service (Amazon S3) バケットまたは Amazon Elastic Container Registry (Amazon ECR) イメージにある場合は、モジュールを自分で更新して urllib3 を使用するバージョンを含める必要があります。cfn-response モジュールの最新バージョンのソースコードを取得するには、cfn-response モジュールをご参照ください。
注: 新しい Python または JavaScript ランタイムで破壊的変更が加えられる場合は、cfn-response モジュールを更新する必要があります。ZipFile を再度更新する代わりに、関数の Runtime プロパティが更新される都度、cfn-response モジュールの最新バージョンを自動的にアタッチできます。